cdfagent.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 71k
Category:

Windows Kernel

Development Platform:

Visual C++

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