Leaked source code of windows server 2003
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.

2403 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_fDesktop &&
  999. !StrCmpIW(bstrValue, L"DesktopComponent"))
  1000. {
  1001. m_fDesktop = TRUE;
  1002. }
  1003. if (!m_fEmail &&
  1004. !StrCmpIW(bstrValue, L"Email"))
  1005. {
  1006. m_fEmail = TRUE;
  1007. }
  1008. SysFreeString(bstrValue);
  1009. }
  1010. }
  1011. else if (!StrCmpIW(pwszTagName, L"A"))
  1012. {
  1013. // Anchor tag found; save URL
  1014. if (!m_bstrAnchorURL)
  1015. GetXMLStringAttribute(pItem, L"HREF", &m_bstrAnchorURL);
  1016. }
  1017. return S_OK;
  1018. }
  1019. HRESULT CProcessItem::EnumerationComplete()
  1020. {
  1021. BOOL fPreCache, fPreCacheValid=FALSE;
  1022. BOOL fDoDownload=FALSE;
  1023. BSTR bstrURL=NULL;
  1024. HRESULT hr = S_OK;
  1025. LPWSTR pwszUrl=NULL;
  1026. // End PCN compat
  1027. if (SUCCEEDED(GetXMLBoolAttribute(m_pElement, L"PreCache", &fPreCache)))
  1028. {
  1029. fPreCacheValid = TRUE;
  1030. }
  1031. // Get the URL from our attribute, or from Anchor tag if not available
  1032. if (FAILED(GetXMLStringAttribute(m_pElement, L"HREF", &bstrURL)) || !bstrURL)
  1033. {
  1034. bstrURL = m_bstrAnchorURL;
  1035. m_bstrAnchorURL = NULL;
  1036. }
  1037. // Get the combined URL
  1038. if (bstrURL)
  1039. CombineWithBaseUrl(bstrURL, &pwszUrl);
  1040. if (pwszUrl)
  1041. {
  1042. // Process tracking for this item
  1043. m_pRoot->DoTrackingFromItem(m_pElement, pwszUrl, IsGlobalLog());
  1044. // Find if we should use this url for the Email agent
  1045. if (m_fEmail)
  1046. {
  1047. // Yes, put this url in the end report
  1048. DBG("Using custom email url");
  1049. WriteOLESTR(m_pRoot->m_pChannelAgent->GetStartItem(), c_szPropEmailURL, pwszUrl);
  1050. }
  1051. // Figure out if we should download our "href" based on Usage and Precache tag
  1052. if (fPreCacheValid)
  1053. {
  1054. if (fPreCache)
  1055. {
  1056. if (m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_SOME))
  1057. fDoDownload = TRUE;
  1058. }
  1059. }
  1060. else
  1061. {
  1062. if (m_pRoot->IsChannelFlagSet(CHANNEL_AGENT_PRECACHE_ALL))
  1063. fDoDownload = TRUE;
  1064. }
  1065. // if (m_fDesktop)
  1066. // Do something for desktop components
  1067. if (fDoDownload && pwszUrl)
  1068. hr = DoWebCrawl(m_pElement, pwszUrl);
  1069. } // pwszUrl
  1070. SAFEFREEBSTR(bstrURL);
  1071. SAFELOCALFREE(pwszUrl);
  1072. return hr;
  1073. }
  1074. CProcessSchedule::CProcessSchedule(CProcessElementSink *pParent,
  1075. CProcessRoot *pRoot,
  1076. IXMLElement *pItem) :
  1077. CProcessElement(pParent, pRoot, pItem)
  1078. {
  1079. }
  1080. HRESULT CProcessSchedule::Run()
  1081. {
  1082. // Get attributes (Start and End date) first
  1083. BSTR bstr=NULL;
  1084. long lTimeZone;
  1085. if (FAILED(GetXMLStringAttribute(m_pElement, L"StartDate", &bstr)) ||
  1086. !ISODateToSystemTime(bstr, &m_stStartDate, &lTimeZone))
  1087. {
  1088. GetLocalTime(&m_stStartDate);
  1089. }
  1090. SAFEFREEBSTR(bstr);
  1091. if (FAILED(GetXMLStringAttribute(m_pElement, L"StopDate", &bstr)) ||
  1092. !ISODateToSystemTime(bstr, &m_stEndDate, &lTimeZone))
  1093. {
  1094. ZeroMemory(&m_stEndDate, sizeof(m_stEndDate));
  1095. }
  1096. SAFEFREEBSTR(bstr);
  1097. return CProcessElement::Run();
  1098. }
  1099. HRESULT CProcessSchedule::ProcessItemInEnum(LPCWSTR pwszTagName, IXMLElement *pItem)
  1100. {
  1101. if (!StrCmpIW(pwszTagName, L"IntervalTime"))
  1102. {
  1103. GetXMLTimeAttributes(pItem, &m_timeInterval);
  1104. }
  1105. else if (!StrCmpIW(pwszTagName, L"EarliestTime"))
  1106. {
  1107. GetXMLTimeAttributes(pItem, &m_timeEarliest);
  1108. }
  1109. else if (!StrCmpIW(pwszTagName, L"LatestTime"))
  1110. {
  1111. GetXMLTimeAttributes(pItem, &m_timeLatest);
  1112. }
  1113. return S_OK;
  1114. }
  1115. HRESULT CProcessSchedule::EnumerationComplete()
  1116. {
  1117. DBG("CProcessSchedule::EnumerationComplete");
  1118. int iZone;
  1119. if (FAILED(GetXMLTimeZoneAttribute(m_pElement, L"TimeZone", &iZone)))
  1120. iZone = 9999;
  1121. m_tt.cbTriggerSize = sizeof(m_tt);
  1122. // m_pRoot is null for XMLElementToTaskTrigger call
  1123. // Always run ScheduleToTaskTrigger
  1124. if (SUCCEEDED(ScheduleToTaskTrigger(&m_tt, &m_stStartDate, &m_stEndDate,
  1125. (long) m_timeInterval.dwConvertedMinutes,
  1126. (long) m_timeEarliest.dwConvertedMinutes,
  1127. (long) m_timeLatest.dwConvertedMinutes,
  1128. iZone))
  1129. && m_pRoot)
  1130. {
  1131. SUBSCRIPTIONITEMINFO sii = { sizeof(SUBSCRIPTIONITEMINFO) };
  1132. if (SUCCEEDED(m_pRoot->m_pChannelAgent->GetStartItem()->GetSubscriptionItemInfo(&sii)))
  1133. {
  1134. if (sii.ScheduleGroup != GUID_NULL)
  1135. {
  1136. if (FAILED(UpdateScheduleTrigger(&sii.ScheduleGroup, &m_tt)))
  1137. {
  1138. DBG_WARN("Failed to update trigger in publisher's recommended schedule.");
  1139. }
  1140. }
  1141. else
  1142. DBG_WARN("No publisher's recommended schedule in sii");
  1143. }
  1144. }
  1145. return S_OK;
  1146. }
  1147. HRESULT ScheduleToTaskTrigger(TASK_TRIGGER *ptt, SYSTEMTIME *pstStartDate, SYSTEMTIME *pstEndDate,
  1148. long lInterval, long lEarliest, long lLatest, int iZone/*=9999*/)
  1149. {
  1150. // Convert our schedule info to a TASK_TRIGGER struct
  1151. ASSERT(pstStartDate);
  1152. int iZoneCorrectionMinutes=0;
  1153. TIME_ZONE_INFORMATION tzi;
  1154. long lRandom;
  1155. if ((lInterval == 0) ||
  1156. (lInterval > 366 * MINUTES_PER_DAY))
  1157. {
  1158. DBG_WARN("ScheduleToTaskTrigger: Invalid IntervalTime - failing");
  1159. return E_INVALIDARG;
  1160. }
  1161. if (ptt->cbTriggerSize < sizeof(TASK_TRIGGER))
  1162. {
  1163. DBG_WARN("ScheduleToTaskTrigger: ptt->cbTriggerSize not initialized");
  1164. ASSERT(!"ScheduleToTaskTrigger");
  1165. return E_INVALIDARG;
  1166. }
  1167. // Fix any invalid stuff
  1168. if (lInterval < MINUTES_PER_DAY)
  1169. {
  1170. // ROUND so that dwIntervalMinutes is an even divisor of one day
  1171. lInterval = MINUTES_PER_DAY / (MINUTES_PER_DAY / lInterval);
  1172. }
  1173. else
  1174. {
  1175. // ROUND to nearest day
  1176. lInterval = MINUTES_PER_DAY * ((lInterval + 12*60)/MINUTES_PER_DAY);
  1177. }
  1178. if (lEarliest >= lInterval)
  1179. {
  1180. DBG("Invalid EarliestTime specified. Fixing."); // Earliest >= Interval!
  1181. lEarliest = lInterval-1;
  1182. }
  1183. if (lLatest < lEarliest)
  1184. {
  1185. DBG("Invalid LatestTime specified. Fixing."); // Latest < Earliest!
  1186. lLatest = lEarliest;
  1187. }
  1188. if (lLatest-lEarliest > lInterval)
  1189. {
  1190. DBG("Invalid LatestTime specified. Fixing."); // Latest > Interval!
  1191. lLatest = lEarliest+lInterval;
  1192. }
  1193. lRandom = lLatest - lEarliest;
  1194. ASSERT(lRandom>=0 && lRandom<=lInterval);
  1195. if (iZone != 9999)
  1196. {
  1197. int iCorrection;
  1198. iCorrection = (60 * (iZone/100)) + (iZone % 100);
  1199. if (iCorrection < -12*60 || iCorrection > 12*60)
  1200. {
  1201. DBG("ScheduleElementToTaskTrigger: Invalid timezone; ignoring");
  1202. }
  1203. else
  1204. {
  1205. if (TIME_ZONE_ID_INVALID != GetTimeZoneInformation(&tzi))
  1206. {
  1207. // tzi.bias has correction from client timezone to UTC (+8 for US west coast)
  1208. // iCorrection has correction from UTC to server time zone (-5 for US east coast)
  1209. // result is correction from server to client time zone (-3 for east to west coast)
  1210. iZoneCorrectionMinutes = - (iCorrection + tzi.Bias + tzi.StandardBias);
  1211. TraceMsg(TF_THISMODULE, "ServerTimeZone = %d, LocalBias = %d min, RelativeCorrection = %d min", iZone, tzi.Bias+tzi.StandardBias, iZoneCorrectionMinutes);
  1212. }
  1213. else
  1214. {
  1215. DBG_WARN("Unable to get local time zone. Not correcting for time zone.");
  1216. }
  1217. }
  1218. }
  1219. 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));
  1220. TraceMsg(TF_THISMODULE, "IntervalTime = %6d minutes", (int)lInterval);
  1221. TraceMsg(TF_THISMODULE, "EarliestTime = %6d minutes", (int)lEarliest);
  1222. TraceMsg(TF_THISMODULE, "LatestTime = %6d minutes", (int)lLatest);
  1223. TraceMsg(TF_THISMODULE, "RandomTime = %6d minutes", (int)lRandom);
  1224. if (iZoneCorrectionMinutes != 0)
  1225. {
  1226. if (lInterval % 60)
  1227. {
  1228. DBG("Not correcting for time zone ; interval not multiple of 1 hour");
  1229. }
  1230. else
  1231. {
  1232. // Correct Earliest time for time zone
  1233. lEarliest += (iZoneCorrectionMinutes % lInterval);
  1234. if (lEarliest < 0)
  1235. lEarliest += lInterval;
  1236. TraceMsg(TF_THISMODULE, "EarliestTime = %6d minutes (after timezone)", (int)lEarliest);
  1237. }
  1238. }
  1239. ZeroMemory(ptt, sizeof(*ptt));
  1240. ptt->cbTriggerSize = sizeof(*ptt);
  1241. ptt->wBeginYear = pstStartDate->wYear;
  1242. ptt->wBeginMonth = pstStartDate->wMonth;
  1243. ptt->wBeginDay = pstStartDate->wDay;
  1244. if (pstEndDate && pstEndDate->wYear)
  1245. {
  1246. ptt->rgFlags |= TASK_TRIGGER_FLAG_HAS_END_DATE;
  1247. ptt->wEndYear = pstEndDate->wYear;
  1248. ptt->wEndMonth = pstEndDate->wMonth;
  1249. ptt->wEndDay = pstEndDate->wDay;
  1250. }
  1251. // Set up Random period ; difference between Latesttime and Earliesttime
  1252. ptt->wRandomMinutesInterval = (WORD) lRandom;
  1253. ptt->wStartHour = (WORD) (lEarliest / 60);
  1254. ptt->wStartMinute = (WORD) (lEarliest % 60);
  1255. // Set up according to IntervalTime
  1256. if (lInterval < MINUTES_PER_DAY)
  1257. {
  1258. // Less than one day (1/2 day, 1/3 day, 1/4 day, etc)
  1259. ptt->MinutesDuration = MINUTES_PER_DAY - lEarliest;
  1260. ptt->MinutesInterval = lInterval;
  1261. ptt->TriggerType = TASK_TIME_TRIGGER_DAILY;
  1262. ptt->Type.Daily.DaysInterval = 1;
  1263. }
  1264. else
  1265. {
  1266. // Greater than or equal to one day.
  1267. DWORD dwIntervalDays = lInterval / MINUTES_PER_DAY;
  1268. TraceMsg(TF_THISMODULE, "Using %d day interval", dwIntervalDays);
  1269. ptt->TriggerType = TASK_TIME_TRIGGER_DAILY;
  1270. ptt->Type.Daily.DaysInterval = (WORD) dwIntervalDays;
  1271. }
  1272. return S_OK;
  1273. }
  1274. //==============================================================================
  1275. // CRunDeliveryAgent provides generic support for synchronous operation of a
  1276. // delivery agent
  1277. // It is aggregatable so that you can add more interfaces to the callback
  1278. //==============================================================================
  1279. CRunDeliveryAgent::CRunDeliveryAgent()
  1280. {
  1281. m_cRef = 1;
  1282. }
  1283. HRESULT CRunDeliveryAgent::Init(CRunDeliveryAgentSink *pParent,
  1284. ISubscriptionItem *pItem,
  1285. REFCLSID rclsidDest)
  1286. {
  1287. ASSERT(pParent && pItem);
  1288. if (m_pParent || m_pItem)
  1289. return E_FAIL; // already initialized. can't reuse an instance.
  1290. if (!pParent || !pItem)
  1291. return E_FAIL;
  1292. m_pParent = pParent;
  1293. m_clsidDest = rclsidDest;
  1294. m_pItem = pItem;
  1295. pItem->AddRef();
  1296. return S_OK;
  1297. }
  1298. CRunDeliveryAgent::~CRunDeliveryAgent()
  1299. {
  1300. CleanUp();
  1301. }
  1302. //
  1303. // IUnknown members
  1304. //
  1305. STDMETHODIMP_(ULONG) CRunDeliveryAgent::AddRef(void)
  1306. {
  1307. return ++m_cRef;
  1308. }
  1309. STDMETHODIMP_(ULONG) CRunDeliveryAgent::Release(void)
  1310. {
  1311. if( 0L != --m_cRef )
  1312. return m_cRef;
  1313. delete this;
  1314. return 0L;
  1315. }
  1316. STDMETHODIMP CRunDeliveryAgent::QueryInterface(REFIID riid, void ** ppv)
  1317. {
  1318. *ppv=NULL;
  1319. // Validate requested interface
  1320. if ((IID_IUnknown == riid) ||
  1321. (IID_ISubscriptionAgentEvents == riid))
  1322. {
  1323. *ppv=(ISubscriptionAgentEvents *)this;
  1324. }
  1325. else
  1326. return E_NOINTERFACE;
  1327. // Addref through the interface
  1328. ((LPUNKNOWN)*ppv)->AddRef();
  1329. return S_OK;
  1330. }
  1331. //
  1332. // ISubscriptionAgentEvents members
  1333. //
  1334. STDMETHODIMP CRunDeliveryAgent::UpdateBegin(const SUBSCRIPTIONCOOKIE *)
  1335. {
  1336. return S_OK;
  1337. }
  1338. STDMETHODIMP CRunDeliveryAgent::UpdateProgress(
  1339. const SUBSCRIPTIONCOOKIE *,
  1340. long lSizeDownloaded,
  1341. long lProgressCurrent,
  1342. long lProgressMax,
  1343. HRESULT hrStatus,
  1344. LPCWSTR wszStatus)
  1345. {
  1346. if (m_pParent)
  1347. m_pParent->OnAgentProgress();
  1348. return S_OK;
  1349. }
  1350. STDMETHODIMP CRunDeliveryAgent::UpdateEnd(const SUBSCRIPTIONCOOKIE *pCookie,
  1351. long lSizeDownloaded,
  1352. HRESULT hrResult,
  1353. LPCWSTR wszResult)
  1354. {
  1355. ASSERT((hrResult != INET_S_AGENT_BASIC_SUCCESS) && (hrResult != E_PENDING));
  1356. m_hrResult = hrResult;
  1357. if (hrResult == INET_S_AGENT_BASIC_SUCCESS || hrResult == E_PENDING)
  1358. {
  1359. // Shouldn't happen; let's be robust anyway.
  1360. m_hrResult = S_OK;
  1361. }
  1362. if (m_pParent)
  1363. {
  1364. m_pParent->OnAgentEnd(pCookie, lSizeDownloaded, hrResult, wszResult, m_fInStartAgent);
  1365. }
  1366. CleanUp();
  1367. return S_OK;
  1368. }
  1369. STDMETHODIMP CRunDeliveryAgent::ReportError(
  1370. const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  1371. HRESULT hrError,
  1372. LPCWSTR wszError)
  1373. {
  1374. return S_FALSE;
  1375. }
  1376. HRESULT CRunDeliveryAgent::StartAgent()
  1377. {
  1378. HRESULT hr;
  1379. if (!m_pParent || !m_pItem || m_pAgent)
  1380. return E_FAIL;
  1381. AddRef(); // Release before we return from this function
  1382. m_fInStartAgent = TRUE;
  1383. m_hrResult = INET_S_AGENT_BASIC_SUCCESS;
  1384. DBG("Using new interfaces to host agent");
  1385. ASSERT(!m_pAgent);
  1386. hr = CoCreateInstance(m_clsidDest, NULL, CLSCTX_INPROC_SERVER,
  1387. IID_ISubscriptionAgentControl, (void **)&m_pAgent);
  1388. if (m_pAgent)
  1389. {
  1390. hr = m_pAgent->StartUpdate(m_pItem, (ISubscriptionAgentEvents *)this);
  1391. }
  1392. hr = m_hrResult;
  1393. m_fInStartAgent = FALSE;
  1394. Release();
  1395. if (hr != INET_S_AGENT_BASIC_SUCCESS)
  1396. {
  1397. return hr;
  1398. }
  1399. return E_PENDING;
  1400. };
  1401. HRESULT CRunDeliveryAgent::AgentPause(DWORD dwFlags)
  1402. {
  1403. if (m_pAgent)
  1404. return m_pAgent->PauseUpdate(0);
  1405. DBG_WARN("CRunDeliveryAgent::AgentPause with no running agent!!");
  1406. return S_FALSE;
  1407. }
  1408. HRESULT CRunDeliveryAgent::AgentResume(DWORD dwFlags)
  1409. {
  1410. if (m_pAgent)
  1411. return m_pAgent->ResumeUpdate(0);
  1412. DBG_WARN("CRunDeliveryAgent::AgentResume with no running agent!!");
  1413. return E_FAIL;
  1414. }
  1415. HRESULT CRunDeliveryAgent::AgentAbort(DWORD dwFlags)
  1416. {
  1417. if (m_pAgent)
  1418. return m_pAgent->AbortUpdate(0);
  1419. DBG_WARN("CRunDeliveryAgent::AgentAbort with no running agent!!");
  1420. return S_FALSE;
  1421. }
  1422. void CRunDeliveryAgent::CleanUp()
  1423. {
  1424. SAFERELEASE(m_pItem);
  1425. SAFERELEASE(m_pAgent);
  1426. m_pParent = NULL;
  1427. }
  1428. //////////////////////////////////////////////////////////////////////////
  1429. //
  1430. // CChannelAgentHolder, derives from CRunDeliveryAgent
  1431. //
  1432. //////////////////////////////////////////////////////////////////////////
  1433. CChannelAgentHolder::CChannelAgentHolder(CChannelAgent *pChannelAgent, CProcessElement *pProcess)
  1434. {
  1435. m_pChannelAgent = pChannelAgent;
  1436. m_pProcess = pProcess;
  1437. }
  1438. CChannelAgentHolder::~CChannelAgentHolder()
  1439. {
  1440. }
  1441. // Won't compile unless we have addref & release here.
  1442. STDMETHODIMP_(ULONG) CChannelAgentHolder::AddRef(void)
  1443. {
  1444. return CRunDeliveryAgent::AddRef();
  1445. }
  1446. STDMETHODIMP_(ULONG) CChannelAgentHolder::Release(void)
  1447. {
  1448. return CRunDeliveryAgent::Release();
  1449. }
  1450. STDMETHODIMP CChannelAgentHolder::QueryInterface(REFIID riid, void ** ppv)
  1451. {
  1452. *ppv=NULL;
  1453. if (IID_IServiceProvider == riid)
  1454. {
  1455. *ppv = (IServiceProvider *)this;
  1456. }
  1457. else
  1458. return CRunDeliveryAgent::QueryInterface(riid, ppv);
  1459. // Addref through the interface
  1460. ((LPUNKNOWN)*ppv)->AddRef();
  1461. return S_OK;
  1462. }
  1463. // IQueryService
  1464. // CLSID_ChannelAgent IID_ISubscriptionItem channel agent start item
  1465. // CLSID_XMLDocument IID_IXMLElement current element
  1466. STDMETHODIMP CChannelAgentHolder::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
  1467. {
  1468. ASSERT(ppvObject);
  1469. if (!ppvObject)
  1470. return E_INVALIDARG;
  1471. if (!m_pChannelAgent || !m_pProcess || !m_pParent)
  1472. return E_FAIL;
  1473. *ppvObject = NULL;
  1474. if (guidService == CLSID_ChannelAgent)
  1475. {
  1476. if (riid == IID_ISubscriptionItem)
  1477. {
  1478. *ppvObject = m_pChannelAgent->GetStartItem();
  1479. }
  1480. // if (riid == IID_IXMLElement) Root XML document?
  1481. }
  1482. else if (guidService == CLSID_XMLDocument)
  1483. {
  1484. if (riid == IID_IXMLElement)
  1485. {
  1486. *ppvObject = m_pProcess->GetCurrentElement();
  1487. }
  1488. }
  1489. if (*ppvObject)
  1490. {
  1491. ((IUnknown *)*ppvObject)->AddRef();
  1492. return S_OK;
  1493. }
  1494. return E_FAIL;
  1495. }
  1496. //////////////////////////////////////////////////////////////////////////
  1497. //
  1498. // CChannelAgent implementation
  1499. //
  1500. //////////////////////////////////////////////////////////////////////////
  1501. CChannelAgent::CChannelAgent()
  1502. {
  1503. DBG("Creating CChannelAgent object");
  1504. // Initialize object
  1505. // Many vars are initialized in StartOperation
  1506. m_pwszURL = NULL;
  1507. m_pCurDownload = NULL;
  1508. m_pProcess = NULL;
  1509. m_fHasInitCookie = FALSE;
  1510. m_pChannelIconHelper = NULL;
  1511. }
  1512. CChannelAgent::~CChannelAgent()
  1513. {
  1514. // DBG("Destroying CChannelAgent object");
  1515. if (m_pwszURL)
  1516. CoTaskMemFree(m_pwszURL);
  1517. SAFELOCALFREE (m_pBuf);
  1518. ASSERT(!m_pProcess);
  1519. SAFERELEASE(m_pChannelIconHelper);
  1520. DBG("Destroyed CChannelAgent object");
  1521. }
  1522. void CChannelAgent::CleanUp()
  1523. {
  1524. if (m_pCurDownload)
  1525. {
  1526. m_pCurDownload->LeaveMeAlone(); // no more calls from them
  1527. m_pCurDownload->DoneDownloading();
  1528. m_pCurDownload->Release();
  1529. m_pCurDownload = NULL;
  1530. }
  1531. SAFEFREEOLESTR(m_pwszURL);
  1532. SAFEDELETE(m_pProcess);
  1533. SAFELOCALFREE(m_pBuf);
  1534. CDeliveryAgent::CleanUp();
  1535. }
  1536. HRESULT CChannelAgent::StartOperation()
  1537. {
  1538. DBG("Channel Agent in StartOperation");
  1539. DWORD dwTemp;
  1540. SAFEFREEOLESTR(m_pwszURL);
  1541. if (FAILED(ReadOLESTR(m_pSubscriptionItem, c_szPropURL, &m_pwszURL)) ||
  1542. !CUrlDownload::IsValidURL(m_pwszURL))
  1543. {
  1544. DBG_WARN("Couldn't get valid URL, aborting");
  1545. SetEndStatus(E_INVALIDARG);
  1546. SendUpdateNone();
  1547. return E_INVALIDARG;
  1548. }
  1549. if (FAILED(ReadDWORD(m_pSubscriptionItem, c_szPropChannelFlags, &m_dwChannelFlags)))
  1550. m_dwChannelFlags = 0;
  1551. // If we download all, we also download some. Makes assumptions easier.
  1552. if (m_dwChannelFlags & CHANNEL_AGENT_PRECACHE_ALL)
  1553. m_dwChannelFlags |= CHANNEL_AGENT_PRECACHE_SOME;
  1554. // NOTE: We may want REST_NoChannelContent to be similar to the webcrawl version.
  1555. // Probably not though because the headlines are useful in the UI.
  1556. if (SHRestricted2W(REST_NoChannelContent, NULL, 0))
  1557. ClearFlag(m_dwChannelFlags, CHANNEL_AGENT_PRECACHE_ALL | CHANNEL_AGENT_PRECACHE_SOME);
  1558. m_dwMaxSizeKB = SHRestricted2W(REST_MaxChannelSize, NULL, 0);
  1559. if (SUCCEEDED(ReadDWORD(m_pSubscriptionItem, c_szPropCrawlMaxSize, &dwTemp))
  1560. && dwTemp
  1561. && (0 == m_dwMaxSizeKB || dwTemp < m_dwMaxSizeKB))
  1562. {
  1563. m_dwMaxSizeKB = dwTemp;
  1564. }
  1565. if (IsAgentFlagSet(FLAG_CHANGESONLY))
  1566. {
  1567. ClearFlag(m_dwChannelFlags, CHANNEL_AGENT_PRECACHE_ALL|
  1568. CHANNEL_AGENT_PRECACHE_SOME|CHANNEL_AGENT_PRECACHE_SCRNSAVER);
  1569. DBG("Channel agent is in 'changes only' mode.");
  1570. }
  1571. else
  1572. {
  1573. // Read old group ID
  1574. ReadLONGLONG(m_pSubscriptionItem, c_szPropCrawlGroupID, &m_llOldCacheGroupID);
  1575. // Read new ID if present
  1576. m_llCacheGroupID = 0;
  1577. ReadLONGLONG(m_pSubscriptionItem, c_szPropCrawlNewGroupID, &m_llCacheGroupID);
  1578. }
  1579. return CDeliveryAgent::StartOperation();
  1580. }
  1581. HRESULT CChannelAgent::StartDownload()
  1582. {
  1583. ASSERT(!m_pCurDownload);
  1584. TraceMsg(TF_THISMODULE, "Channel agent starting download of CDF: URL=%ws", m_pwszURL);
  1585. m_pCurDownload = new CUrlDownload(this, 0);
  1586. if (!m_pCurDownload)
  1587. return E_OUTOFMEMORY;
  1588. // Change detection
  1589. m_varChange.vt = VT_EMPTY;
  1590. if (IsAgentFlagSet(FLAG_CHANGESONLY))
  1591. {
  1592. // "Changes Only" mode, we have persisted a change detection code
  1593. ReadVariant(m_pSubscriptionItem, c_szPropChangeCode, &m_varChange);
  1594. m_llCacheGroupID = 0;
  1595. }
  1596. else
  1597. {
  1598. // Create new cache group
  1599. if (!m_llCacheGroupID)
  1600. {
  1601. m_llCacheGroupID = CreateUrlCacheGroup(CACHEGROUP_FLAG_NONPURGEABLE, 0);
  1602. ASSERT_MSG(m_llCacheGroupID != 0, "Create cache group failed");
  1603. }
  1604. }
  1605. TCHAR szUrl[INTERNET_MAX_URL_LENGTH];
  1606. MyOleStrToStrN(szUrl, INTERNET_MAX_URL_LENGTH, m_pwszURL);
  1607. PreCheckUrlForChange(szUrl, &m_varChange, NULL);
  1608. SendUpdateProgress(m_pwszURL, 0, -1, 0);
  1609. // Start download
  1610. return m_pCurDownload->BeginDownloadURL2(
  1611. m_pwszURL, BDU2_URLMON, BDU2_NEEDSTREAM, NULL, m_dwMaxSizeKB<<10);
  1612. }
  1613. HRESULT CChannelAgent::OnAuthenticate(HWND *phwnd, LPWSTR *ppszUsername, LPWSTR *ppszPassword)
  1614. {
  1615. HRESULT hr;
  1616. ASSERT(phwnd && ppszUsername && ppszPassword);
  1617. ASSERT((HWND)-1 == *phwnd && NULL == *ppszUsername && NULL == *ppszPassword);
  1618. hr = ReadOLESTR(m_pSubscriptionItem, c_szPropCrawlUsername, ppszUsername);
  1619. if (SUCCEEDED(hr))
  1620. {
  1621. BSTR bstrPassword = NULL;
  1622. hr = ReadPassword(m_pSubscriptionItem, &bstrPassword);
  1623. if (SUCCEEDED(hr))
  1624. {
  1625. int len = (lstrlenW(bstrPassword) + 1) * sizeof(WCHAR);
  1626. *ppszPassword = (LPWSTR) CoTaskMemAlloc(len);
  1627. if (*ppszPassword)
  1628. {
  1629. CopyMemory(*ppszPassword, bstrPassword, len);
  1630. }
  1631. SAFEFREEBSTR(bstrPassword);
  1632. if (*ppszPassword)
  1633. {
  1634. return S_OK;
  1635. }
  1636. }
  1637. }
  1638. SAFEFREEOLESTR(*ppszUsername);
  1639. SAFEFREEOLESTR(*ppszPassword);
  1640. return E_FAIL;
  1641. }
  1642. HRESULT CChannelAgent::OnDownloadComplete(UINT iID, int iError)
  1643. {
  1644. TraceMsg(TF_THISMODULE, "Channel Agent: OnDownloadComplete(%d)", iError);
  1645. IStream *pStm = NULL;
  1646. HRESULT hr;
  1647. BOOL fProcessed=FALSE;
  1648. DWORD dwCDFSizeKB=0, dwResponseCode;
  1649. BSTR bstrTmp;
  1650. char chBuf[MY_MAX_CACHE_ENTRY_INFO];
  1651. DWORD dwBufSize = sizeof(chBuf);
  1652. LPINTERNET_CACHE_ENTRY_INFO lpInfo = (LPINTERNET_CACHE_ENTRY_INFO) chBuf;
  1653. if (iError)
  1654. hr = E_FAIL;
  1655. else
  1656. {
  1657. hr = m_pCurDownload->GetResponseCode(&dwResponseCode);
  1658. if (SUCCEEDED(hr))
  1659. {
  1660. hr = CheckResponseCode(dwResponseCode);
  1661. }
  1662. else
  1663. DBG_WARN("CChannelAgent failed to GetResponseCode");
  1664. }
  1665. if (SUCCEEDED(hr))
  1666. {
  1667. hr = m_pCurDownload->GetStream(&pStm);
  1668. m_pCurDownload->ReleaseStream();
  1669. }
  1670. if (SUCCEEDED(hr))
  1671. {
  1672. TCHAR szThisUrl[INTERNET_MAX_URL_LENGTH];
  1673. LPWSTR pwszThisUrl;
  1674. m_pCurDownload->GetRealURL(&pwszThisUrl);
  1675. if (pwszThisUrl)
  1676. {
  1677. MyOleStrToStrN(szThisUrl, INTERNET_MAX_URL_LENGTH, pwszThisUrl);
  1678. LocalFree(pwszThisUrl);
  1679. if (SUCCEEDED(GetUrlInfoAndMakeSticky(
  1680. NULL,
  1681. szThisUrl,
  1682. lpInfo,
  1683. dwBufSize,
  1684. m_llCacheGroupID)))
  1685. {
  1686. dwCDFSizeKB = (((LPINTERNET_CACHE_ENTRY_INFO)chBuf)->dwSizeLow+512) >> 10;
  1687. TraceMsg(TF_THISMODULE, "CDF size %d kb", dwCDFSizeKB);
  1688. hr = PostCheckUrlForChange(&m_varChange, lpInfo, lpInfo->LastModifiedTime);
  1689. // If we FAILED, we mark it as changed.
  1690. if (hr == S_OK || FAILED(hr))
  1691. {
  1692. SetAgentFlag(FLAG_CDFCHANGED);
  1693. DBG("CDF has changed; will flag channel as changed");
  1694. }
  1695. // "Changes Only" mode, persist change detection code
  1696. if (IsAgentFlagSet(FLAG_CHANGESONLY))
  1697. {
  1698. WriteVariant(m_pSubscriptionItem, c_szPropChangeCode, &m_varChange);
  1699. }
  1700. hr = S_OK;
  1701. }
  1702. }
  1703. }
  1704. else
  1705. {
  1706. SetEndStatus(E_INVALIDARG);
  1707. }
  1708. // Get an object model on our Channel Description File
  1709. if (SUCCEEDED(hr) && pStm)
  1710. {
  1711. IPersistStreamInit *pPersistStm=NULL;
  1712. CoCreateInstance(CLSID_XMLDocument, NULL, CLSCTX_INPROC,
  1713. IID_IPersistStreamInit, (void **)&pPersistStm);
  1714. if (pPersistStm)
  1715. {
  1716. pPersistStm->InitNew();
  1717. hr = pPersistStm->Load(pStm);
  1718. if (SUCCEEDED(hr))
  1719. {
  1720. IXMLDocument *pDoc;
  1721. hr = pPersistStm->QueryInterface(IID_IXMLDocument, (void **)&pDoc);
  1722. if (SUCCEEDED(hr) && pDoc)
  1723. {
  1724. IXMLElement *pRoot;
  1725. BSTR bstrCharSet=NULL;
  1726. if (SUCCEEDED(pDoc->get_charset(&bstrCharSet)) && bstrCharSet)
  1727. {
  1728. WriteOLESTR(m_pSubscriptionItem, c_szPropCharSet, bstrCharSet);
  1729. TraceMsg(TF_THISMODULE, "Charset = \"%ws\"", bstrCharSet);
  1730. SysFreeString(bstrCharSet);
  1731. }
  1732. else
  1733. WriteEMPTY(m_pSubscriptionItem, c_szPropCharSet);
  1734. hr = pDoc->get_root(&pRoot);
  1735. if (SUCCEEDED(hr) && pRoot)
  1736. {
  1737. if (SUCCEEDED(pRoot->get_tagName(&bstrTmp)) && bstrTmp)
  1738. {
  1739. if (!StrCmpIW(bstrTmp, L"Channel"))
  1740. {
  1741. ASSERT(!m_pProcess);
  1742. m_pProcess = new CProcessRoot(this, pRoot);
  1743. if (m_pProcess)
  1744. {
  1745. if (IsAgentFlagSet(FLAG_CDFCHANGED))
  1746. SetEndStatus(S_OK);
  1747. else
  1748. SetEndStatus(S_FALSE);
  1749. m_pProcess->m_dwCurSizeKB = dwCDFSizeKB;
  1750. WriteEMPTY(m_pSubscriptionItem, c_szPropEmailURL);
  1751. hr = m_pProcess->Run(); // This will get us cleaned up (now or later)
  1752. fProcessed = TRUE; // So we shouldn't do it ourselves
  1753. }
  1754. }
  1755. else
  1756. DBG_WARN("Valid XML but invalid CDF");
  1757. SAFEFREEBSTR(bstrTmp);
  1758. }
  1759. pRoot->Release();
  1760. }
  1761. pDoc->Release();
  1762. }
  1763. }
  1764. pPersistStm->Release();
  1765. }
  1766. }
  1767. if (!fProcessed || (FAILED(hr) && (hr != E_PENDING)))
  1768. {
  1769. if (INET_S_AGENT_BASIC_SUCCESS == GetEndStatus())
  1770. SetEndStatus(E_FAIL);
  1771. DBG_WARN("Failed to process CDF ; XML load failed?");
  1772. CleanUp(); // CleanUp only if the process failed (otherwise OnChildDone does it)
  1773. }
  1774. #ifdef DEBUG
  1775. if (hr == E_PENDING)
  1776. DBG("CChannelAgent::OnDownloadComplete not cleaning up, webcrawl pending");
  1777. #endif
  1778. return S_OK;
  1779. }
  1780. HRESULT CChannelAgent::OnChildDone(CProcessElement *pChild, HRESULT hr)
  1781. {
  1782. // Our CProcessRoot has reported that it's done. Clean up.
  1783. DBG("CChannelAgent::OnChildDone cleaning up Channel delivery agent");
  1784. if (m_llOldCacheGroupID)
  1785. {
  1786. DBG("Nuking old cache group.");
  1787. if (!DeleteUrlCacheGroup(m_llOldCacheGroupID, 0, 0))
  1788. {
  1789. DBG_WARN("Failed to delete old cache group!");
  1790. }
  1791. }
  1792. WriteLONGLONG(m_pSubscriptionItem, c_szPropCrawlGroupID, m_llCacheGroupID);
  1793. // Add "total size" property
  1794. m_lSizeDownloadedKB = (long) (m_pProcess->m_dwCurSizeKB);
  1795. WriteDWORD(m_pSubscriptionItem, c_szPropCrawlActualSize, m_lSizeDownloadedKB);
  1796. WriteDWORD(m_pSubscriptionItem, c_szPropActualProgressMax, m_pProcess->m_iTotalStarted);
  1797. CleanUp();
  1798. return S_OK;
  1799. }
  1800. HRESULT CChannelAgent::AgentPause(DWORD dwFlags)
  1801. {
  1802. DBG("CChannelAgent::AgentPause");
  1803. if (m_pProcess)
  1804. m_pProcess->Pause(dwFlags);
  1805. return CDeliveryAgent::AgentPause(dwFlags);
  1806. }
  1807. HRESULT CChannelAgent::AgentResume(DWORD dwFlags)
  1808. {
  1809. DBG("CChannelAgent::AgentResume");
  1810. if (m_pProcess)
  1811. m_pProcess->Resume(dwFlags);
  1812. return CDeliveryAgent::AgentResume(dwFlags);
  1813. }
  1814. // Forcibly abort current operation
  1815. HRESULT CChannelAgent::AgentAbort(DWORD dwFlags)
  1816. {
  1817. DBG("CChannelAgent::AgentAbort");
  1818. if (m_pCurDownload)
  1819. m_pCurDownload->DoneDownloading();
  1820. if (m_pProcess)
  1821. m_pProcess->Abort(dwFlags);
  1822. return CDeliveryAgent::AgentAbort(dwFlags);
  1823. }
  1824. HRESULT CChannelAgent::ModifyUpdateEnd(ISubscriptionItem *pEndItem, UINT *puiRes)
  1825. {
  1826. // Customize our end status string
  1827. switch (GetEndStatus())
  1828. {
  1829. case INET_E_AGENT_MAX_SIZE_EXCEEDED :
  1830. *puiRes = IDS_AGNT_STATUS_SIZELIMIT; break;
  1831. case INET_E_AGENT_CACHE_SIZE_EXCEEDED :
  1832. *puiRes = IDS_AGNT_STATUS_CACHELIMIT; break;
  1833. case E_FAIL : *puiRes = IDS_CRAWL_STATUS_NOT_OK; break;
  1834. case S_OK :
  1835. if (!IsAgentFlagSet(FLAG_CHANGESONLY))
  1836. *puiRes = IDS_CRAWL_STATUS_OK;
  1837. else
  1838. *puiRes = IDS_URL_STATUS_OK;
  1839. break;
  1840. case S_FALSE :
  1841. if (!IsAgentFlagSet(FLAG_CHANGESONLY))
  1842. *puiRes = IDS_CRAWL_STATUS_UNCHANGED;
  1843. else
  1844. *puiRes = IDS_URL_STATUS_UNCHANGED;
  1845. break;
  1846. case INET_S_AGENT_PART_FAIL : *puiRes = IDS_CRAWL_STATUS_MOSTLYOK; break;
  1847. }
  1848. return CDeliveryAgent::ModifyUpdateEnd(pEndItem, puiRes);
  1849. }
  1850. const GUID CLSID_CDFICONHANDLER =
  1851. {0xf3ba0dc0, 0x9cc8, 0x11d0, {0xa5, 0x99, 0x0, 0xc0, 0x4f, 0xd6, 0x44, 0x35}};
  1852. extern HRESULT LoadWithCookie(LPCTSTR, POOEBuf, DWORD *, SUBSCRIPTIONCOOKIE *);
  1853. // IExtractIcon members
  1854. STDMETHODIMP CChannelAgent::GetIconLocation(UINT uFlags, LPTSTR szIconFile, UINT cchMax, int * piIndex, UINT * pwFlags)
  1855. {
  1856. DWORD dwSize;
  1857. IChannelMgrPriv* pIChannelMgrPriv = NULL;
  1858. HRESULT hr = E_FAIL;
  1859. TCHAR szPath[MAX_PATH];
  1860. if (!m_pBuf) {
  1861. m_pBuf = (POOEBuf)MemAlloc(LPTR, sizeof(OOEBuf));
  1862. if (!m_pBuf)
  1863. return E_OUTOFMEMORY;
  1864. HRESULT hr = LoadWithCookie(NULL, m_pBuf, &dwSize, &m_SubscriptionCookie);
  1865. RETURN_ON_FAILURE(hr);
  1866. }
  1867. hr = GetChannelPath(m_pBuf->m_URL, szPath, ARRAYSIZE(szPath), &pIChannelMgrPriv);
  1868. if (SUCCEEDED(hr) && pIChannelMgrPriv)
  1869. {
  1870. IPersistFile* ppf = NULL;
  1871. BOOL bCoinit = FALSE;
  1872. HRESULT hr2 = E_FAIL;
  1873. pIChannelMgrPriv->Release();
  1874. hr = CoCreateInstance(CLSID_CDFICONHANDLER, NULL, CLSCTX_INPROC_SERVER,
  1875. IID_IPersistFile, (void**)&ppf);
  1876. if ((hr == CO_E_NOTINITIALIZED || hr == REGDB_E_IIDNOTREG) &&
  1877. SUCCEEDED(CoInitialize(NULL)))
  1878. {
  1879. bCoinit = TRUE;
  1880. hr = CoCreateInstance(CLSID_CDFICONHANDLER, NULL, CLSCTX_INPROC_SERVER,
  1881. IID_IPersistFile, (void**)&ppf);
  1882. }
  1883. if (SUCCEEDED(hr))
  1884. {
  1885. hr = ppf->QueryInterface(IID_IExtractIcon, (void**)&m_pChannelIconHelper);
  1886. WCHAR wszPath[MAX_PATH];
  1887. MyStrToOleStrN(wszPath, ARRAYSIZE(wszPath), szPath);
  1888. hr2 = ppf->Load(wszPath, 0);
  1889. ppf->Release();
  1890. }
  1891. if (SUCCEEDED(hr) && m_pChannelIconHelper)
  1892. {
  1893. hr = m_pChannelIconHelper->GetIconLocation(uFlags, szIconFile, cchMax, piIndex, pwFlags);
  1894. }
  1895. if (bCoinit)
  1896. CoUninitialize();
  1897. }
  1898. if (m_pChannelIconHelper == NULL)
  1899. {
  1900. WCHAR wszCookie[GUIDSTR_MAX];
  1901. ASSERT (piIndex && pwFlags && szIconFile);
  1902. StringFromGUID2(m_SubscriptionCookie, wszCookie, ARRAYSIZE(wszCookie));
  1903. MyOleStrToStrN(szIconFile, cchMax, wszCookie);
  1904. *piIndex = 0;
  1905. *pwFlags |= GIL_NOTFILENAME | GIL_PERINSTANCE;
  1906. hr = NOERROR;
  1907. }
  1908. return hr;
  1909. }
  1910. STDMETHODIMP CChannelAgent::Extract(LPCTSTR szIconFile, UINT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIconSize)
  1911. {
  1912. static HICON channelIcon = NULL;
  1913. if (!phiconLarge || !phiconSmall)
  1914. return E_INVALIDARG;
  1915. * phiconLarge = * phiconSmall = NULL;
  1916. if (m_pChannelIconHelper)
  1917. {
  1918. return m_pChannelIconHelper->Extract(szIconFile, nIconIndex, phiconLarge, phiconSmall, nIconSize);
  1919. }
  1920. else
  1921. {
  1922. DWORD dwSize;
  1923. if (!m_pBuf) {
  1924. m_pBuf = (POOEBuf)MemAlloc(LPTR, sizeof(OOEBuf));
  1925. if (!m_pBuf)
  1926. return E_OUTOFMEMORY;
  1927. HRESULT hr = LoadWithCookie(NULL, m_pBuf, &dwSize, &m_SubscriptionCookie);
  1928. RETURN_ON_FAILURE(hr);
  1929. }
  1930. BYTE bBuf[MY_MAX_CACHE_ENTRY_INFO];
  1931. LPINTERNET_CACHE_ENTRY_INFO pEntry = (INTERNET_CACHE_ENTRY_INFO *)bBuf;
  1932. dwSize = sizeof(bBuf);
  1933. if (GetUrlCacheEntryInfo(m_pBuf->m_URL, pEntry, &dwSize)) {
  1934. SHFILEINFO sfi;
  1935. UINT cbFileInfo = sizeof(sfi), uFlags = SHGFI_ICON | SHGFI_LARGEICON;
  1936. if (NULL != SHGetFileInfo(pEntry->lpszLocalFileName, 0,
  1937. &sfi, cbFileInfo, uFlags))
  1938. {
  1939. ASSERT(sfi.hIcon);
  1940. *phiconLarge = *phiconSmall = sfi.hIcon;
  1941. return NOERROR;
  1942. }
  1943. }
  1944. if (channelIcon == NULL) {
  1945. channelIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_CHANNEL));
  1946. ASSERT(channelIcon);
  1947. }
  1948. * phiconLarge = * phiconSmall = channelIcon;
  1949. return NOERROR;
  1950. }
  1951. }