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.

2492 lines
69 KiB

  1. #include "private.h"
  2. #include "subsmgrp.h"
  3. #include "downld.h"
  4. #include "chanmgr.h"
  5. #include "chanmgrp.h"
  6. #include "helper.h"
  7. #include "shguidp.h" // IID_IChannelMgrPriv
  8. #undef TF_THISMODULE
  9. #define TF_THISMODULE TF_CDFAGENT
  10. const int MINUTES_PER_DAY = 24 * 60;
  11. //==============================================================================
  12. // Convert an XML OM to a TaskTrigger by looking for and converting <schedule>
  13. //==============================================================================
  14. // returns S_OK if succeeded. (S_FALSE if succeeded but TASK_TRIGGER was truncated).
  15. // returns E_FAIL if no task trigger retrieved (returned *ptt is invalid TASK_TRIGGER)
  16. // You Must fill in ptt->cbTriggerSize!!!
  17. // User can pass in Schedule element itself, or any parent element, in pRootEle
  18. HRESULT XMLScheduleElementToTaskTrigger(IXMLElement *pRootEle, TASK_TRIGGER *ptt)
  19. {
  20. HRESULT hr = E_FAIL;
  21. if (!pRootEle || !ptt)
  22. return E_INVALIDARG;
  23. ASSERT(ptt->cbTriggerSize == sizeof(TASK_TRIGGER));
  24. CExtractSchedule *pSched = new CExtractSchedule(pRootEle, NULL);
  25. if (pSched)
  26. {
  27. if (SUCCEEDED(pSched->Run()))
  28. {
  29. hr = pSched->GetTaskTrigger(ptt);
  30. }
  31. delete pSched;
  32. }
  33. return hr;
  34. }
  35. // CExtractSchedule doesn't get used during channel update
  36. // It's just used to traverse the OM and find the first Schedule tag, to
  37. // parse out the schedule info
  38. CExtractSchedule::CExtractSchedule(IXMLElement *pEle, CExtractSchedule *pExtractRoot) :
  39. CProcessElement(NULL, NULL, pEle)
  40. {
  41. m_pExtractRoot = pExtractRoot;
  42. if (!pExtractRoot)
  43. m_pExtractRoot = this;
  44. }
  45. HRESULT CExtractSchedule::Run()
  46. {
  47. // Allow user to pass in Schedule element itself, or Root element
  48. BSTR bstrItem=NULL;
  49. HRESULT hr;
  50. m_pElement->get_tagName(&bstrItem);
  51. if (bstrItem && *bstrItem && !StrCmpIW(bstrItem, L"Schedule"))
  52. {
  53. hr = ProcessItemInEnum(bstrItem, m_pElement);
  54. }
  55. else
  56. {
  57. hr = CProcessElement::Run();
  58. }
  59. SysFreeString(bstrItem);
  60. return hr;
  61. }
  62. HRESULT CExtractSchedule::ProcessItemInEnum(LPCWSTR pwszTagName, IXMLElement *pItem)
  63. {
  64. if (!StrCmpIW(pwszTagName, L"Schedule"))
  65. {
  66. CProcessSchedule *pPS = new CProcessSchedule(this, NULL, pItem);
  67. if (pPS)
  68. {
  69. pPS->Run();
  70. if (pPS->m_tt.cbTriggerSize)
  71. {
  72. ASSERT(pPS->m_tt.cbTriggerSize == sizeof(m_tt));
  73. m_pExtractRoot->m_tt = pPS->m_tt;
  74. }
  75. delete pPS;
  76. }
  77. return E_ABORT; // abort our enumerations
  78. }
  79. else if (!StrCmpIW(pwszTagName, L"Channel"))
  80. {
  81. return DoChild(new CExtractSchedule(pItem, m_pExtractRoot));
  82. }
  83. return S_OK; // ignore other tags
  84. }
  85. HRESULT CExtractSchedule::GetTaskTrigger(TASK_TRIGGER *ptt)
  86. {
  87. if ((0 == m_tt.cbTriggerSize) || // No task trigger
  88. (0 == m_tt.wBeginYear)) // Invalid task trigger
  89. {
  90. return E_FAIL;
  91. }
  92. if (m_tt.cbTriggerSize <= ptt->cbTriggerSize)
  93. {
  94. *ptt = m_tt;
  95. return S_OK;
  96. }
  97. WORD cbTriggerSize = ptt->cbTriggerSize;
  98. CopyMemory(ptt, &m_tt, cbTriggerSize);
  99. ptt->cbTriggerSize = cbTriggerSize;
  100. return S_FALSE;
  101. }
  102. //==============================================================================
  103. // XML OM Helper functions
  104. //==============================================================================
  105. HRESULT GetXMLAttribute(IXMLElement *pItem, LPCWSTR pwszAttribute, VARIANT *pvRet)
  106. {
  107. BSTR bstrName=NULL;
  108. HRESULT hr=E_FAIL;
  109. pvRet->vt = VT_EMPTY;
  110. bstrName = SysAllocString(pwszAttribute);
  111. if (bstrName && SUCCEEDED(pItem->getAttribute(bstrName, pvRet)))
  112. {
  113. hr = S_OK;
  114. }
  115. SysFreeString(bstrName);
  116. return hr;
  117. }
  118. HRESULT GetXMLStringAttribute(IXMLElement *pItem, LPCWSTR pwszAttribute, BSTR *pbstrRet)
  119. {
  120. VARIANT var;
  121. BSTR bstrName=NULL;
  122. HRESULT hr=E_FAIL;
  123. *pbstrRet = NULL;
  124. var.vt = VT_EMPTY;
  125. bstrName = SysAllocString(pwszAttribute);
  126. if (bstrName && SUCCEEDED(pItem->getAttribute(bstrName, &var)))
  127. {
  128. if (var.vt == VT_BSTR && var.bstrVal != NULL)
  129. {
  130. *pbstrRet = var.bstrVal;
  131. hr = S_OK;
  132. }
  133. }
  134. SysFreeString(bstrName);
  135. if (FAILED(hr) && var.vt != VT_EMPTY)
  136. VariantClear(&var);
  137. return hr;
  138. }
  139. DWORD GetXMLDwordAttribute(IXMLElement *pItem, LPCWSTR pwszAttribute, DWORD dwDefault)
  140. {
  141. VARIANT var;
  142. if (SUCCEEDED(GetXMLAttribute(pItem, pwszAttribute, &var)))
  143. {
  144. if (var.vt == VT_I4)
  145. return var.lVal;
  146. if (var.vt == VT_I2)
  147. return var.iVal;
  148. if (var.vt == VT_BSTR)
  149. {
  150. LPCWSTR pwsz = var.bstrVal;
  151. DWORD dwRet;
  152. if (!StrToIntExW(pwsz, 0, (int *)&dwRet))
  153. dwRet = dwDefault;
  154. SysFreeString(var.bstrVal);
  155. return dwRet;
  156. }
  157. VariantClear(&var);
  158. }
  159. return dwDefault;
  160. }
  161. // If failure return code, *pfRet wasn't changed
  162. HRESULT GetXMLBoolAttribute(IXMLElement *pItem, LPCWSTR pwszAttribute, BOOL *pfRet)
  163. {
  164. VARIANT var;
  165. HRESULT hr=E_FAIL;
  166. if (SUCCEEDED(GetXMLAttribute(pItem, pwszAttribute, &var)))
  167. {
  168. if (var.vt == VT_BOOL)
  169. {
  170. *pfRet = (var.boolVal == VARIANT_TRUE);
  171. hr = S_OK;
  172. }
  173. else if (var.vt == VT_BSTR)
  174. {
  175. if (!StrCmpIW(var.bstrVal, L"YES") ||
  176. !StrCmpIW(var.bstrVal, L"\"YES\""))
  177. {
  178. *pfRet = TRUE;
  179. hr = S_OK;
  180. }
  181. else if (!StrCmpIW(var.bstrVal, L"NO") ||
  182. !StrCmpIW(var.bstrVal, L"\"NO\""))
  183. {
  184. *pfRet = FALSE;
  185. hr = S_OK;
  186. }
  187. }
  188. else
  189. hr = E_FAIL;
  190. VariantClear(&var);
  191. }
  192. return hr;
  193. }
  194. HRESULT GetXMLTimeAttributes(IXMLElement *pItem, CDF_TIME *pTime)
  195. {
  196. pTime->wReserved = 0;
  197. pTime->wDay = (WORD) GetXMLDwordAttribute(pItem, L"DAY", 0);
  198. pTime->wHour = (WORD) GetXMLDwordAttribute(pItem, L"HOUR", 0);
  199. pTime->wMin = (WORD) GetXMLDwordAttribute(pItem, L"MIN", 0);
  200. pTime->dwConvertedMinutes = (24 * 60 * pTime->wDay) +
  201. ( 60 * pTime->wHour) +
  202. ( pTime->wMin);
  203. return S_OK;
  204. }
  205. inline BOOL IsNumber(WCHAR x) { return (x >= L'0' && x <= L'9'); }
  206. HRESULT GetXMLTimeZoneAttribute(IXMLElement *pItem, LPCWSTR pwszAttribute, int *piRet)
  207. {
  208. BSTR bstrVal;
  209. HRESULT hrRet = E_FAIL;
  210. ASSERT(pItem && piRet);
  211. if (SUCCEEDED(GetXMLStringAttribute(pItem, pwszAttribute, &bstrVal)))
  212. {
  213. if(bstrVal && bstrVal[0] &&
  214. IsNumber(bstrVal[1]) && IsNumber(bstrVal[2]) &&
  215. IsNumber(bstrVal[3]) && IsNumber(bstrVal[4]))
  216. {
  217. *piRet = 1000*(bstrVal[1] - L'0') +
  218. 100*(bstrVal[2] - L'0') +
  219. 10*(bstrVal[3] - L'0') +
  220. bstrVal[4] - L'0';
  221. hrRet = S_OK;
  222. }
  223. if(bstrVal[0] == L'-')
  224. *piRet *= -1;
  225. }
  226. SysFreeString(bstrVal);
  227. return hrRet;
  228. }
  229. //==============================================================================
  230. // TEMP fn to convert ISO 1234:5678 to SYSTEMTIME
  231. // ISODateToSystemTime returns false if there is a parse error
  232. // true if there isn't
  233. //==============================================================================
  234. BOOL ValidateSystemTime(SYSTEMTIME *time)
  235. {
  236. // IE6 27665. Some incompetent XML file writers provide slightly invalid dates, like 9-31-01.
  237. // Everybody knows that September has 30 days. Anyway, we'll do some minimal fix-up here.
  238. switch (time->wMonth)
  239. {
  240. case 2: // February
  241. // Rule is, every four years is a leap year, except for centuries.
  242. if ((time->wYear % 4) || ((time->wYear % 100)==0))
  243. {
  244. if (time->wDay > 28)
  245. {
  246. time->wDay = 28;
  247. }
  248. }
  249. else if (time->wDay > 29)
  250. {
  251. time->wDay = 29;
  252. }
  253. break;
  254. case 1: // January
  255. case 3: // March
  256. case 5: // May
  257. case 7: // July
  258. case 8: // August
  259. case 10: // October
  260. case 12: // December
  261. if (time->wDay>31)
  262. {
  263. time->wDay = 31;
  264. }
  265. break;
  266. default: // the other 4 months. These have 30 days, apparently.
  267. if (time->wDay>30)
  268. {
  269. time->wDay = 30;
  270. }
  271. break;
  272. }
  273. return TRUE;
  274. }
  275. // yyyy-mm-dd[Thh:mm[+zzzz]]
  276. BOOL ISODateToSystemTime(LPCWSTR string, SYSTEMTIME *time, long *timezone)
  277. {
  278. if (!string || (lstrlenW(string) < 10) || !time)
  279. return FALSE;
  280. ZeroMemory(time, sizeof(SYSTEMTIME));
  281. if (timezone)
  282. *timezone = 0;
  283. if (IsNumber(string[0]) &&
  284. IsNumber(string[1]) &&
  285. IsNumber(string[2]) &&
  286. IsNumber(string[3]) &&
  287. (string[4] != L'\0') &&
  288. IsNumber(string[5]) &&
  289. IsNumber(string[6]) &&
  290. (string[7] != L'\0') &&
  291. IsNumber(string[8]) &&
  292. IsNumber(string[9]))
  293. {
  294. time->wYear = 1000*(string[0] - L'0') +
  295. 100*(string[1] - L'0') +
  296. 10*(string[2] - L'0') +
  297. string[3] - L'0';
  298. time->wMonth = 10*(string[5] - L'0') + string[6] - L'0';
  299. time->wDay = 10*(string[8] - L'0') + string[9] - L'0';
  300. }
  301. else
  302. {
  303. return FALSE;
  304. }
  305. if ((string[10]!= L'\0') &&
  306. IsNumber(string[11]) &&
  307. IsNumber(string[12]) &&
  308. (string[13] != L'\0') &&
  309. IsNumber(string[14]) &&
  310. IsNumber(string[15]))
  311. {
  312. time->wHour = 10*(string[11] - L'0') + string[12] - L'0';
  313. time->wMinute = 10*(string[14] - L'0') + string[15] - L'0';
  314. if (timezone &&
  315. (string[16]!= L'\0') &&
  316. IsNumber(string[17]) &&
  317. IsNumber(string[18]) &&
  318. IsNumber(string[19]) &&
  319. IsNumber(string[20]))
  320. {
  321. *timezone = 1000*(string[17] - L'0') +
  322. 100*(string[18] - L'0') +
  323. 10*(string[19] - L'0') +
  324. string[20] - L'0';
  325. if(string[16] == L'-')
  326. *timezone = - *timezone;
  327. }
  328. }
  329. return ValidateSystemTime(time);
  330. }
  331. //==============================================================================
  332. // CProcessElement class provides generic support for sync or async enumeration
  333. // of an XML OM
  334. //==============================================================================
  335. CProcessElement::CProcessElement(CProcessElementSink *pParent,
  336. CProcessRoot *pRoot,
  337. IXMLElement *pEle)
  338. {
  339. ASSERT(m_pRunAgent == NULL && m_pCurChild == NULL && m_pCollection == NULL);
  340. m_pElement = pEle; pEle->AddRef();
  341. m_pRoot = pRoot;
  342. m_pParent = pParent;
  343. }
  344. CProcessElement::~CProcessElement()
  345. {
  346. ASSERT(!m_pCurChild);
  347. CRunDeliveryAgent::SafeRelease(m_pRunAgent);
  348. SAFERELEASE(m_pCollection);
  349. SAFERELEASE(m_pElement);
  350. SAFERELEASE(m_pChildElement);
  351. }
  352. HRESULT CProcessElement::Pause(DWORD dwFlags)
  353. {
  354. if (m_pCurChild)
  355. return m_pCurChild->Pause(dwFlags);
  356. ASSERT(m_pRunAgent);
  357. if (m_pRunAgent)
  358. return m_pRunAgent->AgentPause(dwFlags);
  359. return E_FAIL;
  360. }
  361. HRESULT CProcessElement::Resume(DWORD dwFlags)
  362. {
  363. if (m_pCurChild)
  364. return m_pCurChild->Resume(dwFlags);
  365. if (m_pRunAgent)
  366. m_pRunAgent->AgentResume(dwFlags);
  367. else
  368. DoEnumeration();
  369. return S_OK;
  370. }
  371. HRESULT CProcessElement::Abort(DWORD dwFlags)
  372. {
  373. if (m_pCurChild)
  374. {
  375. m_pCurChild->Abort(dwFlags);
  376. SAFEDELETE(m_pCurChild);
  377. }
  378. if (m_pRunAgent)
  379. {
  380. // Prevent reentrancy into OnAgentEnd
  381. m_pRunAgent->LeaveMeAlone();
  382. m_pRunAgent->AgentAbort(dwFlags);
  383. CRunDeliveryAgent::SafeRelease(m_pRunAgent);
  384. }
  385. return S_OK;
  386. }
  387. HRESULT CProcessElement::Run()
  388. {
  389. ASSERT(!m_pCollection);
  390. ASSERT(m_lMax == 0);
  391. // ASSERT(m_fSentEnumerationComplete == FALSE); // DoEnumeration may have sent this
  392. m_lIndex = 0;
  393. if (SUCCEEDED(m_pElement->get_children(&m_pCollection)) && m_pCollection)
  394. {
  395. m_pCollection->get_length(&m_lMax);
  396. }
  397. else
  398. m_lMax = 0;
  399. return DoEnumeration(); // Will call OnChildDone when appropriate
  400. }
  401. HRESULT CProcessElement::OnAgentEnd(const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  402. long lSizeDownloaded, HRESULT hrResult, LPCWSTR wszResult,
  403. BOOL fSynchronous)
  404. {
  405. // Our delivery agent is done. Continue enumeration
  406. ASSERT(!m_pCurChild);
  407. if (lSizeDownloaded > 0)
  408. m_pRoot->m_dwCurSizeKB += (ULONG) lSizeDownloaded;
  409. TraceMsg(TF_THISMODULE, "ChannelAgent up to %dkb of %dkb", m_pRoot->m_dwCurSizeKB, m_pRoot->m_pChannelAgent->m_dwMaxSizeKB);
  410. if ((hrResult == INET_E_AGENT_MAX_SIZE_EXCEEDED) ||
  411. (hrResult == INET_E_AGENT_CACHE_SIZE_EXCEEDED))
  412. {
  413. DBG("CProcessElement got max size or cache size exceeded; not running any more delivery agents");
  414. m_pRoot->m_fMaxSizeExceeded = TRUE;
  415. m_pRoot->m_pChannelAgent->SetEndStatus(hrResult);
  416. }
  417. CRunDeliveryAgent::SafeRelease(m_pRunAgent);
  418. if (fSynchronous)
  419. {
  420. // we are still in our DoDeliveryAgent call. Let it return out through there.
  421. return S_OK;
  422. }
  423. // Continue enumeration, or start enumeration if we haven't yet.
  424. if (m_fStartedEnumeration)
  425. DoEnumeration();
  426. else
  427. Run();
  428. return S_OK;
  429. }
  430. HRESULT CProcessElement::DoEnumeration()
  431. {
  432. IDispatch *pDisp;
  433. IXMLElement *pItem;
  434. BSTR bstrTagName;
  435. VARIANT vIndex, vEmpty;
  436. HRESULT hr = S_OK;
  437. BOOL fStarted = FALSE;
  438. m_fStartedEnumeration = TRUE;
  439. ASSERT(m_pCollection || !m_lMax);
  440. if (m_pRoot && m_pRoot->IsPaused())
  441. {
  442. DBG("CProcessElement::DoEnumeration returning E_PENDING, we're paused");
  443. return E_PENDING;
  444. }
  445. vEmpty.vt = VT_EMPTY;
  446. for (; (m_lIndex < m_lMax) && !fStarted && (hr != E_ABORT); m_lIndex++)
  447. {
  448. vIndex.vt = VT_UI4;
  449. vIndex.lVal = m_lIndex;
  450. if (SUCCEEDED(m_pCollection->item(vIndex, vEmpty, &pDisp)))
  451. {
  452. if (SUCCEEDED(pDisp->QueryInterface(IID_IXMLElement, (void **)&pItem)))
  453. {
  454. if (SUCCEEDED(pItem->get_tagName(&bstrTagName)) && bstrTagName)
  455. {
  456. SAFERELEASE(m_pChildElement);
  457. m_pChildElement=pItem;
  458. m_pChildElement->AddRef();
  459. hr = ProcessItemInEnum(bstrTagName, pItem);
  460. SysFreeString(bstrTagName);
  461. if (hr == E_PENDING)
  462. fStarted = TRUE;
  463. }
  464. pItem->Release();
  465. }
  466. pDisp->Release();
  467. }
  468. }
  469. // Tell this instance we're done with enumeration, unless we already have
  470. if (!fStarted && !m_fSentEnumerationComplete)
  471. {
  472. m_fSentEnumerationComplete = TRUE;
  473. hr = EnumerationComplete(); // WARNING this eats E_ABORT
  474. if (hr == E_PENDING)
  475. fStarted = TRUE;
  476. }
  477. // Notify our parent if we're done with our enumeration,
  478. if (!fStarted)
  479. {
  480. if (m_pParent) // Check for CExtractSchedule
  481. m_pParent->OnChildDone(this, hr); // This may delete us
  482. }
  483. if (hr == E_ABORT)
  484. return E_ABORT;
  485. return (fStarted) ? E_PENDING : S_OK;
  486. }
  487. HRESULT CProcessElement::OnChildDone(CProcessElement *pChild, HRESULT hr)
  488. {
  489. ASSERT(pChild && (!m_pCurChild || (pChild == m_pCurChild)));
  490. if (m_pCurChild)
  491. {
  492. // A child returned from async operation.
  493. SAFEDELETE(m_pCurChild);
  494. // Continue enumeration. This will call our parent's ChildDone if it
  495. // finishes, so it may delete us.
  496. DoEnumeration();
  497. }
  498. else
  499. {
  500. // Our child has finished synchronously. Ignore (DoChild() will take care of it).
  501. }
  502. return S_OK;
  503. }
  504. HRESULT CProcessElement::DoChild(CProcessElement *pChild)
  505. {
  506. HRESULT hr;
  507. ASSERT(m_pCurChild == NULL);
  508. if (!pChild)
  509. return E_POINTER; // FEATURE should call parent's OnChildDone here
  510. hr = pChild->Run();
  511. if (hr == E_PENDING)
  512. {
  513. // Returned async. Will call back OnChildDone.
  514. m_pCurChild = pChild;
  515. return E_PENDING;
  516. }
  517. // Synchronously returned. Clean up.
  518. delete pChild;
  519. return hr;
  520. }
  521. // E_PENDING if async operation started
  522. HRESULT CProcessElement::DoDeliveryAgent(ISubscriptionItem *pItem, REFCLSID rclsid, LPCWSTR pwszURL)
  523. {
  524. ASSERT(pItem);
  525. HRESULT hr=E_FAIL;
  526. if (m_pRoot->m_fMaxSizeExceeded)
  527. {
  528. // DBG("CProcessElement::RunDeliveryAgent failing; exceeded max size.");
  529. return E_FAIL;
  530. }
  531. if (m_pRunAgent)
  532. {
  533. DBG_WARN("CProcessElement::DoDeliveryAgent already running!");
  534. return E_FAIL;
  535. }
  536. m_pRunAgent = new CChannelAgentHolder(m_pRoot->m_pChannelAgent, this);
  537. if (m_pRunAgent)
  538. {
  539. hr = m_pRunAgent->Init(this, pItem, rclsid);
  540. if (SUCCEEDED(hr))
  541. hr = m_pRunAgent->StartAgent();
  542. if (hr == E_PENDING)
  543. {
  544. m_pRoot->m_pChannelAgent->SendUpdateProgress(pwszURL, ++(m_pRoot->m_iTotalStarted), -1, m_pRoot->m_dwCurSizeKB);
  545. }
  546. else
  547. CRunDeliveryAgent::SafeRelease(m_pRunAgent);
  548. }
  549. else
  550. hr = E_OUTOFMEMORY;
  551. return hr;
  552. }
  553. HRESULT CProcessElement::DoSoftDist(IXMLElement *pItem)
  554. {
  555. HRESULT hr = S_OK;
  556. ISubscriptionItem *pSubsItem;
  557. if (SUCCEEDED(m_pRoot->CreateStartItem(&pSubsItem)))
  558. {
  559. if (pSubsItem)
  560. {
  561. hr = DoDeliveryAgent(pSubsItem, CLSID_CDLAgent);
  562. pSubsItem->Release();
  563. }
  564. }
  565. return hr;
  566. }
  567. HRESULT CProcessElement::DoWebCrawl(IXMLElement *pItem, LPCWSTR pwszURL /* = NULL */)
  568. {
  569. BSTR bstrURL=NULL, bstrTmp=NULL;
  570. HRESULT hr = S_OK;
  571. ISubscriptionItem *pSubsItem;
  572. DWORD dwLevels=0, dwFlags;
  573. LPWSTR pwszUrl2=NULL;
  574. BOOL fOffline=FALSE;
  575. if (!pwszURL && SUCCEEDED(GetXMLStringAttribute(pItem, L"HREF", &bstrURL)) && bstrURL)
  576. pwszURL = bstrURL;
  577. if (pwszURL)
  578. {
  579. SYSTEMTIME stLastMod;
  580. long lTimezone;
  581. hr = CombineWithBaseUrl(pwszURL, &pwszUrl2);
  582. if (SUCCEEDED(hr) && pwszUrl2)
  583. pwszURL = pwszUrl2; // Got a new URL
  584. hr = CUrlDownload::IsValidURL(pwszURL);
  585. if (SUCCEEDED(hr) &&
  586. SUCCEEDED(GetXMLStringAttribute(m_pElement, L"LastMod", &bstrTmp)) &&
  587. ISODateToSystemTime(bstrTmp, &stLastMod, &lTimezone))
  588. {
  589. // Check Last Modified time
  590. TCHAR szThisUrl[INTERNET_MAX_URL_LENGTH];
  591. char chBuf[MY_MAX_CACHE_ENTRY_INFO];
  592. DWORD dwBufSize = sizeof(chBuf);
  593. LPINTERNET_CACHE_ENTRY_INFO lpInfo = (LPINTERNET_CACHE_ENTRY_INFO) chBuf;
  594. MyOleStrToStrN(szThisUrl, INTERNET_MAX_URL_LENGTH, pwszURL);
  595. hr = GetUrlInfoAndMakeSticky(NULL, szThisUrl, lpInfo, dwBufSize, 0);
  596. if (SUCCEEDED(hr))
  597. {
  598. FILETIME ft;
  599. if (SystemTimeToFileTime(&stLastMod, &ft))
  600. {
  601. // APPCOMPAT: In an ideal world, all servers would support LastModifiedTime accurately.
  602. // In our world, some do not support it and wininet returns a value of zero.
  603. // Without maintaining checksums of the files, we have two options: always download
  604. // the URL or never update it. Since it would be odd not to update it, we always do.
  605. if ((lpInfo->LastModifiedTime.dwHighDateTime || lpInfo->LastModifiedTime.dwLowDateTime)
  606. && (lpInfo->LastModifiedTime.dwHighDateTime >= ft.dwHighDateTime)
  607. && ((lpInfo->LastModifiedTime.dwHighDateTime > ft.dwHighDateTime)
  608. || (lpInfo->LastModifiedTime.dwLowDateTime >= ft.dwLowDateTime)))
  609. {
  610. // Skip it.
  611. TraceMsg(TF_THISMODULE, "Running webcrawl OFFLINE due to Last Modified time URL=%ws", pwszURL);
  612. fOffline = TRUE;
  613. }
  614. }
  615. }
  616. hr = S_OK;
  617. }
  618. SAFEFREEBSTR(bstrTmp);
  619. if (SUCCEEDED(hr) && SUCCEEDED(m_pRoot->CreateStartItem(&pSubsItem)))
  620. {
  621. WriteOLESTR(pSubsItem, c_szPropURL, pwszURL);
  622. dwLevels = GetXMLDwordAttribute(pItem, L"LEVEL", 0);
  623. if (dwLevels && m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SOME))
  624. {
  625. // Note: MaxChannelLevels is stored as N+1 because 0
  626. // means the restriction is disabled.
  627. DWORD dwMaxLevels = SHRestricted2W(REST_MaxChannelLevels, NULL, 0);
  628. if (!dwMaxLevels)
  629. dwMaxLevels = MAX_CDF_CRAWL_LEVELS + 1;
  630. if (dwLevels >= dwMaxLevels)
  631. dwLevels = dwMaxLevels - 1;
  632. WriteDWORD(pSubsItem, c_szPropCrawlLevels, dwLevels);
  633. }
  634. if (fOffline)
  635. {
  636. if (SUCCEEDED(ReadDWORD(pSubsItem, c_szPropCrawlFlags, &dwFlags)))
  637. {
  638. dwFlags |= CWebCrawler::WEBCRAWL_PRIV_OFFLINE_MODE;
  639. WriteDWORD(pSubsItem, c_szPropCrawlFlags, dwFlags);
  640. }
  641. }
  642. hr = DoDeliveryAgent(pSubsItem, CLSID_WebCrawlerAgent, pwszURL);
  643. SAFERELEASE(pSubsItem);
  644. }
  645. }
  646. if (bstrURL)
  647. SysFreeString(bstrURL);
  648. if (pwszUrl2)
  649. MemFree(pwszUrl2);
  650. return hr;
  651. }
  652. BOOL CProcessElement::ShouldDownloadLogo(IXMLElement *pLogo)
  653. {
  654. return m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SOME);
  655. }
  656. // If relative url, will combine with most recent base URL
  657. // *ppwszRetUrl should be NULL & will be MemAlloced
  658. HRESULT CProcessElement::CombineWithBaseUrl(LPCWSTR pwszUrl, LPWSTR *ppwszRetUrl)
  659. {
  660. ASSERT(ppwszRetUrl && !*ppwszRetUrl && pwszUrl);
  661. // Optimization: if pwszURL is absolute, we don't need to do this expensive
  662. // combine operation
  663. // if (*pwszUrl != L'/') // BOGUS
  664. // {
  665. // *ppwszRetUrl = StrDupW(pwszUrl);
  666. // return S_FALSE; // Succeeded; pwszUrl is already OK
  667. // }
  668. // Find appropriate Base URL to use
  669. LPCWSTR pwszBaseUrl = GetBaseUrl();
  670. WCHAR wszUrl[INTERNET_MAX_URL_LENGTH];
  671. DWORD dwLen = ARRAYSIZE(wszUrl);
  672. if (SUCCEEDED(UrlCombineW(pwszBaseUrl, pwszUrl, wszUrl, &dwLen, 0)))
  673. {
  674. *ppwszRetUrl = StrDupW(wszUrl);
  675. return (*ppwszRetUrl) ? S_OK : E_OUTOFMEMORY;
  676. }
  677. *ppwszRetUrl = NULL;
  678. return E_FAIL; // Erg?
  679. }
  680. //==============================================================================
  681. // CProcessElement derived classes, to handle specific CDF tags
  682. //==============================================================================
  683. // CProcessRoot doesn't behave like a normal CProcessElement class. It calls
  684. // CProcessChannel to process the *same element*
  685. CProcessRoot::CProcessRoot(CChannelAgent *pParent, IXMLElement *pItem) :
  686. CProcessElement(pParent, NULL, pItem)
  687. {
  688. ASSERT(m_pDefaultStartItem == FALSE && m_pTracking == NULL && !m_dwCurSizeKB);
  689. m_pRoot = this;
  690. m_pChannelAgent = pParent; pParent->AddRef();
  691. m_iTotalStarted = 1;
  692. }
  693. CProcessRoot::~CProcessRoot()
  694. {
  695. SAFEDELETE(m_pTracking);
  696. SAFERELEASE(m_pChannelAgent);
  697. SAFERELEASE(m_pDefaultStartItem);
  698. }
  699. // Should never get called. CProcessRoot is an odd duck.
  700. HRESULT CProcessRoot::ProcessItemInEnum(LPCWSTR pwszTagName, IXMLElement *pItem)
  701. {
  702. ASSERT(0);
  703. return E_NOTIMPL;
  704. }
  705. HRESULT CProcessRoot::CreateStartItem(ISubscriptionItem **ppItem)
  706. {
  707. if (ppItem)
  708. *ppItem = NULL;
  709. if (!m_pDefaultStartItem)
  710. {
  711. DoCloneSubscriptionItem(m_pChannelAgent->GetStartItem(), NULL, &m_pDefaultStartItem);
  712. if (m_pDefaultStartItem)
  713. {
  714. DWORD dwTemp;
  715. // Clear out properties we don't want
  716. const LPCWSTR pwszPropsToClear[] =
  717. {
  718. c_szPropCrawlLevels,
  719. c_szPropCrawlLocalDest,
  720. c_szPropCrawlActualSize,
  721. c_szPropCrawlMaxSize,
  722. c_szPropCrawlGroupID
  723. };
  724. VARIANT varEmpty[ARRAYSIZE(pwszPropsToClear)] = {0};
  725. ASSERT(ARRAYSIZE(pwszPropsToClear) == ARRAYSIZE(varEmpty));
  726. m_pDefaultStartItem->WriteProperties(
  727. ARRAYSIZE(pwszPropsToClear), pwszPropsToClear, varEmpty);
  728. // Add in properties we do want
  729. dwTemp = DELIVERY_AGENT_FLAG_NO_BROADCAST |
  730. DELIVERY_AGENT_FLAG_NO_RESTRICTIONS;
  731. WriteDWORD(m_pDefaultStartItem, c_szPropAgentFlags, dwTemp);
  732. if (FAILED(ReadDWORD(m_pDefaultStartItem, c_szPropCrawlFlags, &dwTemp)))
  733. {
  734. WriteDWORD(m_pDefaultStartItem, c_szPropCrawlFlags,
  735. WEBCRAWL_GET_IMAGES|WEBCRAWL_LINKS_ELSEWHERE);
  736. }
  737. WriteLONGLONG(m_pDefaultStartItem, c_szPropCrawlNewGroupID, m_pChannelAgent->m_llCacheGroupID);
  738. }
  739. }
  740. if (m_pDefaultStartItem && ppItem)
  741. {
  742. DoCloneSubscriptionItem(m_pDefaultStartItem, NULL, ppItem);
  743. if (*ppItem)
  744. {
  745. // Add in properties for our new clone
  746. if ((m_pChannelAgent->m_dwMaxSizeKB > 0) &&
  747. (m_dwCurSizeKB <= m_pChannelAgent->m_dwMaxSizeKB))
  748. {
  749. WriteDWORD(*ppItem, c_szPropCrawlMaxSize,
  750. (m_pChannelAgent->m_dwMaxSizeKB - m_dwCurSizeKB));
  751. }
  752. }
  753. }
  754. return (ppItem) ? (*ppItem) ? S_OK : E_FAIL :
  755. (m_pDefaultStartItem) ? S_OK : E_FAIL;
  756. }
  757. HRESULT CProcessRoot::Run()
  758. {
  759. if (FAILED(CreateStartItem(NULL)))
  760. return E_FAIL;
  761. return DoChild(new CProcessChannel(this, this, m_pElement));
  762. }
  763. HRESULT CProcessRoot::DoTrackingFromItem(IXMLElement *pItem, LPCWSTR pwszUrl, BOOL fForceLog)
  764. {
  765. HRESULT hr = E_FAIL;
  766. // if m_pTracking is not created before this call, means no <LogTarget> tag was found or
  767. // global logging is turned off
  768. if (m_pTracking)
  769. hr = m_pTracking->ProcessTrackingInItem(pItem, pwszUrl, fForceLog);
  770. return hr;
  771. }
  772. HRESULT CProcessRoot::DoTrackingFromLog(IXMLElement *pItem)
  773. {
  774. HRESULT hr = S_OK;
  775. if (!m_pTracking
  776. && !SHRestricted2W(REST_NoChannelLogging, m_pChannelAgent->GetUrl(), 0)
  777. && !ReadRegDWORD(HKEY_CURRENT_USER, c_szRegKey, c_szNoChannelLogging))
  778. {
  779. m_pTracking = new CUrlTrackingCache(m_pChannelAgent->GetStartItem(), m_pChannelAgent->GetUrl());
  780. }
  781. if (!m_pTracking)
  782. return E_OUTOFMEMORY;
  783. hr = m_pTracking->ProcessTrackingInLog(pItem);
  784. // skip tracking if PostURL is not specified
  785. if (m_pTracking->get_PostURL() == NULL)
  786. {
  787. SAFEDELETE(m_pTracking);
  788. }
  789. return hr;
  790. }
  791. // Overload this since we never do enumeration. Call delivery agent if necessary,
  792. // call m_pParent->OnChildDone if necessary
  793. HRESULT CProcessRoot::OnChildDone(CProcessElement *pChild, HRESULT hr)
  794. {
  795. ASSERT(pChild && (!m_pCurChild || (pChild == m_pCurChild)));
  796. // Our processing is done. Now we decide if we'd like to call the post agent.
  797. BSTR bstrURL=NULL;
  798. ISubscriptionItem *pStartItem;
  799. hr = S_OK;
  800. SAFEDELETE(m_pCurChild);
  801. ASSERT(m_pDefaultStartItem);
  802. ReadBSTR(m_pDefaultStartItem, c_szTrackingPostURL, &bstrURL);
  803. if (bstrURL && *bstrURL)
  804. {
  805. TraceMsg(TF_THISMODULE, "ChannelAgent calling post agent posturl=%ws", bstrURL);
  806. if (SUCCEEDED(m_pRoot->CreateStartItem(&pStartItem)))
  807. {
  808. m_pRunAgent = new CChannelAgentHolder(m_pChannelAgent, this);
  809. if (m_pRunAgent)
  810. {
  811. hr = m_pRunAgent->Init(this, pStartItem, CLSID_PostAgent);
  812. if (SUCCEEDED(hr))
  813. hr = m_pRunAgent->StartAgent();
  814. if (hr != E_PENDING)
  815. CRunDeliveryAgent::SafeRelease(m_pRunAgent);
  816. }
  817. pStartItem->Release();
  818. }
  819. }
  820. SysFreeString(bstrURL);
  821. if (hr != E_PENDING)
  822. m_pParent->OnChildDone(this, hr); // This may delete us
  823. return hr;
  824. }
  825. // Our delivery agent (post agent) is done running. Tell CDF agent we're done.
  826. HRESULT CProcessRoot::OnAgentEnd(const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  827. long lSizeDownloaded, HRESULT hrResult, LPCWSTR wszResult,
  828. BOOL fSynchronous)
  829. {
  830. if (!fSynchronous)
  831. m_pParent->OnChildDone(this, S_OK); // This may delete us
  832. return S_OK;
  833. }
  834. CProcessChannel::CProcessChannel(CProcessElementSink *pParent,
  835. CProcessRoot *pRoot,
  836. IXMLElement *pItem) :
  837. CProcessElement(pParent, pRoot, pItem)
  838. {
  839. m_fglobalLog = FALSE;
  840. }
  841. CProcessChannel::~CProcessChannel()
  842. {
  843. SAFEFREEBSTR(m_bstrBaseUrl);
  844. }
  845. HRESULT CProcessChannel::CheckPreCache()
  846. {
  847. BOOL fPreCache;
  848. if (SUCCEEDED(GetXMLBoolAttribute(m_pElement, L"PreCache", &fPreCache)))
  849. {
  850. if (fPreCache)
  851. return S_OK;
  852. return S_FALSE;
  853. }
  854. return S_OK;
  855. }
  856. HRESULT CProcessChannel::Run()
  857. {
  858. // Process Channel attributes, then any sub elements
  859. if (0 == m_lIndex)
  860. {
  861. m_lIndex ++;
  862. BSTR bstrURL=NULL;
  863. LPWSTR pwszUrl=NULL;
  864. HRESULT hr = S_OK;
  865. ASSERT(!m_bstrBaseUrl);
  866. // Get base URL if specified
  867. GetXMLStringAttribute(m_pElement, L"BASE", &m_bstrBaseUrl);
  868. if (SUCCEEDED(GetXMLStringAttribute(m_pElement, L"HREF", &bstrURL)) && bstrURL)
  869. CombineWithBaseUrl(bstrURL, &pwszUrl);
  870. if (pwszUrl && (m_pRoot==m_pParent))
  871. {
  872. // Use this as default "email url"
  873. WriteOLESTR(m_pRoot->m_pChannelAgent->GetStartItem(), c_szPropEmailURL, pwszUrl);
  874. }
  875. if (pwszUrl && m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SOME) &&
  876. (S_OK == CheckPreCache()))
  877. {
  878. if (E_PENDING == DoWebCrawl(m_pElement, pwszUrl))
  879. {
  880. m_fDownloadedHREF = TRUE;
  881. hr = E_PENDING;
  882. }
  883. }
  884. // If no URL for this <Channel> log, check if global log exists
  885. if (SUCCEEDED(m_pRoot->DoTrackingFromItem(m_pElement, pwszUrl, m_pParent->IsGlobalLog())))
  886. {
  887. SetGlobalLogFlag(TRUE);
  888. }
  889. SAFELOCALFREE(pwszUrl);
  890. SAFEFREEBSTR(bstrURL);
  891. if (hr == E_PENDING)
  892. return hr;
  893. }
  894. // We've processed attributes. Run sub-elements.
  895. return CProcessElement::Run();
  896. }
  897. HRESULT CProcessChannel::ProcessItemInEnum(LPCWSTR pwszTagName, IXMLElement *pItem)
  898. {
  899. HRESULT hr;
  900. BSTR bstrTemp;
  901. if (!StrCmpIW(pwszTagName, L"Logo"))
  902. {
  903. if (ShouldDownloadLogo(pItem))
  904. return DoWebCrawl(pItem);
  905. else
  906. return S_OK;
  907. }
  908. else if (!StrCmpIW(pwszTagName, L"Item"))
  909. {
  910. return DoChild(new CProcessItem(this, m_pRoot, pItem));
  911. }
  912. else if (!StrCmpIW(pwszTagName, L"Channel"))
  913. {
  914. return DoChild(new CProcessChannel(this, m_pRoot, pItem));
  915. }
  916. /*
  917. else if (!StrCmpIW(pwszTagName, L"Login"))
  918. {
  919. // No sub-elements to process. Do it here.
  920. return m_pRoot->ProcessLogin(pItem);
  921. }
  922. */
  923. else if (!StrCmpIW(pwszTagName, L"LOGTARGET"))
  924. {
  925. return m_pRoot->DoTrackingFromLog(pItem);
  926. }
  927. else if (!StrCmpIW(pwszTagName, L"Schedule"))
  928. {
  929. if (m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_DYNAMIC_SCHEDULE))
  930. return DoChild(new CProcessSchedule(this, m_pRoot, pItem));
  931. else
  932. return S_OK;
  933. }
  934. else if (!StrCmpIW(pwszTagName, L"SoftPkg"))
  935. {
  936. return DoSoftDist(pItem);
  937. }
  938. else if (!StrCmpIW(pwszTagName, L"A"))
  939. {
  940. // Process Anchor tag
  941. if (!m_fDownloadedHREF
  942. && (m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SOME) || (m_pRoot==m_pParent))
  943. && SUCCEEDED(GetXMLStringAttribute(pItem, L"HREF", &bstrTemp))
  944. && bstrTemp)
  945. {
  946. LPWSTR pwszUrl=NULL;
  947. hr = S_OK;
  948. CombineWithBaseUrl(bstrTemp, &pwszUrl); // not really necessary (a href)
  949. if (pwszUrl)
  950. {
  951. // Use this as default "email url"
  952. if (m_pRoot == m_pParent)
  953. WriteOLESTR(m_pRoot->m_pChannelAgent->GetStartItem(), c_szPropEmailURL, pwszUrl);
  954. if (m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SOME) &&
  955. (S_OK == CheckPreCache()))
  956. {
  957. hr = DoWebCrawl(m_pElement, pwszUrl);
  958. if (E_PENDING == hr)
  959. m_fDownloadedHREF = TRUE;
  960. // Process tracking for this item
  961. if (SUCCEEDED(m_pRoot->DoTrackingFromItem(m_pElement, pwszUrl, m_pParent->IsGlobalLog())))
  962. SetGlobalLogFlag(TRUE);
  963. }
  964. }
  965. SAFELOCALFREE(pwszUrl);
  966. SysFreeString(bstrTemp);
  967. return hr;
  968. }
  969. return S_OK;
  970. }
  971. return S_OK;
  972. }
  973. CProcessItem::CProcessItem(CProcessElementSink *pParent,
  974. CProcessRoot *pRoot,
  975. IXMLElement *pItem) :
  976. CProcessElement(pParent, pRoot, pItem)
  977. {
  978. }
  979. CProcessItem::~CProcessItem()
  980. {
  981. SAFEFREEBSTR(m_bstrAnchorURL);
  982. }
  983. HRESULT CProcessItem::ProcessItemInEnum(LPCWSTR pwszTagName, IXMLElement *pItem)
  984. {
  985. if (!StrCmpIW(pwszTagName, L"Logo"))
  986. {
  987. if (ShouldDownloadLogo(pItem))
  988. return DoWebCrawl(pItem);
  989. else
  990. return S_OK;
  991. }
  992. else if (!StrCmpIW(pwszTagName, L"Usage"))
  993. {
  994. // Usage tag found.
  995. BSTR bstrValue;
  996. if (SUCCEEDED(GetXMLStringAttribute(pItem, L"Value", &bstrValue)))
  997. {
  998. if (!m_fScreenSaver &&
  999. (!StrCmpIW(bstrValue, L"ScreenSaver") ||
  1000. !StrCmpIW(bstrValue, L"SmartScreen"))) // PCN compat only
  1001. {
  1002. m_fScreenSaver = TRUE;
  1003. }
  1004. if (!m_fDesktop &&
  1005. !StrCmpIW(bstrValue, L"DesktopComponent"))
  1006. {
  1007. m_fDesktop = TRUE;
  1008. }
  1009. if (!m_fEmail &&
  1010. !StrCmpIW(bstrValue, L"Email"))
  1011. {
  1012. m_fEmail = TRUE;
  1013. }
  1014. SysFreeString(bstrValue);
  1015. }
  1016. }
  1017. else if (!StrCmpIW(pwszTagName, L"A"))
  1018. {
  1019. // Anchor tag found; save URL
  1020. if (!m_bstrAnchorURL)
  1021. GetXMLStringAttribute(pItem, L"HREF", &m_bstrAnchorURL);
  1022. }
  1023. return S_OK;
  1024. }
  1025. HRESULT CProcessItem::EnumerationComplete()
  1026. {
  1027. BOOL fPreCache, fPreCacheValid=FALSE;
  1028. BOOL fDoDownload=FALSE;
  1029. BSTR bstrShow=NULL;
  1030. BSTR bstrURL=NULL;
  1031. HRESULT hr = S_OK;
  1032. LPWSTR pwszUrl=NULL;
  1033. // PCN Compat only - not in spec
  1034. if (SUCCEEDED(GetXMLStringAttribute(m_pElement, L"Show", &bstrShow)) && bstrShow)
  1035. {
  1036. if (!StrCmpIW(bstrShow, L"SmartScreen") &&
  1037. !StrCmpIW(bstrShow, L"ScreenSaver"))
  1038. {
  1039. m_fScreenSaver = TRUE;
  1040. }
  1041. SysFreeString(bstrShow);
  1042. bstrShow=NULL;
  1043. }
  1044. // End PCN compat
  1045. if (SUCCEEDED(GetXMLBoolAttribute(m_pElement, L"PreCache", &fPreCache)))
  1046. {
  1047. fPreCacheValid = TRUE;
  1048. }
  1049. // Get the URL from our attribute, or from Anchor tag if not available
  1050. if (FAILED(GetXMLStringAttribute(m_pElement, L"HREF", &bstrURL)) || !bstrURL)
  1051. {
  1052. bstrURL = m_bstrAnchorURL;
  1053. m_bstrAnchorURL = NULL;
  1054. }
  1055. // Get the combined URL
  1056. if (bstrURL)
  1057. CombineWithBaseUrl(bstrURL, &pwszUrl);
  1058. if (pwszUrl)
  1059. {
  1060. // Process tracking for this item
  1061. m_pRoot->DoTrackingFromItem(m_pElement, pwszUrl, IsGlobalLog());
  1062. // Find if we should use this url for the Email agent
  1063. if (m_fEmail)
  1064. {
  1065. // Yes, put this url in the end report
  1066. DBG("Using custom email url");
  1067. WriteOLESTR(m_pRoot->m_pChannelAgent->GetStartItem(), c_szPropEmailURL, pwszUrl);
  1068. }
  1069. if (m_fScreenSaver)
  1070. {
  1071. m_pRoot->m_pChannelAgent->SetScreenSaverURL(pwszUrl);
  1072. }
  1073. // Figure out if we should download our "href" based on Usage and Precache tag
  1074. if (fPreCacheValid)
  1075. {
  1076. if (fPreCache)
  1077. {
  1078. if (m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SOME))
  1079. fDoDownload = TRUE;
  1080. else if (m_fScreenSaver && m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SCRNSAVER))
  1081. fDoDownload = TRUE;
  1082. }
  1083. }
  1084. else
  1085. {
  1086. if (m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_ALL))
  1087. fDoDownload = TRUE;
  1088. else if (m_fScreenSaver && m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SCRNSAVER))
  1089. fDoDownload = TRUE;
  1090. }
  1091. // if (m_fDesktop)
  1092. // Do something for desktop components
  1093. if (fDoDownload && pwszUrl)
  1094. hr = DoWebCrawl(m_pElement, pwszUrl);
  1095. } // pwszUrl
  1096. SAFEFREEBSTR(bstrURL);
  1097. SAFELOCALFREE(pwszUrl);
  1098. return hr;
  1099. }
  1100. CProcessSchedule::CProcessSchedule(CProcessElementSink *pParent,
  1101. CProcessRoot *pRoot,
  1102. IXMLElement *pItem) :
  1103. CProcessElement(pParent, pRoot, pItem)
  1104. {
  1105. }
  1106. HRESULT CProcessSchedule::Run()
  1107. {
  1108. // Get attributes (Start and End date) first
  1109. BSTR bstr=NULL;
  1110. long lTimeZone;
  1111. if (FAILED(GetXMLStringAttribute(m_pElement, L"StartDate", &bstr)) ||
  1112. !ISODateToSystemTime(bstr, &m_stStartDate, &lTimeZone))
  1113. {
  1114. GetLocalTime(&m_stStartDate);
  1115. }
  1116. SAFEFREEBSTR(bstr);
  1117. if (FAILED(GetXMLStringAttribute(m_pElement, L"StopDate", &bstr)) ||
  1118. !ISODateToSystemTime(bstr, &m_stEndDate, &lTimeZone))
  1119. {
  1120. ZeroMemory(&m_stEndDate, sizeof(SYSTEMTIME));
  1121. }
  1122. SAFEFREEBSTR(bstr);
  1123. return CProcessElement::Run();
  1124. }
  1125. HRESULT CProcessSchedule::ProcessItemInEnum(LPCWSTR pwszTagName, IXMLElement *pItem)
  1126. {
  1127. if (!StrCmpIW(pwszTagName, L"IntervalTime"))
  1128. {
  1129. GetXMLTimeAttributes(pItem, &m_timeInterval);
  1130. }
  1131. else if (!StrCmpIW(pwszTagName, L"EarliestTime"))
  1132. {
  1133. GetXMLTimeAttributes(pItem, &m_timeEarliest);
  1134. }
  1135. else if (!StrCmpIW(pwszTagName, L"LatestTime"))
  1136. {
  1137. GetXMLTimeAttributes(pItem, &m_timeLatest);
  1138. }
  1139. return S_OK;
  1140. }
  1141. HRESULT CProcessSchedule::EnumerationComplete()
  1142. {
  1143. DBG("CProcessSchedule::EnumerationComplete");
  1144. int iZone;
  1145. if (FAILED(GetXMLTimeZoneAttribute(m_pElement, L"TimeZone", &iZone)))
  1146. iZone = 9999;
  1147. m_tt.cbTriggerSize = sizeof(m_tt);
  1148. // m_pRoot is null for XMLElementToTaskTrigger call
  1149. // Always run ScheduleToTaskTrigger
  1150. if (SUCCEEDED(ScheduleToTaskTrigger(&m_tt, &m_stStartDate, &m_stEndDate,
  1151. (long) m_timeInterval.dwConvertedMinutes,
  1152. (long) m_timeEarliest.dwConvertedMinutes,
  1153. (long) m_timeLatest.dwConvertedMinutes,
  1154. iZone))
  1155. && m_pRoot)
  1156. {
  1157. SUBSCRIPTIONITEMINFO sii = { sizeof(SUBSCRIPTIONITEMINFO) };
  1158. if (SUCCEEDED(m_pRoot->m_pChannelAgent->GetStartItem()->GetSubscriptionItemInfo(&sii)))
  1159. {
  1160. if (sii.ScheduleGroup != GUID_NULL)
  1161. {
  1162. if (FAILED(UpdateScheduleTrigger(&sii.ScheduleGroup, &m_tt)))
  1163. {
  1164. DBG_WARN("Failed to update trigger in publisher's recommended schedule.");
  1165. }
  1166. }
  1167. else
  1168. DBG_WARN("No publisher's recommended schedule in sii");
  1169. }
  1170. }
  1171. return S_OK;
  1172. }
  1173. HRESULT ScheduleToTaskTrigger(TASK_TRIGGER *ptt, SYSTEMTIME *pstStartDate, SYSTEMTIME *pstEndDate,
  1174. long lInterval, long lEarliest, long lLatest, int iZone/*=9999*/)
  1175. {
  1176. // Convert our schedule info to a TASK_TRIGGER struct
  1177. ASSERT(pstStartDate);
  1178. int iZoneCorrectionMinutes=0;
  1179. TIME_ZONE_INFORMATION tzi;
  1180. long lRandom;
  1181. if ((lInterval == 0) ||
  1182. (lInterval > 366 * MINUTES_PER_DAY))
  1183. {
  1184. DBG_WARN("ScheduleToTaskTrigger: Invalid IntervalTime - failing");
  1185. return E_INVALIDARG;
  1186. }
  1187. if (ptt->cbTriggerSize < sizeof(TASK_TRIGGER))
  1188. {
  1189. DBG_WARN("ScheduleToTaskTrigger: ptt->cbTriggerSize not initialized");
  1190. ASSERT(!"ScheduleToTaskTrigger");
  1191. return E_INVALIDARG;
  1192. }
  1193. // Fix any invalid stuff
  1194. if (lInterval < MINUTES_PER_DAY)
  1195. {
  1196. // ROUND so that dwIntervalMinutes is an even divisor of one day
  1197. lInterval = MINUTES_PER_DAY / (MINUTES_PER_DAY / lInterval);
  1198. }
  1199. else
  1200. {
  1201. // ROUND to nearest day
  1202. lInterval = MINUTES_PER_DAY * ((lInterval + 12*60)/MINUTES_PER_DAY);
  1203. }
  1204. if (lEarliest >= lInterval)
  1205. {
  1206. DBG("Invalid EarliestTime specified. Fixing."); // Earliest >= Interval!
  1207. lEarliest = lInterval-1;
  1208. }
  1209. if (lLatest < lEarliest)
  1210. {
  1211. DBG("Invalid LatestTime specified. Fixing."); // Latest < Earliest!
  1212. lLatest = lEarliest;
  1213. }
  1214. if (lLatest-lEarliest > lInterval)
  1215. {
  1216. DBG("Invalid LatestTime specified. Fixing."); // Latest > Interval!
  1217. lLatest = lEarliest+lInterval;
  1218. }
  1219. lRandom = lLatest - lEarliest;
  1220. ASSERT(lRandom>=0 && lRandom<=lInterval);
  1221. if (iZone != 9999)
  1222. {
  1223. int iCorrection;
  1224. iCorrection = (60 * (iZone/100)) + (iZone % 100);
  1225. if (iCorrection < -12*60 || iCorrection > 12*60)
  1226. {
  1227. DBG("ScheduleElementToTaskTrigger: Invalid timezone; ignoring");
  1228. }
  1229. else
  1230. {
  1231. if (TIME_ZONE_ID_INVALID != GetTimeZoneInformation(&tzi))
  1232. {
  1233. // tzi.bias has correction from client timezone to UTC (+8 for US west coast)
  1234. // iCorrection has correction from UTC to server time zone (-5 for US east coast)
  1235. // result is correction from server to client time zone (-3 for east to west coast)
  1236. iZoneCorrectionMinutes = - (iCorrection + tzi.Bias + tzi.StandardBias);
  1237. TraceMsg(TF_THISMODULE, "ServerTimeZone = %d, LocalBias = %d min, RelativeCorrection = %d min", iZone, tzi.Bias+tzi.StandardBias, iZoneCorrectionMinutes);
  1238. }
  1239. else
  1240. {
  1241. DBG_WARN("Unable to get local time zone. Not correcting for time zone.");
  1242. }
  1243. }
  1244. }
  1245. TraceMsg(TF_THISMODULE, "StartDate = %d/%d/%d StopDate = %d/%d/%d", (int)(pstStartDate->wMonth),(int)(pstStartDate->wDay),(int)(pstStartDate->wYear),(int)(pstEndDate->wMonth),(int)(pstEndDate->wDay),(int)(pstEndDate->wYear));
  1246. TraceMsg(TF_THISMODULE, "IntervalTime = %6d minutes", (int)lInterval);
  1247. TraceMsg(TF_THISMODULE, "EarliestTime = %6d minutes", (int)lEarliest);
  1248. TraceMsg(TF_THISMODULE, "LatestTime = %6d minutes", (int)lLatest);
  1249. TraceMsg(TF_THISMODULE, "RandomTime = %6d minutes", (int)lRandom);
  1250. if (iZoneCorrectionMinutes != 0)
  1251. {
  1252. if (lInterval % 60)
  1253. {
  1254. DBG("Not correcting for time zone ; interval not multiple of 1 hour");
  1255. }
  1256. else
  1257. {
  1258. // Correct Earliest time for time zone
  1259. lEarliest += (iZoneCorrectionMinutes % lInterval);
  1260. if (lEarliest < 0)
  1261. lEarliest += lInterval;
  1262. TraceMsg(TF_THISMODULE, "EarliestTime = %6d minutes (after timezone)", (int)lEarliest);
  1263. }
  1264. }
  1265. ZeroMemory(ptt, sizeof(*ptt));
  1266. ptt->cbTriggerSize = sizeof(*ptt);
  1267. ptt->wBeginYear = pstStartDate->wYear;
  1268. ptt->wBeginMonth = pstStartDate->wMonth;
  1269. ptt->wBeginDay = pstStartDate->wDay;
  1270. if (pstEndDate && pstEndDate->wYear)
  1271. {
  1272. ptt->rgFlags |= TASK_TRIGGER_FLAG_HAS_END_DATE;
  1273. ptt->wEndYear = pstEndDate->wYear;
  1274. ptt->wEndMonth = pstEndDate->wMonth;
  1275. ptt->wEndDay = pstEndDate->wDay;
  1276. }
  1277. // Set up Random period ; difference between Latesttime and Earliesttime
  1278. ptt->wRandomMinutesInterval = (WORD) lRandom;
  1279. ptt->wStartHour = (WORD) (lEarliest / 60);
  1280. ptt->wStartMinute = (WORD) (lEarliest % 60);
  1281. // Set up according to IntervalTime
  1282. if (lInterval < MINUTES_PER_DAY)
  1283. {
  1284. // Less than one day (1/2 day, 1/3 day, 1/4 day, etc)
  1285. ptt->MinutesDuration = MINUTES_PER_DAY - lEarliest;
  1286. ptt->MinutesInterval = lInterval;
  1287. ptt->TriggerType = TASK_TIME_TRIGGER_DAILY;
  1288. ptt->Type.Daily.DaysInterval = 1;
  1289. }
  1290. else
  1291. {
  1292. // Greater than or equal to one day.
  1293. DWORD dwIntervalDays = lInterval / MINUTES_PER_DAY;
  1294. TraceMsg(TF_THISMODULE, "Using %d day interval", dwIntervalDays);
  1295. ptt->TriggerType = TASK_TIME_TRIGGER_DAILY;
  1296. ptt->Type.Daily.DaysInterval = (WORD) dwIntervalDays;
  1297. }
  1298. return S_OK;
  1299. }
  1300. //==============================================================================
  1301. // CRunDeliveryAgent provides generic support for synchronous operation of a
  1302. // delivery agent
  1303. // It is aggregatable so that you can add more interfaces to the callback
  1304. //==============================================================================
  1305. CRunDeliveryAgent::CRunDeliveryAgent()
  1306. {
  1307. m_cRef = 1;
  1308. }
  1309. HRESULT CRunDeliveryAgent::Init(CRunDeliveryAgentSink *pParent,
  1310. ISubscriptionItem *pItem,
  1311. REFCLSID rclsidDest)
  1312. {
  1313. ASSERT(pParent && pItem);
  1314. if (m_pParent || m_pItem)
  1315. return E_FAIL; // already initialized. can't reuse an instance.
  1316. if (!pParent || !pItem)
  1317. return E_FAIL;
  1318. m_pParent = pParent;
  1319. m_clsidDest = rclsidDest;
  1320. m_pItem = pItem;
  1321. pItem->AddRef();
  1322. return S_OK;
  1323. }
  1324. CRunDeliveryAgent::~CRunDeliveryAgent()
  1325. {
  1326. CleanUp();
  1327. }
  1328. //
  1329. // IUnknown members
  1330. //
  1331. STDMETHODIMP_(ULONG) CRunDeliveryAgent::AddRef(void)
  1332. {
  1333. return ++m_cRef;
  1334. }
  1335. STDMETHODIMP_(ULONG) CRunDeliveryAgent::Release(void)
  1336. {
  1337. if( 0L != --m_cRef )
  1338. return m_cRef;
  1339. delete this;
  1340. return 0L;
  1341. }
  1342. STDMETHODIMP CRunDeliveryAgent::QueryInterface(REFIID riid, void ** ppv)
  1343. {
  1344. *ppv=NULL;
  1345. // Validate requested interface
  1346. if ((IID_IUnknown == riid) ||
  1347. (IID_ISubscriptionAgentEvents == riid))
  1348. {
  1349. *ppv=(ISubscriptionAgentEvents *)this;
  1350. }
  1351. else
  1352. return E_NOINTERFACE;
  1353. // Addref through the interface
  1354. ((LPUNKNOWN)*ppv)->AddRef();
  1355. return S_OK;
  1356. }
  1357. //
  1358. // ISubscriptionAgentEvents members
  1359. //
  1360. STDMETHODIMP CRunDeliveryAgent::UpdateBegin(const SUBSCRIPTIONCOOKIE *)
  1361. {
  1362. return S_OK;
  1363. }
  1364. STDMETHODIMP CRunDeliveryAgent::UpdateProgress(
  1365. const SUBSCRIPTIONCOOKIE *,
  1366. long lSizeDownloaded,
  1367. long lProgressCurrent,
  1368. long lProgressMax,
  1369. HRESULT hrStatus,
  1370. LPCWSTR wszStatus)
  1371. {
  1372. if (m_pParent)
  1373. m_pParent->OnAgentProgress();
  1374. return S_OK;
  1375. }
  1376. STDMETHODIMP CRunDeliveryAgent::UpdateEnd(const SUBSCRIPTIONCOOKIE *pCookie,
  1377. long lSizeDownloaded,
  1378. HRESULT hrResult,
  1379. LPCWSTR wszResult)
  1380. {
  1381. ASSERT((hrResult != INET_S_AGENT_BASIC_SUCCESS) && (hrResult != E_PENDING));
  1382. m_hrResult = hrResult;
  1383. if (hrResult == INET_S_AGENT_BASIC_SUCCESS || hrResult == E_PENDING)
  1384. {
  1385. // Shouldn't happen; let's be robust anyway.
  1386. m_hrResult = S_OK;
  1387. }
  1388. if (m_pParent)
  1389. {
  1390. m_pParent->OnAgentEnd(pCookie, lSizeDownloaded, hrResult, wszResult, m_fInStartAgent);
  1391. }
  1392. CleanUp();
  1393. return S_OK;
  1394. }
  1395. STDMETHODIMP CRunDeliveryAgent::ReportError(
  1396. const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  1397. HRESULT hrError,
  1398. LPCWSTR wszError)
  1399. {
  1400. return S_FALSE;
  1401. }
  1402. HRESULT CRunDeliveryAgent::StartAgent()
  1403. {
  1404. HRESULT hr;
  1405. if (!m_pParent || !m_pItem || m_pAgent)
  1406. return E_FAIL;
  1407. AddRef(); // Release before we return from this function
  1408. m_fInStartAgent = TRUE;
  1409. m_hrResult = INET_S_AGENT_BASIC_SUCCESS;
  1410. DBG("Using new interfaces to host agent");
  1411. ASSERT(!m_pAgent);
  1412. hr = CoCreateInstance(m_clsidDest, NULL, CLSCTX_INPROC_SERVER,
  1413. IID_ISubscriptionAgentControl, (void **)&m_pAgent);
  1414. if (m_pAgent)
  1415. {
  1416. hr = m_pAgent->StartUpdate(m_pItem, (ISubscriptionAgentEvents *)this);
  1417. }
  1418. hr = m_hrResult;
  1419. m_fInStartAgent = FALSE;
  1420. Release();
  1421. if (hr != INET_S_AGENT_BASIC_SUCCESS)
  1422. {
  1423. return hr;
  1424. }
  1425. return E_PENDING;
  1426. };
  1427. HRESULT CRunDeliveryAgent::AgentPause(DWORD dwFlags)
  1428. {
  1429. if (m_pAgent)
  1430. return m_pAgent->PauseUpdate(0);
  1431. DBG_WARN("CRunDeliveryAgent::AgentPause with no running agent!!");
  1432. return S_FALSE;
  1433. }
  1434. HRESULT CRunDeliveryAgent::AgentResume(DWORD dwFlags)
  1435. {
  1436. if (m_pAgent)
  1437. return m_pAgent->ResumeUpdate(0);
  1438. DBG_WARN("CRunDeliveryAgent::AgentResume with no running agent!!");
  1439. return E_FAIL;
  1440. }
  1441. HRESULT CRunDeliveryAgent::AgentAbort(DWORD dwFlags)
  1442. {
  1443. if (m_pAgent)
  1444. return m_pAgent->AbortUpdate(0);
  1445. DBG_WARN("CRunDeliveryAgent::AgentAbort with no running agent!!");
  1446. return S_FALSE;
  1447. }
  1448. void CRunDeliveryAgent::CleanUp()
  1449. {
  1450. SAFERELEASE(m_pItem);
  1451. SAFERELEASE(m_pAgent);
  1452. m_pParent = NULL;
  1453. }
  1454. // static
  1455. #if 0 // unused by us, but do not remove
  1456. HRESULT CRunDeliveryAgent::CreateNewItem(ISubscriptionItem **ppItem, REFCLSID rclsidAgent)
  1457. {
  1458. ISubscriptionMgrPriv *pSubsMgrPriv=NULL;
  1459. SUBSCRIPTIONITEMINFO info;
  1460. *ppItem = NULL;
  1461. CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER,
  1462. IID_ISubscriptionMgrPriv, (void**)&pSubsMgrPriv);
  1463. if (pSubsMgrPriv)
  1464. {
  1465. SUBSCRIPTIONCOOKIE cookie;
  1466. info.cbSize = sizeof(info);
  1467. info.dwFlags = SI_TEMPORARY;
  1468. info.dwPriority = 0;
  1469. info.ScheduleGroup = GUID_NULL;
  1470. info.clsidAgent = rclsidAgent;
  1471. pSubsMgrPriv->CreateSubscriptionItem(&info, &cookie, ppItem);
  1472. pSubsMgrPriv->Release();
  1473. }
  1474. return (*ppItem) ? S_OK : E_FAIL;
  1475. }
  1476. #endif
  1477. //////////////////////////////////////////////////////////////////////////
  1478. //
  1479. // CChannelAgentHolder, derives from CRunDeliveryAgent
  1480. //
  1481. //////////////////////////////////////////////////////////////////////////
  1482. CChannelAgentHolder::CChannelAgentHolder(CChannelAgent *pChannelAgent, CProcessElement *pProcess)
  1483. {
  1484. m_pChannelAgent = pChannelAgent;
  1485. m_pProcess = pProcess;
  1486. }
  1487. CChannelAgentHolder::~CChannelAgentHolder()
  1488. {
  1489. }
  1490. // Won't compile unless we have addref & release here.
  1491. STDMETHODIMP_(ULONG) CChannelAgentHolder::AddRef(void)
  1492. {
  1493. return CRunDeliveryAgent::AddRef();
  1494. }
  1495. STDMETHODIMP_(ULONG) CChannelAgentHolder::Release(void)
  1496. {
  1497. return CRunDeliveryAgent::Release();
  1498. }
  1499. STDMETHODIMP CChannelAgentHolder::QueryInterface(REFIID riid, void ** ppv)
  1500. {
  1501. *ppv=NULL;
  1502. if (IID_IServiceProvider == riid)
  1503. {
  1504. *ppv = (IServiceProvider *)this;
  1505. }
  1506. else
  1507. return CRunDeliveryAgent::QueryInterface(riid, ppv);
  1508. // Addref through the interface
  1509. ((LPUNKNOWN)*ppv)->AddRef();
  1510. return S_OK;
  1511. }
  1512. // IQueryService
  1513. // CLSID_ChannelAgent IID_ISubscriptionItem channel agent start item
  1514. // CLSID_XMLDocument IID_IXMLElement current element
  1515. STDMETHODIMP CChannelAgentHolder::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
  1516. {
  1517. ASSERT(ppvObject);
  1518. if (!ppvObject)
  1519. return E_INVALIDARG;
  1520. if (!m_pChannelAgent || !m_pProcess || !m_pParent)
  1521. return E_FAIL;
  1522. *ppvObject = NULL;
  1523. if (guidService == CLSID_ChannelAgent)
  1524. {
  1525. if (riid == IID_ISubscriptionItem)
  1526. {
  1527. *ppvObject = m_pChannelAgent->GetStartItem();
  1528. }
  1529. // if (riid == IID_IXMLElement) Root XML document?
  1530. }
  1531. else if (guidService == CLSID_XMLDocument)
  1532. {
  1533. if (riid == IID_IXMLElement)
  1534. {
  1535. *ppvObject = m_pProcess->GetCurrentElement();
  1536. }
  1537. }
  1538. if (*ppvObject)
  1539. {
  1540. ((IUnknown *)*ppvObject)->AddRef();
  1541. return S_OK;
  1542. }
  1543. return E_FAIL;
  1544. }
  1545. //////////////////////////////////////////////////////////////////////////
  1546. //
  1547. // CChannelAgent implementation
  1548. //
  1549. //////////////////////////////////////////////////////////////////////////
  1550. CChannelAgent::CChannelAgent()
  1551. {
  1552. DBG("Creating CChannelAgent object");
  1553. // Initialize object
  1554. // Many vars are initialized in StartOperation
  1555. m_pwszURL = NULL;
  1556. m_pCurDownload = NULL;
  1557. m_pProcess = NULL;
  1558. m_fHasInitCookie = FALSE;
  1559. m_pChannelIconHelper = NULL;
  1560. }
  1561. CChannelAgent::~CChannelAgent()
  1562. {
  1563. // DBG("Destroying CChannelAgent object");
  1564. if (m_pwszURL)
  1565. CoTaskMemFree(m_pwszURL);
  1566. SAFELOCALFREE (m_pBuf);
  1567. ASSERT(!m_pProcess);
  1568. SAFERELEASE(m_pChannelIconHelper);
  1569. SAFELOCALFREE(m_pwszScreenSaverURL);
  1570. DBG("Destroyed CChannelAgent object");
  1571. }
  1572. void CChannelAgent::CleanUp()
  1573. {
  1574. if (m_pCurDownload)
  1575. {
  1576. m_pCurDownload->LeaveMeAlone(); // no more calls from them
  1577. m_pCurDownload->DoneDownloading();
  1578. m_pCurDownload->Release();
  1579. m_pCurDownload = NULL;
  1580. }
  1581. SAFEFREEOLESTR(m_pwszURL);
  1582. SAFEDELETE(m_pProcess);
  1583. SAFELOCALFREE(m_pBuf);
  1584. CDeliveryAgent::CleanUp();
  1585. }
  1586. void CChannelAgent::SetScreenSaverURL(LPCWSTR pwszURL)
  1587. {
  1588. // We only take the first one
  1589. if (NULL == m_pwszScreenSaverURL)
  1590. {
  1591. m_pwszScreenSaverURL = StrDupW(pwszURL);
  1592. }
  1593. }
  1594. HRESULT CChannelAgent::StartOperation()
  1595. {
  1596. DBG("Channel Agent in StartOperation");
  1597. DWORD dwTemp;
  1598. SAFEFREEOLESTR(m_pwszURL);
  1599. if (FAILED(ReadOLESTR(m_pSubscriptionItem, c_szPropURL, &m_pwszURL)) ||
  1600. !CUrlDownload::IsValidURL(m_pwszURL))
  1601. {
  1602. DBG_WARN("Couldn't get valid URL, aborting");
  1603. SetEndStatus(E_INVALIDARG);
  1604. SendUpdateNone();
  1605. return E_INVALIDARG;
  1606. }
  1607. if (FAILED(ReadDWORD(m_pSubscriptionItem, c_szPropChannelFlags, &m_dwChannelFlags)))
  1608. m_dwChannelFlags = 0;
  1609. // If we download all, we also download some. Makes assumptions easier.
  1610. if (m_dwChannelFlags & CHANNEL_AGENT_PRECACHE_ALL)
  1611. m_dwChannelFlags |= CHANNEL_AGENT_PRECACHE_SOME;
  1612. // NOTE: We may want REST_NoChannelContent to be similar to the webcrawl version.
  1613. // Probably not though because the headlines are useful in the UI.
  1614. if (SHRestricted2W(REST_NoChannelContent, NULL, 0))
  1615. ClearFlag(m_dwChannelFlags, CHANNEL_AGENT_PRECACHE_ALL | CHANNEL_AGENT_PRECACHE_SOME);
  1616. m_dwMaxSizeKB = SHRestricted2W(REST_MaxChannelSize, NULL, 0);
  1617. if (SUCCEEDED(ReadDWORD(m_pSubscriptionItem, c_szPropCrawlMaxSize, &dwTemp))
  1618. && dwTemp
  1619. && (0 == m_dwMaxSizeKB || dwTemp < m_dwMaxSizeKB))
  1620. {
  1621. m_dwMaxSizeKB = dwTemp;
  1622. }
  1623. if (IsAgentFlagSet(FLAG_CHANGESONLY))
  1624. {
  1625. ClearFlag(m_dwChannelFlags, CHANNEL_AGENT_PRECACHE_ALL|
  1626. CHANNEL_AGENT_PRECACHE_SOME|CHANNEL_AGENT_PRECACHE_SCRNSAVER);
  1627. DBG("Channel agent is in 'changes only' mode.");
  1628. }
  1629. else
  1630. {
  1631. // Read old group ID
  1632. ReadLONGLONG(m_pSubscriptionItem, c_szPropCrawlGroupID, &m_llOldCacheGroupID);
  1633. // Read new ID if present
  1634. m_llCacheGroupID = 0;
  1635. ReadLONGLONG(m_pSubscriptionItem, c_szPropCrawlNewGroupID, &m_llCacheGroupID);
  1636. }
  1637. return CDeliveryAgent::StartOperation();
  1638. }
  1639. HRESULT CChannelAgent::StartDownload()
  1640. {
  1641. ASSERT(!m_pCurDownload);
  1642. TraceMsg(TF_THISMODULE, "Channel agent starting download of CDF: URL=%ws", m_pwszURL);
  1643. m_pCurDownload = new CUrlDownload(this, 0);
  1644. if (!m_pCurDownload)
  1645. return E_OUTOFMEMORY;
  1646. // Change detection
  1647. m_varChange.vt = VT_EMPTY;
  1648. if (IsAgentFlagSet(FLAG_CHANGESONLY))
  1649. {
  1650. // "Changes Only" mode, we have persisted a change detection code
  1651. ReadVariant(m_pSubscriptionItem, c_szPropChangeCode, &m_varChange);
  1652. m_llCacheGroupID = 0;
  1653. }
  1654. else
  1655. {
  1656. // Create new cache group
  1657. if (!m_llCacheGroupID)
  1658. {
  1659. m_llCacheGroupID = CreateUrlCacheGroup(CACHEGROUP_FLAG_NONPURGEABLE, 0);
  1660. ASSERT_MSG(m_llCacheGroupID != 0, "Create cache group failed");
  1661. }
  1662. }
  1663. TCHAR szUrl[INTERNET_MAX_URL_LENGTH];
  1664. MyOleStrToStrN(szUrl, INTERNET_MAX_URL_LENGTH, m_pwszURL);
  1665. PreCheckUrlForChange(szUrl, &m_varChange, NULL);
  1666. SendUpdateProgress(m_pwszURL, 0, -1, 0);
  1667. // Start download
  1668. return m_pCurDownload->BeginDownloadURL2(
  1669. m_pwszURL, BDU2_URLMON, BDU2_NEEDSTREAM, NULL, m_dwMaxSizeKB<<10);
  1670. }
  1671. HRESULT CChannelAgent::OnAuthenticate(HWND *phwnd, LPWSTR *ppszUsername, LPWSTR *ppszPassword)
  1672. {
  1673. HRESULT hr;
  1674. ASSERT(phwnd && ppszUsername && ppszPassword);
  1675. ASSERT((HWND)-1 == *phwnd && NULL == *ppszUsername && NULL == *ppszPassword);
  1676. hr = ReadOLESTR(m_pSubscriptionItem, c_szPropCrawlUsername, ppszUsername);
  1677. if (SUCCEEDED(hr))
  1678. {
  1679. BSTR bstrPassword = NULL;
  1680. hr = ReadPassword(m_pSubscriptionItem, &bstrPassword);
  1681. if (SUCCEEDED(hr))
  1682. {
  1683. int len = (lstrlenW(bstrPassword) + 1) * sizeof(WCHAR);
  1684. *ppszPassword = (LPWSTR) CoTaskMemAlloc(len);
  1685. if (*ppszPassword)
  1686. {
  1687. CopyMemory(*ppszPassword, bstrPassword, len);
  1688. }
  1689. SAFEFREEBSTR(bstrPassword);
  1690. if (*ppszPassword)
  1691. {
  1692. return S_OK;
  1693. }
  1694. }
  1695. }
  1696. SAFEFREEOLESTR(*ppszUsername);
  1697. SAFEFREEOLESTR(*ppszPassword);
  1698. return E_FAIL;
  1699. }
  1700. HRESULT CChannelAgent::OnDownloadComplete(UINT iID, int iError)
  1701. {
  1702. TraceMsg(TF_THISMODULE, "Channel Agent: OnDownloadComplete(%d)", iError);
  1703. IStream *pStm = NULL;
  1704. HRESULT hr;
  1705. BOOL fProcessed=FALSE;
  1706. DWORD dwCDFSizeKB=0, dwResponseCode;
  1707. BSTR bstrTmp;
  1708. char chBuf[MY_MAX_CACHE_ENTRY_INFO];
  1709. DWORD dwBufSize = sizeof(chBuf);
  1710. LPINTERNET_CACHE_ENTRY_INFO lpInfo = (LPINTERNET_CACHE_ENTRY_INFO) chBuf;
  1711. if (iError)
  1712. hr = E_FAIL;
  1713. else
  1714. {
  1715. hr = m_pCurDownload->GetResponseCode(&dwResponseCode);
  1716. if (SUCCEEDED(hr))
  1717. {
  1718. hr = CheckResponseCode(dwResponseCode);
  1719. }
  1720. else
  1721. DBG_WARN("CChannelAgent failed to GetResponseCode");
  1722. }
  1723. if (SUCCEEDED(hr))
  1724. {
  1725. hr = m_pCurDownload->GetStream(&pStm);
  1726. m_pCurDownload->ReleaseStream();
  1727. }
  1728. if (SUCCEEDED(hr))
  1729. {
  1730. TCHAR szThisUrl[INTERNET_MAX_URL_LENGTH];
  1731. LPWSTR pwszThisUrl;
  1732. m_pCurDownload->GetRealURL(&pwszThisUrl);
  1733. if (pwszThisUrl)
  1734. {
  1735. MyOleStrToStrN(szThisUrl, INTERNET_MAX_URL_LENGTH, pwszThisUrl);
  1736. LocalFree(pwszThisUrl);
  1737. if (SUCCEEDED(GetUrlInfoAndMakeSticky(
  1738. NULL,
  1739. szThisUrl,
  1740. lpInfo,
  1741. dwBufSize,
  1742. m_llCacheGroupID)))
  1743. {
  1744. dwCDFSizeKB = (((LPINTERNET_CACHE_ENTRY_INFO)chBuf)->dwSizeLow+512) >> 10;
  1745. TraceMsg(TF_THISMODULE, "CDF size %d kb", dwCDFSizeKB);
  1746. hr = PostCheckUrlForChange(&m_varChange, lpInfo, lpInfo->LastModifiedTime);
  1747. // If we FAILED, we mark it as changed.
  1748. if (hr == S_OK || FAILED(hr))
  1749. {
  1750. SetAgentFlag(FLAG_CDFCHANGED);
  1751. DBG("CDF has changed; will flag channel as changed");
  1752. }
  1753. // "Changes Only" mode, persist change detection code
  1754. if (IsAgentFlagSet(FLAG_CHANGESONLY))
  1755. {
  1756. WriteVariant(m_pSubscriptionItem, c_szPropChangeCode, &m_varChange);
  1757. }
  1758. hr = S_OK;
  1759. }
  1760. }
  1761. }
  1762. else
  1763. {
  1764. SetEndStatus(E_INVALIDARG);
  1765. }
  1766. // Get an object model on our Channel Description File
  1767. if (SUCCEEDED(hr) && pStm)
  1768. {
  1769. IPersistStreamInit *pPersistStm=NULL;
  1770. CoCreateInstance(CLSID_XMLDocument, NULL, CLSCTX_INPROC,
  1771. IID_IPersistStreamInit, (void **)&pPersistStm);
  1772. if (pPersistStm)
  1773. {
  1774. pPersistStm->InitNew();
  1775. hr = pPersistStm->Load(pStm);
  1776. if (SUCCEEDED(hr))
  1777. {
  1778. IXMLDocument *pDoc;
  1779. hr = pPersistStm->QueryInterface(IID_IXMLDocument, (void **)&pDoc);
  1780. if (SUCCEEDED(hr) && pDoc)
  1781. {
  1782. IXMLElement *pRoot;
  1783. BSTR bstrCharSet=NULL;
  1784. if (SUCCEEDED(pDoc->get_charset(&bstrCharSet)) && bstrCharSet)
  1785. {
  1786. WriteOLESTR(m_pSubscriptionItem, c_szPropCharSet, bstrCharSet);
  1787. TraceMsg(TF_THISMODULE, "Charset = \"%ws\"", bstrCharSet);
  1788. SysFreeString(bstrCharSet);
  1789. }
  1790. else
  1791. WriteEMPTY(m_pSubscriptionItem, c_szPropCharSet);
  1792. hr = pDoc->get_root(&pRoot);
  1793. if (SUCCEEDED(hr) && pRoot)
  1794. {
  1795. if (SUCCEEDED(pRoot->get_tagName(&bstrTmp)) && bstrTmp)
  1796. {
  1797. if (!StrCmpIW(bstrTmp, L"Channel"))
  1798. {
  1799. ASSERT(!m_pProcess);
  1800. m_pProcess = new CProcessRoot(this, pRoot);
  1801. if (m_pProcess)
  1802. {
  1803. if (IsAgentFlagSet(FLAG_CDFCHANGED))
  1804. SetEndStatus(S_OK);
  1805. else
  1806. SetEndStatus(S_FALSE);
  1807. m_pProcess->m_dwCurSizeKB = dwCDFSizeKB;
  1808. WriteEMPTY(m_pSubscriptionItem, c_szPropEmailURL);
  1809. hr = m_pProcess->Run(); // This will get us cleaned up (now or later)
  1810. fProcessed = TRUE; // So we shouldn't do it ourselves
  1811. }
  1812. }
  1813. else
  1814. DBG_WARN("Valid XML but invalid CDF");
  1815. SAFEFREEBSTR(bstrTmp);
  1816. }
  1817. pRoot->Release();
  1818. }
  1819. pDoc->Release();
  1820. }
  1821. }
  1822. pPersistStm->Release();
  1823. }
  1824. }
  1825. if (!fProcessed || (FAILED(hr) && (hr != E_PENDING)))
  1826. {
  1827. if (INET_S_AGENT_BASIC_SUCCESS == GetEndStatus())
  1828. SetEndStatus(E_FAIL);
  1829. DBG_WARN("Failed to process CDF ; XML load failed?");
  1830. CleanUp(); // CleanUp only if the process failed (otherwise OnChildDone does it)
  1831. }
  1832. #ifdef DEBUG
  1833. if (hr == E_PENDING)
  1834. DBG("CChannelAgent::OnDownloadComplete not cleaning up, webcrawl pending");
  1835. #endif
  1836. return S_OK;
  1837. }
  1838. HRESULT CChannelAgent::OnChildDone(CProcessElement *pChild, HRESULT hr)
  1839. {
  1840. // Our CProcessRoot has reported that it's done. Clean up.
  1841. DBG("CChannelAgent::OnChildDone cleaning up Channel delivery agent");
  1842. if (m_llOldCacheGroupID)
  1843. {
  1844. DBG("Nuking old cache group.");
  1845. if (!DeleteUrlCacheGroup(m_llOldCacheGroupID, 0, 0))
  1846. {
  1847. DBG_WARN("Failed to delete old cache group!");
  1848. }
  1849. }
  1850. if (SUCCEEDED(GetEndStatus()))
  1851. {
  1852. IChannelMgrPriv2 *pChannelMgrPriv2;
  1853. HRESULT hrTmp = CoCreateInstance(CLSID_ChannelMgr,
  1854. NULL,
  1855. CLSCTX_INPROC_SERVER,
  1856. IID_IChannelMgrPriv2, (void**)&pChannelMgrPriv2);
  1857. if (SUCCEEDED(hrTmp))
  1858. {
  1859. ASSERT(NULL != pChannelMgrPriv2);
  1860. hrTmp = pChannelMgrPriv2->WriteScreenSaverURL(m_pwszURL, m_pwszScreenSaverURL);
  1861. pChannelMgrPriv2->Release();
  1862. }
  1863. }
  1864. WriteLONGLONG(m_pSubscriptionItem, c_szPropCrawlGroupID, m_llCacheGroupID);
  1865. // Add "total size" property
  1866. m_lSizeDownloadedKB = (long) (m_pProcess->m_dwCurSizeKB);
  1867. WriteDWORD(m_pSubscriptionItem, c_szPropCrawlActualSize, m_lSizeDownloadedKB);
  1868. WriteDWORD(m_pSubscriptionItem, c_szPropActualProgressMax, m_pProcess->m_iTotalStarted);
  1869. CleanUp();
  1870. return S_OK;
  1871. }
  1872. HRESULT CChannelAgent::AgentPause(DWORD dwFlags)
  1873. {
  1874. DBG("CChannelAgent::AgentPause");
  1875. if (m_pProcess)
  1876. m_pProcess->Pause(dwFlags);
  1877. return CDeliveryAgent::AgentPause(dwFlags);
  1878. }
  1879. HRESULT CChannelAgent::AgentResume(DWORD dwFlags)
  1880. {
  1881. DBG("CChannelAgent::AgentResume");
  1882. if (m_pProcess)
  1883. m_pProcess->Resume(dwFlags);
  1884. return CDeliveryAgent::AgentResume(dwFlags);
  1885. }
  1886. // Forcibly abort current operation
  1887. HRESULT CChannelAgent::AgentAbort(DWORD dwFlags)
  1888. {
  1889. DBG("CChannelAgent::AgentAbort");
  1890. if (m_pCurDownload)
  1891. m_pCurDownload->DoneDownloading();
  1892. if (m_pProcess)
  1893. m_pProcess->Abort(dwFlags);
  1894. return CDeliveryAgent::AgentAbort(dwFlags);
  1895. }
  1896. HRESULT CChannelAgent::ModifyUpdateEnd(ISubscriptionItem *pEndItem, UINT *puiRes)
  1897. {
  1898. // Customize our end status string
  1899. switch (GetEndStatus())
  1900. {
  1901. case INET_E_AGENT_MAX_SIZE_EXCEEDED :
  1902. *puiRes = IDS_AGNT_STATUS_SIZELIMIT; break;
  1903. case INET_E_AGENT_CACHE_SIZE_EXCEEDED :
  1904. *puiRes = IDS_AGNT_STATUS_CACHELIMIT; break;
  1905. case E_FAIL : *puiRes = IDS_CRAWL_STATUS_NOT_OK; break;
  1906. case S_OK :
  1907. if (!IsAgentFlagSet(FLAG_CHANGESONLY))
  1908. *puiRes = IDS_CRAWL_STATUS_OK;
  1909. else
  1910. *puiRes = IDS_URL_STATUS_OK;
  1911. break;
  1912. case S_FALSE :
  1913. if (!IsAgentFlagSet(FLAG_CHANGESONLY))
  1914. *puiRes = IDS_CRAWL_STATUS_UNCHANGED;
  1915. else
  1916. *puiRes = IDS_URL_STATUS_UNCHANGED;
  1917. break;
  1918. case INET_S_AGENT_PART_FAIL : *puiRes = IDS_CRAWL_STATUS_MOSTLYOK; break;
  1919. }
  1920. return CDeliveryAgent::ModifyUpdateEnd(pEndItem, puiRes);
  1921. }
  1922. const GUID CLSID_CDFICONHANDLER =
  1923. {0xf3ba0dc0, 0x9cc8, 0x11d0, {0xa5, 0x99, 0x0, 0xc0, 0x4f, 0xd6, 0x44, 0x35}};
  1924. extern HRESULT LoadWithCookie(LPCTSTR, POOEBuf, DWORD *, SUBSCRIPTIONCOOKIE *);
  1925. // IExtractIcon members
  1926. STDMETHODIMP CChannelAgent::GetIconLocation(UINT uFlags, LPTSTR szIconFile, UINT cchMax, int * piIndex, UINT * pwFlags)
  1927. {
  1928. DWORD dwSize;
  1929. IChannelMgrPriv* pIChannelMgrPriv = NULL;
  1930. HRESULT hr = E_FAIL;
  1931. TCHAR szPath[MAX_PATH];
  1932. if (!m_pBuf) {
  1933. m_pBuf = (POOEBuf)MemAlloc(LPTR, sizeof(OOEBuf));
  1934. if (!m_pBuf)
  1935. return E_OUTOFMEMORY;
  1936. HRESULT hr = LoadWithCookie(NULL, m_pBuf, &dwSize, &m_SubscriptionCookie);
  1937. RETURN_ON_FAILURE(hr);
  1938. }
  1939. hr = GetChannelPath(m_pBuf->m_URL, szPath, ARRAYSIZE(szPath), &pIChannelMgrPriv);
  1940. if (SUCCEEDED(hr) && pIChannelMgrPriv)
  1941. {
  1942. IPersistFile* ppf = NULL;
  1943. BOOL bCoinit = FALSE;
  1944. HRESULT hr2 = E_FAIL;
  1945. pIChannelMgrPriv->Release();
  1946. hr = CoCreateInstance(CLSID_CDFICONHANDLER, NULL, CLSCTX_INPROC_SERVER,
  1947. IID_IPersistFile, (void**)&ppf);
  1948. if ((hr == CO_E_NOTINITIALIZED || hr == REGDB_E_IIDNOTREG) &&
  1949. SUCCEEDED(CoInitialize(NULL)))
  1950. {
  1951. bCoinit = TRUE;
  1952. hr = CoCreateInstance(CLSID_CDFICONHANDLER, NULL, CLSCTX_INPROC_SERVER,
  1953. IID_IPersistFile, (void**)&ppf);
  1954. }
  1955. if (SUCCEEDED(hr))
  1956. {
  1957. hr = ppf->QueryInterface(IID_IExtractIcon, (void**)&m_pChannelIconHelper);
  1958. WCHAR wszPath[MAX_PATH];
  1959. MyStrToOleStrN(wszPath, ARRAYSIZE(wszPath), szPath);
  1960. hr2 = ppf->Load(wszPath, 0);
  1961. ppf->Release();
  1962. }
  1963. if (SUCCEEDED(hr) && m_pChannelIconHelper)
  1964. {
  1965. hr = m_pChannelIconHelper->GetIconLocation(uFlags, szIconFile, cchMax, piIndex, pwFlags);
  1966. }
  1967. if (bCoinit)
  1968. CoUninitialize();
  1969. }
  1970. if (m_pChannelIconHelper == NULL)
  1971. {
  1972. WCHAR wszCookie[GUIDSTR_MAX];
  1973. ASSERT (piIndex && pwFlags && szIconFile);
  1974. StringFromGUID2(m_SubscriptionCookie, wszCookie, ARRAYSIZE(wszCookie));
  1975. MyOleStrToStrN(szIconFile, cchMax, wszCookie);
  1976. *piIndex = 0;
  1977. *pwFlags |= GIL_NOTFILENAME | GIL_PERINSTANCE;
  1978. hr = NOERROR;
  1979. }
  1980. return hr;
  1981. }
  1982. STDMETHODIMP CChannelAgent::Extract(LPCTSTR szIconFile, UINT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIconSize)
  1983. {
  1984. static HICON channelIcon = NULL;
  1985. if (!phiconLarge || !phiconSmall)
  1986. return E_INVALIDARG;
  1987. * phiconLarge = * phiconSmall = NULL;
  1988. if (m_pChannelIconHelper)
  1989. {
  1990. return m_pChannelIconHelper->Extract(szIconFile, nIconIndex, phiconLarge, phiconSmall, nIconSize);
  1991. }
  1992. else
  1993. {
  1994. DWORD dwSize;
  1995. if (!m_pBuf) {
  1996. m_pBuf = (POOEBuf)MemAlloc(LPTR, sizeof(OOEBuf));
  1997. if (!m_pBuf)
  1998. return E_OUTOFMEMORY;
  1999. HRESULT hr = LoadWithCookie(NULL, m_pBuf, &dwSize, &m_SubscriptionCookie);
  2000. RETURN_ON_FAILURE(hr);
  2001. }
  2002. BYTE bBuf[MY_MAX_CACHE_ENTRY_INFO];
  2003. LPINTERNET_CACHE_ENTRY_INFO pEntry = (INTERNET_CACHE_ENTRY_INFO *)bBuf;
  2004. dwSize = sizeof(bBuf);
  2005. if (GetUrlCacheEntryInfo(m_pBuf->m_URL, pEntry, &dwSize)) {
  2006. SHFILEINFO sfi;
  2007. UINT cbFileInfo = sizeof(sfi), uFlags = SHGFI_ICON | SHGFI_LARGEICON;
  2008. if (NULL != SHGetFileInfo(pEntry->lpszLocalFileName, 0,
  2009. &sfi, cbFileInfo, uFlags))
  2010. {
  2011. ASSERT(sfi.hIcon);
  2012. *phiconLarge = *phiconSmall = sfi.hIcon;
  2013. return NOERROR;
  2014. }
  2015. }
  2016. if (channelIcon == NULL) {
  2017. channelIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_CHANNEL));
  2018. ASSERT(channelIcon);
  2019. }
  2020. * phiconLarge = * phiconSmall = channelIcon;
  2021. return NOERROR;
  2022. }
  2023. }