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

Windows Kernel

Development Platform:

Visual C++

  1. #include "local.h"
  2. #include "resource.h"
  3. #include "hsfolder.h"
  4. #include "cachesrch.h"
  5. #include "shdguid.h"
  6. #include "sfview.h"
  7. #include <shlwapi.h>
  8. #include <limits.h>
  9. #include <mluisupp.h>
  10. #define DM_HSFOLDER 0
  11. #define DM_HISTVIEW    0x00000000
  12. #define DM_CACHESEARCH 0x40000000
  13. const TCHAR c_szRegKeyTopNSites[] = TEXT("HistoryTopNSitesView");
  14. #define REGKEYTOPNSITESLEN (ARRAYSIZE(c_szRegKeyTopNSites) - 1)
  15. const TCHAR c_szHistPrefix[] = TEXT("Visited: ");
  16. #define HISTPREFIXLEN (ARRAYSIZE(c_szHistPrefix)-1)
  17. const TCHAR c_szHostPrefix[] = TEXT(":Host: ");
  18. #define HOSTPREFIXLEN (ARRAYSIZE(c_szHostPrefix)-1)
  19. const CHAR c_szIntervalPrefix[] = "MSHist";
  20. #define INTERVALPREFIXLEN (ARRAYSIZE(c_szIntervalPrefix)-1)
  21. const TCHAR c_szTextHeader[] = TEXT("Content-type: text/");
  22. #define TEXTHEADERLEN (ARRAYSIZE(c_szTextHeader) - 1)
  23. const TCHAR c_szHTML[] = TEXT("html");
  24. #define HTMLLEN (ARRAYSIZE(c_szHTML) - 1)
  25. #define TYPICAL_INTERVALS (4+7)
  26. // these are common flags to ShChangeNotify
  27. #ifndef UNIX
  28. #define CHANGE_FLAGS (0)
  29. #else
  30. #define CHANGE_FLAGS SHCNF_FLUSH
  31. #endif
  32. #define ALL_CHANGES (SHCNE_DELETE|SHCNE_MKDIR|SHCNE_RMDIR|SHCNE_CREATE|SHCNE_UPDATEDIR)
  33. #define FORMAT_PARAMS (FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY|FORMAT_MESSAGE_MAX_WIDTH_MASK)
  34. // this functions needs to stay here since it has cpp stuff
  35. LPCEIPIDL _CreateCacheFolderPidl(BOOL fOleAlloc, DWORD dwSize, USHORT usSign);
  36. LPCEIPIDL _CreateIdCacheFolderPidl(BOOL fOleAlloc, USHORT usSign, LPCTSTR szId);
  37. LPCEIPIDL _CreateBuffCacheFolderPidl(BOOL fOleAlloc, DWORD dwSize, LPINTERNET_CACHE_ENTRY_INFO pcei);
  38. LPHEIPIDL _CreateHCacheFolderPidl(BOOL fOleMalloc, LPCTSTR pszUrl, FILETIME ftModified, LPSTATURL lpStatURL,
  39.                                   __int64 llPriority = 0, DWORD dwNumHits = 0);
  40. DWORD     _DaysInInterval(HSFINTERVAL *pInterval);
  41. void      _KeyForInterval(HSFINTERVAL *pInterval, LPTSTR pszInterval, int cchInterval);
  42. void      _FileTimeDeltaDays(FILETIME *pftBase, FILETIME *pftNew, int Days);
  43. void      ResizeStatusBar(HWND hwnd, BOOL fInit);
  44. //  BEGIN OF JCORDELL CODE
  45. #define QUANTA_IN_A_SECOND  10000000
  46. #define SECONDS_IN_A_DAY    60 * 60 * 24
  47. #define QUANTA_IN_A_DAY     ((__int64) QUANTA_IN_A_SECOND * SECONDS_IN_A_DAY)
  48. #define INT64_VALUE(pFT)    ((((__int64)(pFT)->dwHighDateTime) << 32) + (__int64) (pFT)->dwLowDateTime)
  49. #define DAYS_DIFF(s,e)      ((int) (( INT64_VALUE(s) - INT64_VALUE(e) ) / QUANTA_IN_A_DAY))
  50. BOOL      GetDisplayNameForTimeInterval( const FILETIME *pStartTime, const FILETIME *pEndTime,
  51.                                          TCHAR *szBuffer, int cbBufferLength );
  52. BOOL      GetTooltipForTimeInterval( const FILETIME *pStartTime, const FILETIME *pEndTime,
  53.                                      TCHAR *szBuffer, int cbBufferLength );
  54. //  END OF JCORDELL CODE
  55. class CHistFolder : public CHistCacheFolder
  56. {
  57. public:
  58.     CHistFolder(FOLDER_TYPE FolderType) : CHistCacheFolder(FolderType) {}
  59.     STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  60. protected:
  61.     STDMETHODIMP _GetDetail(LPCITEMIDLIST pidl, UINT iColumn, LPTSTR pszStr, UINT cchStr);
  62. };
  63. class CCacheFolder : public CHistCacheFolder
  64. {
  65. public:
  66.     CCacheFolder(FOLDER_TYPE FolderType) : CHistCacheFolder(FolderType) {}
  67.     STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  68. protected:
  69.     STDMETHODIMP _GetDetail(LPCITEMIDLIST pidl, UINT iColumn, LPTSTR pszStr, UINT cchStr);
  70. };
  71. class CDetailsOfFolder : public IShellDetails
  72. {
  73. public:
  74.     CDetailsOfFolder(HWND hwnd, IShellFolder2 *psf) : _cRef(1), _psf(psf), _hwnd(hwnd)
  75.     {
  76.         _psf->AddRef();
  77.     }
  78.     // *** IUnknown methods ***
  79.     STDMETHOD(QueryInterface)(REFIID riid, void ** ppv);
  80.     STDMETHOD_(ULONG,AddRef)();
  81.     STDMETHOD_(ULONG,Release)();
  82.     // IShellDetails
  83.     STDMETHOD(GetDetailsOf)(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi);
  84.     STDMETHOD(ColumnClick)(UINT iColumn);
  85. private:
  86.     virtual ~CDetailsOfFolder() { _psf->Release(); }
  87.     LONG _cRef;
  88.     IShellFolder2 *_psf;
  89.     HWND _hwnd;
  90. };
  91. STDMETHODIMP CDetailsOfFolder::QueryInterface(REFIID riid, void **ppv)
  92. {
  93.     static const QITAB qit[] = {
  94.         QITABENT(CDetailsOfFolder, IShellDetails),
  95.         { 0 },
  96.     };
  97.     return QISearch(this, qit, riid, ppv);
  98. }
  99. STDMETHODIMP_(ULONG) CDetailsOfFolder::AddRef()
  100. {
  101.     return InterlockedIncrement(&_cRef);
  102. }
  103. STDMETHODIMP_(ULONG) CDetailsOfFolder::Release()
  104. {
  105.     if (InterlockedDecrement(&_cRef))
  106.         return _cRef;
  107.     delete this;
  108.     return 0;
  109. }
  110. HRESULT CDetailsOfFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi)
  111. {
  112.     return _psf->GetDetailsOf(pidl, iColumn, pdi);
  113. }
  114. HRESULT CDetailsOfFolder::ColumnClick(UINT iColumn)
  115. {
  116.     ShellFolderView_ReArrange(_hwnd, iColumn);
  117.     return NOERROR;
  118. }
  119. //////////////////////////////////////////////////////////////////////////////
  120. //
  121. // CHistCacheFolderView Functions and Definitions
  122. //
  123. //////////////////////////////////////////////////////////////////////////////
  124. ////////////////////////
  125. //
  126. // Column definition for the Cache Folder DefView
  127. //
  128. enum {
  129.     ICOLC_URL_SHORTNAME = 0,
  130.     ICOLC_URL_NAME,
  131.     ICOLC_URL_TYPE,
  132.     ICOLC_URL_SIZE,
  133.     ICOLC_URL_EXPIRES,
  134.     ICOLC_URL_MODIFIED,
  135.     ICOLC_URL_ACCESSED,
  136.     ICOLC_URL_LASTSYNCED,
  137.     ICOLC_URL_MAX         // Make sure this is the last enum item
  138. };
  139. typedef struct _COLSPEC
  140. {
  141.     short int iCol;
  142.     short int ids;        // Id of string for title
  143.     short int cchCol;     // Number of characters wide to make column
  144.     short int iFmt;       // The format of the column;
  145. } COLSPEC;
  146. const COLSPEC s_CacheFolder_cols[] = {
  147.     {ICOLC_URL_SHORTNAME,  IDS_SHORTNAME_COL,  18, LVCFMT_LEFT},
  148.     {ICOLC_URL_NAME,       IDS_NAME_COL,       30, LVCFMT_LEFT},
  149.     {ICOLC_URL_TYPE,       IDS_TYPE_COL,       15, LVCFMT_LEFT},
  150.     {ICOLC_URL_SIZE,       IDS_SIZE_COL,        8, LVCFMT_RIGHT},
  151.     {ICOLC_URL_EXPIRES,    IDS_EXPIRES_COL,    18, LVCFMT_LEFT},
  152.     {ICOLC_URL_MODIFIED,   IDS_MODIFIED_COL,   18, LVCFMT_LEFT},
  153.     {ICOLC_URL_ACCESSED,   IDS_ACCESSED_COL,   18, LVCFMT_LEFT},
  154.     {ICOLC_URL_LASTSYNCED, IDS_LASTSYNCED_COL, 18, LVCFMT_LEFT}
  155. };
  156. const COLSPEC s_HistIntervalFolder_cols[] = {
  157.     {ICOLH_URL_NAME,          IDS_TIMEPERIOD_COL,           30, LVCFMT_LEFT},
  158. };
  159. const COLSPEC s_HistHostFolder_cols[] = {
  160.     {ICOLH_URL_NAME,          IDS_HOSTNAME_COL,           30, LVCFMT_LEFT},
  161. };
  162. const COLSPEC s_HistFolder_cols[] = {
  163.     {ICOLH_URL_NAME,          IDS_NAME_COL,           30, LVCFMT_LEFT},
  164.     {ICOLH_URL_TITLE,         IDS_TITLE_COL,          30, LVCFMT_LEFT},
  165.     {ICOLH_URL_LASTVISITED,   IDS_LASTVISITED_COL,    18, LVCFMT_LEFT},
  166. };
  167. //////////////////////////////////////////////////////////////////////
  168. #ifdef DEBUG
  169. void DumpPidl(LPCITEMIDLIST pidl, LPTSTR opt) {
  170.     if (!DM_HISTVIEW)
  171.         return;
  172.     TraceMsg(DM_HISTVIEW, "=========pidl dump: %s", opt);
  173.     LPCITEMIDLIST pidlTemp = pidl;
  174.     while(pidlTemp->mkid.cb) {
  175.         DWORD cch = (pidlTemp->mkid.cb * 6) + 25;
  176.         LPSTR ostr = (LPSTR)LocalAlloc(LPTR, cch);
  177.         if (ostr != NULL)
  178.         {
  179.             wnsprintfA(ostr, cch, "  [%02X] ", pidlTemp->mkid.cb);
  180.             UINT ostrOffset = lstrlenA(ostr);
  181.             int i;
  182.             for (i = 2; i <= pidlTemp->mkid.cb; ++i) {
  183.                 wnsprintfA(ostr + ostrOffset, 4, "  %c",
  184.                           (((*((char*)pidlTemp + i)) >= 20) ?
  185.                            ((*(((char*)pidlTemp) + i))) : '.'));
  186.                 ostrOffset += 3;
  187.             }
  188.             wnsprintfA(ostr + ostrOffset, 17, "n               ");
  189.             ostrOffset += 16;
  190.             for (i = 2; i <= pidlTemp->mkid.cb; ++i) {
  191.                 wnsprintfA(ostr + ostrOffset, cch - ostrOffset, "%02X ", *(((char*)pidlTemp) + i));
  192.                 ostrOffset += 3;
  193.             }
  194.             //        TraceMsg(DM_HISTVIEW, "   [%d] %s", pidlTemp->mkid.cb, ((LPSTR)pidlTemp + 1));
  195.             pidlTemp = _ILNext(pidlTemp);
  196.             OutputDebugStringA(ostr);
  197.             OutputDebugStringA("n");
  198.             //TraceMsg(DM_HISTVIEW, "n%s", ostr);
  199.             LocalFree(ostr);
  200.         }
  201.     }
  202. }
  203. #else
  204. #define DumpPidl(x,y)
  205. #endif
  206. HRESULT CreateSpecialViewPidl(USHORT usViewType, LPITEMIDLIST* ppidlOut, UINT cbExtra = 0, LPBYTE *ppbExtra = NULL);
  207. HRESULT ConvertStandardHistPidlToSpecialViewPidl(LPCITEMIDLIST pidlStandardHist,
  208.                                                  USHORT        usViewType,
  209.                                                  LPITEMIDLIST *ppidlOut);
  210. UINT MergeMenuHierarchy(HMENU hmenuDst, HMENU hmenuSrc, UINT idcMin, UINT idcMax)
  211. {
  212.     UINT idcMaxUsed = idcMin;
  213.     int imi = GetMenuItemCount(hmenuSrc);
  214.     while (--imi >= 0)
  215.     {
  216.         MENUITEMINFO mii = { sizeof(mii), MIIM_ID | MIIM_SUBMENU, 0, 0, 0, NULL, NULL, NULL, 0, NULL, 0 };
  217.         if (GetMenuItemInfo(hmenuSrc, imi, TRUE, &mii))
  218.         {
  219.             UINT idcT = Shell_MergeMenus(GetMenuFromID(hmenuDst, mii.wID),
  220.                     mii.hSubMenu, 0, idcMin, idcMax, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
  221.             idcMaxUsed = max(idcMaxUsed, idcT);
  222.         }
  223.     }
  224.     return idcMaxUsed;
  225. }
  226. HRESULT HistCacheFolderView_MergeMenu(UINT idMenu, LPQCMINFO pqcm)
  227. {
  228.     HMENU hmenu = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(idMenu));
  229.     if (hmenu)
  230.     {
  231.         MergeMenuHierarchy(pqcm->hmenu, hmenu, pqcm->idCmdFirst, pqcm->idCmdLast);
  232.         DestroyMenu(hmenu);
  233.     }
  234.     return NOERROR;
  235. }
  236. HRESULT HistCacheFolderView_Command(HWND hwnd, UINT uID, FOLDER_TYPE FolderType)
  237. {
  238.     if ((!IsLeaf(FolderType)) && uID != IDM_SORTBYTITLE)
  239.         return E_FAIL;
  240.     switch (uID) {
  241.     case IDM_SORTBYTITLE:
  242.     case IDM_SORTBYADDRESS:
  243.     case IDM_SORTBYVISITED:
  244.     case IDM_SORTBYUPDATED:
  245.         ShellFolderView_ReArrange(hwnd, uID - IDM_SORTBYTITLE);
  246.         break;
  247.     case IDM_SORTBYNAME:
  248.     case IDM_SORTBYADDRESS2:
  249.     case IDM_SORTBYSIZE:
  250.     case IDM_SORTBYEXPIRES2:
  251.     case IDM_SORTBYMODIFIED:
  252.     case IDM_SORTBYACCESSED:
  253.     case IDM_SORTBYCHECKED:
  254.         ShellFolderView_ReArrange(hwnd, uID - IDM_SORTBYNAME);
  255.         break;
  256.     default:
  257.         return E_FAIL;
  258.     }
  259.     return NOERROR;
  260. }
  261. HRESULT HistCacheFolderView_InitMenuPopup(HWND hwnd, UINT idCmdFirst, int nIndex, HMENU hmenu)
  262. {
  263.     return NOERROR;
  264. }
  265. HRESULT HistCacheFolderView_OnColumnClick(HWND hwnd, UINT iColumn)
  266. {
  267.     ShellFolderView_ReArrange(hwnd, iColumn);
  268.     return NOERROR;
  269. }
  270. HRESULT HistCacheFolderView_DidDragDrop(IDataObject *pdo, DWORD dwEffect)
  271. {
  272.     if (dwEffect & DROPEFFECT_MOVE)
  273.     {
  274.         CHistCacheItem *pHCItem;
  275.         BOOL fBulkDelete;
  276.         if (SUCCEEDED(pdo->QueryInterface(IID_IHistCache, (void **)&pHCItem)))
  277.         {
  278.             fBulkDelete = pHCItem->_cItems > LOTS_OF_FILES;
  279.             for (UINT i = 0; i < pHCItem->_cItems; i++)
  280.             {
  281.                 if (DeleteUrlCacheEntry(HCPidlToSourceUrl((LPCITEMIDLIST)pHCItem->_ppcei[i])))
  282.                 {
  283.                     if (!fBulkDelete)
  284.                     {
  285.                         _GenerateEvent(SHCNE_DELETE, pHCItem->_pHCFolder->_pidl, (LPITEMIDLIST)(pHCItem->_ppcei[i]), NULL);
  286.                     }
  287.                 }
  288.             }
  289.             if (fBulkDelete)
  290.             {
  291.                 _GenerateEvent(SHCNE_UPDATEDIR, pHCItem->_pHCFolder->_pidl, NULL, NULL);
  292.             }
  293.             SHChangeNotifyHandleEvents();
  294.             pHCItem->Release();
  295.             return S_OK;
  296.         }
  297.     }
  298.     return E_FAIL;
  299. }
  300. //BUGBUG: There are copies of exactly this function in SHELL32
  301. // Add the File Type page
  302. HRESULT HistCacheFolderView_OnAddPropertyPages(DWORD pv, SFVM_PROPPAGE_DATA * ppagedata)
  303. {
  304.     IShellPropSheetExt * pspse;
  305.     HRESULT hres = CoCreateInstance(CLSID_FileTypes, NULL, CLSCTX_INPROC_SERVER,
  306.                               IID_IShellPropSheetExt, (void **)&pspse);
  307.     if (SUCCEEDED(hres))
  308.     {
  309.         hres = pspse->AddPages(ppagedata->pfn, ppagedata->lParam);
  310.         pspse->Release();
  311.     }
  312.     return hres;
  313. }
  314. HRESULT HistCacheFolderView_OnGetSortDefaults(FOLDER_TYPE FolderType, int * piDirection, int * plParamSort)
  315. {
  316.     *plParamSort = IsHistory(FolderType) ? (int)ICOLH_URL_LASTVISITED : (int)ICOLC_URL_ACCESSED;
  317.     *piDirection = 1;
  318.     return S_OK;
  319. }
  320. //#define ZONES_PANE_WIDTH 120
  321. HRESULT CALLBACK HistCacheFolderView_ViewCallback(
  322.      IShellView *psvOuter,
  323.      IShellFolder *psf,
  324.      HWND hwnd,
  325.      UINT uMsg,
  326.      WPARAM wParam,
  327.      LPARAM lParam,
  328.      FOLDER_TYPE FolderType
  329.      )
  330. {
  331.     HRESULT hres = NOERROR;
  332.     switch (uMsg)
  333.     {
  334.     case DVM_GETHELPTEXT:
  335.     {
  336.         TCHAR szText[MAX_PATH];
  337.         UINT id = LOWORD(wParam);
  338.         UINT cchBuf = HIWORD(wParam);
  339.         LPTSTR pszBuf = (LPTSTR)lParam;
  340.         MLLoadString(id+IDS_MH_FIRST, szText, ARRAYSIZE(szText)-1);
  341.         // we know for a fact that this parameter is really a TCHAR
  342.         if ( IsOS( OS_NT ))
  343.         {
  344.             SHTCharToUnicode( szText, (LPWSTR) pszBuf, cchBuf );
  345.         }
  346.         else
  347.         {
  348.             SHTCharToAnsi( szText, (LPSTR) pszBuf, cchBuf );
  349.         }
  350.         break;
  351.     }
  352.     case SFVM_GETNOTIFY:
  353.     {
  354.         CHistCacheFolder *pHCFolder = NULL;
  355.         LPCITEMIDLIST pidl;
  356.         hres = psf->QueryInterface(CLSID_HistFolder, (void **) &pHCFolder);
  357.         if (SUCCEEDED(hres))
  358.         {
  359.             pidl = pHCFolder->_pidl;
  360.             pHCFolder->Release();
  361.         }
  362.         ASSERT(pidl);
  363.         *(LPCITEMIDLIST*)wParam = pidl;
  364.         *(LONG*)lParam = IsHistory(FolderType) ? ALL_CHANGES: SHCNE_DELETE|SHCNE_UPDATEDIR;
  365.     }
  366.         break;
  367.     case DVM_DIDDRAGDROP:
  368.         hres = HistCacheFolderView_DidDragDrop((IDataObject *)lParam, (DWORD)wParam);
  369.         break;
  370.     case DVM_INITMENUPOPUP:
  371.         hres = HistCacheFolderView_InitMenuPopup(hwnd, LOWORD(wParam), HIWORD(wParam), (HMENU)lParam);
  372.         break;
  373.     case DVM_INVOKECOMMAND:
  374.         HistCacheFolderView_Command(hwnd, (UINT)wParam, FolderType);
  375.         break;
  376.     case DVM_COLUMNCLICK:
  377.         hres = HistCacheFolderView_OnColumnClick(hwnd, (UINT)wParam);
  378.         break;
  379.     case DVM_MERGEMENU:
  380.         hres = HistCacheFolderView_MergeMenu(IsHistory(FolderType) ? MENU_HISTORY : MENU_CACHE, (LPQCMINFO)lParam);
  381.         break;
  382.     case DVM_DEFVIEWMODE:
  383.         *(FOLDERVIEWMODE *)lParam = FVM_DETAILS;
  384.         break;
  385.     case SFVM_ADDPROPERTYPAGES:
  386.         hres = HistCacheFolderView_OnAddPropertyPages((DWORD)wParam, (SFVM_PROPPAGE_DATA *)lParam);
  387.         break;
  388.     case SFVM_GETSORTDEFAULTS:
  389.         hres = HistCacheFolderView_OnGetSortDefaults(FolderType, (int *)wParam, (int *)lParam);
  390.         break;
  391.     case SFVM_UPDATESTATUSBAR:
  392.         ResizeStatusBar(hwnd, FALSE);
  393.         // We did not set any text; let defview do it
  394.         hres = E_NOTIMPL;
  395.         break;
  396.     case SFVM_SIZE:
  397.         ResizeStatusBar(hwnd, FALSE);
  398.         break;
  399.     case SFVM_GETPANE:
  400.         if (wParam == PANE_ZONE)
  401.             *(DWORD*)lParam = 1;
  402.         else
  403.             *(DWORD*)lParam = PANE_NONE;
  404.         break;
  405.     case SFVM_WINDOWCREATED:
  406.         ResizeStatusBar(hwnd, TRUE);
  407.         break;
  408.     case SFVM_GETZONE:
  409.         *(DWORD*)lParam = IsHistory(FolderType) ? URLZONE_LOCAL_MACHINE : URLZONE_INTERNET; // Internet by default
  410.         break;
  411.     default:
  412.         hres = E_FAIL;
  413.     }
  414.     return hres;
  415. }
  416. HRESULT CALLBACK CacheFolderView_ViewCallback(IShellView *psvOuter, IShellFolder *psf,
  417.                                               HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  418. {
  419.     return HistCacheFolderView_ViewCallback(psvOuter, psf, hwnd, uMsg, wParam, lParam, FOLDER_TYPE_Cache);
  420. }
  421. HRESULT CALLBACK HistFolderView_ViewCallback(IShellView *psvOuter, IShellFolder *psf,
  422.                                              HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  423. {
  424.     return HistCacheFolderView_ViewCallback(psvOuter, psf, hwnd, uMsg, wParam, lParam, FOLDER_TYPE_Hist);
  425. }
  426. HRESULT CALLBACK IntervalFolderView_ViewCallback(IShellView *psvOuter, IShellFolder *psf,
  427.                                              HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  428. {
  429.     return HistCacheFolderView_ViewCallback(psvOuter, psf, hwnd, uMsg, wParam, lParam, FOLDER_TYPE_HistInterval);
  430. }
  431. HRESULT CALLBACK DomainFolderView_ViewCallback(IShellView *psvOuter, IShellFolder *psf,
  432.                                              HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  433. {
  434.     return HistCacheFolderView_ViewCallback(psvOuter, psf, hwnd, uMsg, wParam, lParam, FOLDER_TYPE_HistDomain);
  435. }
  436. HRESULT HistCacheFolderView_CreateInstance(CHistCacheFolder *pHCFolder, LPCITEMIDLIST pidl, void **ppv)
  437. {
  438.     CSFV csfv;
  439.     csfv.cbSize = sizeof(csfv);
  440.     csfv.pshf = (IShellFolder *)pHCFolder;
  441.     csfv.psvOuter = NULL;
  442.     csfv.pidl = pidl;
  443.     csfv.lEvents = SHCNE_DELETE; // SHCNE_DISKEVENTS | SHCNE_ASSOCCHANGED | SHCNE_GLOBALEVENTS;
  444.     switch (pHCFolder->_foldertype)
  445.     {
  446.         case FOLDER_TYPE_Cache:
  447.             csfv.pfnCallback = CacheFolderView_ViewCallback;
  448.             break;
  449.         case FOLDER_TYPE_Hist:
  450.             csfv.pfnCallback = HistFolderView_ViewCallback;
  451.             break;
  452.         case FOLDER_TYPE_HistInterval:
  453.             csfv.pfnCallback = IntervalFolderView_ViewCallback;
  454.             break;
  455.         case FOLDER_TYPE_HistDomain:
  456.             csfv.pfnCallback = DomainFolderView_ViewCallback;
  457.             break;
  458.         default:
  459.             return E_FAIL;
  460.     }
  461.     csfv.fvm = (FOLDERVIEWMODE)0;         // Have defview restore the folder view mode
  462.     return SHCreateShellFolderViewEx(&csfv, (IShellView**)ppv); // &this->psv);
  463. }
  464. //////////////////////////////////////////////////////////////////////////////
  465. //
  466. // CHistCacheFolderEnum Object
  467. //
  468. //////////////////////////////////////////////////////////////////////////////
  469. CHistCacheFolderEnum::CHistCacheFolderEnum(DWORD grfFlags, CHistCacheFolder *pHCFolder)
  470. {
  471.     TraceMsg(DM_HSFOLDER, "hcfe - CHistCacheFolderEnum() called");
  472.     _cRef = 1;
  473.     DllAddRef();
  474.     _grfFlags = grfFlags,
  475.     _pHCFolder = pHCFolder;
  476.     pHCFolder->AddRef();
  477.     ASSERT(_hEnum             == NULL &&
  478.            _cbCurrentInterval == 0    &&
  479.            _cbIntervals       == 0    &&
  480.            _pshHashTable      == NULL &&
  481.            _polFrequentPages  == NULL &&
  482.            _pIntervalCache    == NULL);
  483. }
  484. CHistCacheFolderEnum::~CHistCacheFolderEnum()
  485. {
  486.     ASSERT(_cRef == 0);         // we should always have a zero ref count here
  487.     TraceMsg(DM_HSFOLDER, "hcfe - ~CHistCacheFolderEnum() called.");
  488.     _pHCFolder->Release();
  489.     if (_pceiWorking)
  490.     {
  491.         LocalFree(_pceiWorking);
  492.     }
  493.     if (_pIntervalCache)
  494.     {
  495.         LocalFree(_pIntervalCache);
  496.     }
  497.     if (_hEnum)
  498.     {
  499.         FindCloseUrlCache(_hEnum);
  500.         _hEnum = NULL;
  501.     }
  502.     if (_pshHashTable)
  503.         delete _pshHashTable;
  504.     if (_polFrequentPages)
  505.         delete _polFrequentPages;
  506.     if (_pstatenum)
  507.         _pstatenum->Release();
  508.     DllRelease();
  509. }
  510. HRESULT CHistCacheFolderEnum_CreateInstance(DWORD grfFlags, CHistCacheFolder *pHCFolder, IEnumIDList **ppeidl)
  511. {
  512.     TraceMsg(DM_HSFOLDER, "hcfe - CreateInstance() called.");
  513.     *ppeidl = NULL;                 // null the out param
  514.     CHistCacheFolderEnum *pHCFE = new CHistCacheFolderEnum(grfFlags, pHCFolder);
  515.     if (!pHCFE)
  516.         return E_OUTOFMEMORY;
  517.     *ppeidl = pHCFE;
  518.     return S_OK;
  519. }
  520. HRESULT CHistCacheFolderEnum::QueryInterface(REFIID riid, void **ppv)
  521. {
  522.     static const QITAB qit[] = {
  523.         QITABENT(CHistCacheFolderEnum, IEnumIDList),
  524.         { 0 },
  525.     };
  526.     return QISearch(this, qit, riid, ppv);
  527. }
  528. ULONG CHistCacheFolderEnum::AddRef(void)
  529. {
  530.     return InterlockedIncrement(&_cRef);
  531. }
  532. ULONG CHistCacheFolderEnum::Release(void)
  533. {
  534.     if (InterlockedDecrement(&_cRef))
  535.         return _cRef;
  536.     delete this;
  537.     return 0;
  538. }
  539. HRESULT CHistCacheFolderEnum::_NextHistInterval(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  540. {
  541.     HRESULT hres = S_OK;
  542.     LPCEIPIDL pcei = NULL;
  543.     TCHAR szCurrentInterval[INTERVAL_SIZE+1];
  544.     //  BUGBUG chrisfra 3/27/97 on NT cache files are per user, not so on win95.  how do
  545.     //  we manage containers on win95 if different users are specified different history
  546.     //  intervals
  547.     if (0 == _cbCurrentInterval)
  548.     {
  549.         hres = _pHCFolder->_ValidateIntervalCache();
  550.         if (SUCCEEDED(hres))
  551.         {
  552.             hres = S_OK;
  553.             ENTERCRITICAL;
  554.             if (_pIntervalCache)
  555.             {
  556.                 LocalFree(_pIntervalCache);
  557.                 _pIntervalCache = NULL;
  558.             }
  559.             if (_pHCFolder->_pIntervalCache)
  560.             {
  561.                 _pIntervalCache = (HSFINTERVAL *)LocalAlloc(LPTR,
  562.                                                             _pHCFolder->_cbIntervals*sizeof(HSFINTERVAL));
  563.                 if (_pIntervalCache == NULL)
  564.                 {
  565.                     hres = E_OUTOFMEMORY;
  566.                 }
  567.                 else
  568.                 {
  569.                     _cbIntervals = _pHCFolder->_cbIntervals;
  570.                     CopyMemory(_pIntervalCache,
  571.                                _pHCFolder->_pIntervalCache,
  572.                                _cbIntervals*sizeof(HSFINTERVAL));
  573.                 }
  574.             }
  575.             LEAVECRITICAL;
  576.         }
  577.     }
  578.     if (_pIntervalCache && _cbCurrentInterval < _cbIntervals)
  579.     {
  580.         _KeyForInterval(&_pIntervalCache[_cbCurrentInterval], szCurrentInterval,
  581.                         ARRAYSIZE(szCurrentInterval));
  582.         pcei = _CreateIdCacheFolderPidl(TRUE,
  583.                                         _pIntervalCache[_cbCurrentInterval].usSign,
  584.                                         szCurrentInterval);
  585.         _cbCurrentInterval++;
  586.     }
  587.     if (pcei)
  588.     {
  589.         rgelt[0] = (LPITEMIDLIST)pcei;
  590.         if (pceltFetched) *pceltFetched = 1;
  591.     }
  592.     else
  593.     {
  594.         if (pceltFetched) *pceltFetched = 0;
  595.         rgelt[0] = NULL;
  596.         hres = S_FALSE;
  597.     }
  598.     return hres;
  599. }
  600. // This function dispatches the different "views" on History that are possible
  601. HRESULT CHistCacheFolderEnum::_NextViewPart(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  602. {
  603.     switch(_pHCFolder->_uViewType) {
  604.     case VIEWPIDL_SEARCH:
  605.         return _NextViewPart_OrderSearch(celt, rgelt, pceltFetched);
  606.     case VIEWPIDL_ORDER_TODAY:
  607.         return _NextViewPart_OrderToday(celt, rgelt, pceltFetched);
  608.     case VIEWPIDL_ORDER_SITE:
  609.         return _NextViewPart_OrderSite(celt, rgelt, pceltFetched);
  610.     case VIEWPIDL_ORDER_FREQ:
  611.         return _NextViewPart_OrderFreq(celt, rgelt, pceltFetched);
  612.     default:
  613.         return E_NOTIMPL;
  614.     }
  615. }
  616. LPITEMIDLIST _Combine_ViewPidl(USHORT usViewType, LPITEMIDLIST pidl);
  617. // This function wraps wininet's Find(First/Next)UrlCacheEntry API
  618. // returns DWERROR code or zero if successful
  619. DWORD _FindURLCacheEntry(IN LPCTSTR                          pszCachePrefix,
  620.                          IN OUT LPINTERNET_CACHE_ENTRY_INFO  pcei,
  621.                          IN OUT HANDLE                      &hEnum,
  622.                          IN OUT LPDWORD                      pdwBuffSize)
  623. {
  624.     if (!hEnum)
  625.     {
  626.         if (! (hEnum = FindFirstUrlCacheEntry(pszCachePrefix, pcei, pdwBuffSize)) )
  627.             return GetLastError();
  628.     }
  629.     else if (!FindNextUrlCacheEntry(hEnum, pcei, pdwBuffSize))
  630.         return GetLastError();
  631.     return NOERROR;
  632. }
  633. // Thie function provides an iterator over all entries in all (MSHIST-type) buckets
  634. //   in the cache
  635. DWORD _FindURLFlatCacheEntry(
  636.                              IN HSFINTERVAL *pIntervalCache,
  637.                              IN LPTSTR       pszUserName,       // filter out cache entries owned by user
  638.                              IN BOOL         fHostEntry,        // retrieve host entries only (FALSE), or no host entries (TRUE)
  639.                              IN OUT int     &cbCurrentInterval, // should begin at the maximum number of intervals
  640.                              IN OUT LPINTERNET_CACHE_ENTRY_INFO  pcei,
  641.                              IN OUT HANDLE  &hEnum,
  642.                              IN OUT LPDWORD  pdwBuffSize
  643.                              )
  644. {
  645.     DWORD dwStoreBuffSize = *pdwBuffSize;
  646.     DWORD dwResult        = ERROR_NO_MORE_ITEMS;
  647.     while (cbCurrentInterval >= 0) {
  648.         if ((dwResult = _FindURLCacheEntry(pIntervalCache[cbCurrentInterval].szPrefix,
  649.                                            pcei, hEnum, pdwBuffSize)) != NOERROR)
  650.         {
  651.             if (dwResult == ERROR_NO_MORE_ITEMS) {
  652.                 // This bucket is done, now go get the next one
  653.                 FindCloseUrlCache(hEnum);
  654.                 hEnum = NULL;
  655.                 --cbCurrentInterval;
  656.             }
  657.             else
  658.                 break;
  659.         }
  660.         else
  661.         {
  662.             // Do requested filtering...
  663.             BOOL fIsHost = (StrStr(pcei->lpszSourceUrlName, c_szHostPrefix) == NULL);
  664.             if ( ((!pszUserName) ||  // if requested, filter username
  665.                   _FilterUserName(pcei, pIntervalCache[cbCurrentInterval].szPrefix, pszUserName)) &&
  666.                  ((!fHostEntry && !fIsHost) ||  // filter for host entries
  667.                   (fHostEntry  && fIsHost))    )
  668.             {
  669.                 break;
  670.             }
  671.         }
  672.         // reset for next iteration
  673.         *pdwBuffSize = dwStoreBuffSize;
  674.     }
  675.     return dwResult;
  676. }
  677. // This guy will search the flat cache (MSHist buckets) for a particular URL
  678. //  * This function assumes that the Interval cache is good and loaded
  679. // RETURNS: Windows Error code
  680. DWORD CHistCacheFolder::_SearchFlatCacheForUrl(LPCTSTR pszUrl, LPINTERNET_CACHE_ENTRY_INFO pcei, LPDWORD pdwBuffSize)
  681. {
  682.     TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];      // username of person logged on
  683.     DWORD dwUserNameLen = ARRAYSIZE(szUserName);
  684.     if (FAILED(_GetUserName(szUserName, dwUserNameLen)))
  685.         szUserName[0] = TEXT('');
  686.     UINT   uSuffixLen     = lstrlen(pszUrl) + lstrlen(szUserName) + 1; // extra 1 for '@'
  687.     LPTSTR pszPrefixedUrl = ((LPTSTR)LocalAlloc(LPTR, (PREFIX_SIZE + uSuffixLen + 1) * sizeof(TCHAR)));
  688.     DWORD  dwError        = ERROR_FILE_NOT_FOUND;
  689.     if (pszPrefixedUrl != NULL)
  690.     {
  691.         // pszPrefixedUrl will have the format of "PREFIX username@
  692.         wnsprintf(pszPrefixedUrl + PREFIX_SIZE, uSuffixLen + 1, TEXT("%s@%s"), szUserName, pszUrl);
  693.         for (int i =_cbIntervals - 1; i >= 0; --i) {
  694.             // memcpy doesn't null terminate
  695.             memcpy(pszPrefixedUrl, _pIntervalCache[i].szPrefix, PREFIX_SIZE * sizeof(TCHAR));
  696.             if (GetUrlCacheEntryInfo(pszPrefixedUrl, pcei, pdwBuffSize)) {
  697.                 dwError = ERROR_SUCCESS;
  698.                 break;
  699.             }
  700.             else if ( ((dwError = GetLastError()) != ERROR_FILE_NOT_FOUND) ) {
  701.                 break;
  702.             }
  703.         }
  704.         LocalFree(pszPrefixedUrl);
  705.     }
  706.     else
  707.     {
  708.         // BUGBUG return an error indicated out of memory
  709.     }
  710.     
  711.     return dwError;
  712. }
  713. //////////////////////////////////////////////////////////////////////
  714. //  Most Frequently Visited Sites;
  715. // this structure is used by the enumeration of the cache
  716. //   to get the most frequently seen sites
  717. class OrderList_CacheElement : public OrderedList::Element {
  718. public:
  719.     LPTSTR    pszUrl;
  720.     DWORD     dwHitRate;
  721.     __int64   llPriority;
  722.     int       nDaysSinceLastHit;
  723.     LPSTATURL lpSTATURL;
  724.     static   FILETIME ftToday;
  725.     static   BOOL     fInited;
  726.     OrderList_CacheElement(LPTSTR pszStr, DWORD dwHR, LPSTATURL lpSU) {
  727.         s_initToday();
  728.         ASSERT(pszStr);
  729.         pszUrl         = (pszStr ? StrDup(pszStr) : StrDup(TEXT("")));
  730.         dwHitRate      = dwHR;
  731.         lpSTATURL      = lpSU;
  732.         nDaysSinceLastHit = DAYS_DIFF(&ftToday, &(lpSTATURL->ftLastVisited));
  733.         // prevent division by zero
  734.         if (nDaysSinceLastHit < 0)
  735.             nDaysSinceLastHit = 0;
  736.         // scale division up by a little less than half of the __int64
  737.         llPriority  = ((((__int64)dwHitRate) * LONG_MAX) /
  738.                        ((__int64)(nDaysSinceLastHit + 1)));
  739.         //dPriority  = ((double)dwHitRate / (double)(nDaysSinceLastHit + 1));
  740.     }
  741.     virtual int compareWith(OrderedList::Element *pelt) {
  742.         OrderList_CacheElement *polce;
  743.         if (pelt) {
  744.             polce = reinterpret_cast<OrderList_CacheElement *>(pelt);
  745.             // we're cheating here a bit by returning 1 instead of testing
  746.             //   for equality, but that's ok...
  747.             //            return ( (dwHitRate < polce->dwHitRate) ? -1 : 1 );
  748.             return ( (llPriority < polce->llPriority) ? -1 : 1 );
  749.         }
  750.         DebugBreak();
  751.         return 0;
  752.     }
  753.     virtual ~OrderList_CacheElement() {
  754.         if (pszUrl)    LocalFree(pszUrl);
  755.         if (lpSTATURL) {
  756.             if (lpSTATURL->pwcsUrl)
  757.                 OleFree(lpSTATURL->pwcsUrl);
  758.             if (lpSTATURL->pwcsTitle)
  759.                 OleFree(lpSTATURL->pwcsTitle);
  760.             delete lpSTATURL;
  761.         }
  762.     }
  763.     /*
  764.     friend ostream& operator<<(ostream& os, OrderList_CacheElement& olce) {
  765.         os << " (" << olce.dwHitRate << "; " << olce.nDaysSinceLastHit
  766.            << " days; pri=" << olce.llPriority << ") " << olce.pszUrl;
  767.         return os;
  768.     }
  769.     */
  770.     static void s_initToday() {
  771.         if (!fInited) {
  772.             SYSTEMTIME sysTime;
  773.             GetLocalTime(&sysTime);
  774.             SystemTimeToFileTime(&sysTime, &ftToday);
  775.             fInited = TRUE;
  776.         }
  777.     }
  778. };
  779. FILETIME OrderList_CacheElement::ftToday;
  780. BOOL OrderList_CacheElement::fInited = FALSE;
  781. // caller must delete OrderedList
  782. OrderedList* CHistCacheFolderEnum::_GetMostFrequentPages() {
  783.     TCHAR      szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];      // username of person logged on
  784.     DWORD      dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;
  785.     if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  786.         szUserName[0] = TEXT('');
  787.     UINT       uUserNameLen = lstrlen(szUserName);
  788.     // reinit the current time
  789.     OrderList_CacheElement::fInited = FALSE;
  790.     IUrlHistoryPriv *pUrlHistStg = _pHCFolder->_GetHistStg();
  791.     OrderedList     *pol         = NULL;
  792.     if (pUrlHistStg)
  793.     {
  794.         IEnumSTATURL *penum = NULL;
  795.         if (SUCCEEDED(pUrlHistStg->EnumUrls(&penum)) && penum)
  796.         {
  797.             DWORD dwSites = -1;
  798.             DWORD dwType  = REG_DWORD;
  799.             DWORD dwSize  = sizeof(DWORD);
  800.             EVAL(SHRegGetUSValue(REGSTR_PATH_MAIN, c_szRegKeyTopNSites, &dwType,
  801.                                  (LPVOID)&dwSites, &dwSize, FALSE,
  802.                                  (LPVOID)&dwSites, dwSize) == ERROR_SUCCESS);
  803.             if ( (dwType != REG_DWORD)     ||
  804.                  (dwSize != sizeof(DWORD)) ||
  805.                  (dwSites < 0) )
  806.             {
  807.                 dwSites = NUM_TOP_SITES;
  808.                 SHRegSetUSValue(REGSTR_PATH_MAIN, c_szRegKeyTopNSites, REG_DWORD,
  809.                                 (LPVOID)&dwSites, dwSize, SHREGSET_HKCU);
  810.                 dwSites = NUM_TOP_SITES;
  811.             }
  812.             pol = new OrderedList(dwSites);
  813.             if (pol)
  814.             {
  815.                 STATURL *psuThis = new STATURL;
  816.                 if (psuThis)
  817.                 {
  818.                     penum->SetFilter(NULL, STATURL_QUERYFLAG_TOPLEVEL);
  819.                     while (pol) {
  820.                         psuThis->cbSize    = sizeof(STATURL);
  821.                         psuThis->pwcsUrl   = NULL;
  822.                         psuThis->pwcsTitle = NULL;
  823.                         ULONG   cFetched;
  824.                         if (SUCCEEDED(penum->Next(1, psuThis, &cFetched)) && cFetched)
  825.                         {
  826.                             // test: the url (taken from the VISITED history bucket) is a "top-level"
  827.                             //  url that would be in the MSHIST (displayed to user) history bucket
  828.                             //  things ommitted will be certain error urls and frame children pages etc...
  829.                             if ( (psuThis->dwFlags & STATURLFLAG_ISTOPLEVEL) &&
  830.                                  (psuThis->pwcsUrl)                          &&
  831.                                  (!IsErrorUrl(psuThis->pwcsUrl)) )
  832.                             {
  833.                                 UINT   uUrlLen        = lstrlenW(psuThis->pwcsUrl);
  834.                                 UINT   uPrefixLen     = HISTPREFIXLEN + uUserNameLen + 1; // '@' and ''
  835.                                 LPTSTR pszPrefixedUrl =
  836.                                     ((LPTSTR)LocalAlloc(LPTR, (uUrlLen + uPrefixLen + 1) * sizeof(TCHAR)));
  837.                                 if (pszPrefixedUrl)
  838.                                 {
  839.                                     wnsprintf(pszPrefixedUrl, uPrefixLen + 1 , TEXT("%s%s@"), c_szHistPrefix, szUserName);
  840.                                     StrCpyN(pszPrefixedUrl + uPrefixLen, psuThis->pwcsUrl, uUrlLen + 1);
  841.                                     PROPVARIANT vProp = {0};
  842.                                     if (SUCCEEDED(pUrlHistStg->GetProperty(pszPrefixedUrl + uPrefixLen,
  843.                                                                            PID_INTSITE_VISITCOUNT, &vProp)) &&
  844.                                         (vProp.vt == VT_UI4))
  845.                                     {
  846.                                         pol->insert(new OrderList_CacheElement(pszPrefixedUrl,
  847.                                                                                vProp.lVal,
  848.                                                                                psuThis));
  849.                                         // OrderList now owns this -- he'll free it
  850.                                         psuThis = new STATURL;
  851.                                         if (psuThis)
  852.                                         {
  853.                                             psuThis->cbSize    = sizeof(STATURL);
  854.                                             psuThis->pwcsUrl   = NULL;
  855.                                             psuThis->pwcsTitle = NULL;
  856.                                         }
  857.                                         else if (pol) {
  858.                                             delete pol;
  859.                                             pol = NULL;
  860.                                         }
  861.                                     }
  862.                                     LocalFree(pszPrefixedUrl);
  863.                                 }
  864.                                 else if (pol) { // couldn't allocate
  865.                                     delete pol;
  866.                                     pol = NULL;
  867.                                 }
  868.                             }
  869.                             if (psuThis && psuThis->pwcsUrl)
  870.                                 OleFree(psuThis->pwcsUrl);
  871.                             if (psuThis && psuThis->pwcsTitle)
  872.                                 OleFree(psuThis->pwcsTitle);
  873.                         }
  874.                         else // nothing more from the enumeration...
  875.                             break;
  876.                     } //while
  877.                     if (psuThis)
  878.                         delete psuThis;
  879.                 }
  880.                 else if (pol) { //allocation failed
  881.                     delete pol;
  882.                     pol = NULL;
  883.                 }
  884.             }
  885.             penum->Release();
  886.         }
  887.         /*    DWORD dwBuffSize = MAX_URLCACHE_ENTRY;
  888.               DWORD dwError; */
  889.         // This commented-out code does the same thing WITHOUT going through
  890.         //  the IUrlHistoryPriv interface, but, instead going directly
  891.         //  to wininet
  892.         /*
  893.           while ((dwError = _FindURLCacheEntry(c_szHistPrefix, _pceiWorking,
  894.           _hEnum, &dwBuffSize)) == NOERROR) {
  895.           // if its a top-level history guy && is cache entry to valid username
  896.           if ( (((HISTDATA *)_pceiWorking->lpHeaderInfo)->dwFlags & PIDISF_HISTORY) && //top-level
  897.           (_FilterUserName(_pceiWorking, c_szHistPrefix, szUserName)) ) // username is good
  898.           {
  899.           // perf:  we can avoid needlessly creating new cache elements if we're less lazy
  900.           pol->insert(new OrderList_CacheElement(_pceiWorking->lpszSourceUrlName,
  901.           _pceiWorking->dwHitRate,
  902.           _pceiWorking->LastModifiedTime));
  903.           }
  904.           dwBuffSize = MAX_URLCACHE_ENTRY;
  905.           }
  906.           ASSERT(dwError == ERROR_NO_MORE_ITEMS);
  907.           */
  908.         pUrlHistStg->Release();
  909.     } // no storage
  910.     return pol;
  911. }
  912. HRESULT CHistCacheFolderEnum::_NextViewPart_OrderFreq(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  913. {
  914.     HRESULT hRes = E_INVALIDARG;
  915.     if ( (!_polFrequentPages) && (!(_polFrequentPages = _GetMostFrequentPages())) )
  916.         return E_FAIL;
  917.     if (rgelt && pceltFetched) {
  918.         // loop to fetch as many elements as requested.
  919.         for (*pceltFetched = 0; *pceltFetched < celt;) {
  920.             // contruct a pidl out of the first element in the orderedlist cache
  921.             OrderList_CacheElement *polce = reinterpret_cast<OrderList_CacheElement *>
  922.                 (_polFrequentPages->removeFirst());
  923.             if (polce) {
  924.                 if (!(rgelt[*pceltFetched] =
  925.                       reinterpret_cast<LPITEMIDLIST>
  926.                       (_CreateHCacheFolderPidl(TRUE,
  927.                                                polce->pszUrl, polce->lpSTATURL->ftLastVisited,
  928.                                                polce->lpSTATURL,
  929.                                                polce->llPriority,
  930.                                                polce->dwHitRate))))
  931.                 {
  932.                     delete polce;
  933.                     hRes = E_OUTOFMEMORY;
  934.                     break;
  935.                 }
  936.                 ++(*pceltFetched);
  937.                 delete polce;
  938.                 hRes = S_OK;
  939.             }
  940.             else {
  941.                 hRes = S_FALSE; // no more...
  942.                 break;
  943.             }
  944.         }
  945.     }
  946.     return hRes;
  947. }
  948. // The Next method for view -- Order by Site
  949. HRESULT CHistCacheFolderEnum::_NextViewPart_OrderSite(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  950. {
  951.     DWORD      dwError         = NOERROR;
  952.     TCHAR      szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];      // username of person logged on
  953.     DWORD      dwUserNameLen   = INTERNET_MAX_USER_NAME_LENGTH + 1;  // len of this buffer
  954.     LPCTSTR    pszStrippedUrl, pszHost, pszCachePrefix = NULL;
  955.     LPITEMIDLIST  pcei         = NULL;
  956.     LPCTSTR    pszHostToMatch  = NULL;
  957.     UINT       nHostToMatchLen = 0;
  958.     if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  959.         szUserName[0] = TEXT('');
  960.     if ((!_pceiWorking) &&
  961.         (!(_pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY))))
  962.         return E_OUTOFMEMORY;
  963.     DWORD dwBuffSize = MAX_URLCACHE_ENTRY;
  964.     // load all the intervals and do some cache maintenance:
  965.     if (FAILED(_pHCFolder->_ValidateIntervalCache()))
  966.         return E_OUTOFMEMORY;
  967.     /* To get all sites, we will search all the history buckets
  968.        for "Host"-type entries.  These entries will be put into
  969.        a hash table as we enumerate so that redundant results are
  970.        not returned.                                               */
  971.     if (!_pshHashTable)
  972.     {
  973.         // start a new case-insensitive hash table
  974.         _pshHashTable = new StrHash(TRUE);
  975.         if (_pshHashTable == NULL)
  976.         {
  977.             return E_OUTOFMEMORY;
  978.         }
  979.     }
  980.     // if we are looking for individual pages within a host,
  981.     //  then we must find which host to match...
  982.     if (_pHCFolder->_uViewDepth == 1) {
  983.         LPCITEMIDLIST pidlHost = ILFindLastID(_pHCFolder->_pidl);
  984.         ASSERT(_IsValid_IDPIDL(pidlHost) &&
  985.                EQUIV_IDSIGN(((LPCEIPIDL)pidlHost)->usSign, IDDPIDL_SIGN));
  986.         pszHostToMatch = _GetURLTitle((LPCEIPIDL)pidlHost);
  987.         nHostToMatchLen = (pszHostToMatch ? lstrlen(pszHostToMatch) : 0);
  988.     }
  989.     // iterate backwards through containers so most recent
  990.     //  information gets put into the final pidl
  991.     if (!_hEnum)
  992.         _cbCurrentInterval = (_pHCFolder->_cbIntervals - 1);
  993.     while((dwError = _FindURLFlatCacheEntry(_pHCFolder->_pIntervalCache, szUserName,
  994.                                             (_pHCFolder->_uViewDepth == 1),
  995.                                             _cbCurrentInterval,
  996.                                             _pceiWorking, _hEnum, &dwBuffSize)) == NOERROR)
  997.     {
  998.         // reset for next iteration
  999.         dwBuffSize = MAX_CACHE_ENTRY_INFO_SIZE;
  1000.         // this guy takes out the "t-marcmi@" part of the URL
  1001.         pszStrippedUrl = _StripHistoryUrlToUrl(_pceiWorking->lpszSourceUrlName);
  1002.         if (_pHCFolder->_uViewDepth == 0) {
  1003.             if ((DWORD)lstrlen(pszStrippedUrl) > HOSTPREFIXLEN) {
  1004.                 pszHost = &pszStrippedUrl[HOSTPREFIXLEN];
  1005.                 // insertUnique returns non-NULL if this key already exists
  1006.                 if (_pshHashTable->insertUnique(pszHost, TRUE, reinterpret_cast<void *>(1)))
  1007.                     continue; // already given out
  1008.                 pcei = (LPITEMIDLIST)_CreateIdCacheFolderPidl(TRUE, IDDPIDL_SIGN, pszHost);
  1009.             }
  1010.             break;
  1011.         }
  1012.         else if (_pHCFolder->_uViewDepth == 1) {
  1013.             TCHAR szHost[INTERNET_MAX_HOST_NAME_LENGTH+1];
  1014.             // is this entry a doc from the host we're looking for?
  1015.             _GetURLHost(_pceiWorking, szHost, INTERNET_MAX_HOST_NAME_LENGTH, _GetLocalHost());
  1016.             if ( (!StrCmpI(szHost, pszHostToMatch)) &&
  1017.                  (!_pshHashTable->insertUnique(pszStrippedUrl,
  1018.                                                TRUE, reinterpret_cast<void *>(1))) )
  1019.             {
  1020.                 STATURL suThis;
  1021.                 HRESULT hresLocal            = E_FAIL;
  1022.                 IUrlHistoryPriv *pUrlHistStg = _pHCFolder->_GetHistStg();
  1023.                 if (pUrlHistStg) {
  1024.                     hresLocal = pUrlHistStg->QueryUrl(pszStrippedUrl, STATURL_QUERYFLAG_NOURL, &suThis);
  1025.                     pUrlHistStg->Release();
  1026.                 }
  1027.                 pcei = (LPITEMIDLIST)
  1028.                     _CreateHCacheFolderPidl(TRUE, _pceiWorking->lpszSourceUrlName,
  1029.                                             _pceiWorking->LastModifiedTime,
  1030.                                             (SUCCEEDED(hresLocal) ? &suThis : NULL), 0,
  1031.                                             _pHCFolder->_GetHitCount(_StripHistoryUrlToUrl(_pceiWorking->lpszSourceUrlName)));
  1032.                 if (SUCCEEDED(hresLocal) && suThis.pwcsTitle)
  1033.                     OleFree(suThis.pwcsTitle);
  1034.                 break;
  1035.             }
  1036.         }
  1037.     }
  1038.     if (pcei && rgelt) {
  1039.         rgelt[0] = (LPITEMIDLIST)pcei;
  1040.         if (pceltFetched)
  1041.             *pceltFetched = 1;
  1042.     }
  1043.     else {
  1044.         dwError = ERROR_NOT_ENOUGH_MEMORY;
  1045.     }
  1046.     if (dwError != NOERROR) {
  1047.         if (pceltFetched)
  1048.             *pceltFetched = 0;
  1049.         if (_hEnum)
  1050.             FindCloseUrlCache(_hEnum);
  1051.         return S_FALSE;
  1052.     }
  1053.     return S_OK;
  1054. }
  1055. // "Next" method for View by "Order seen today"
  1056. HRESULT CHistCacheFolderEnum::_NextViewPart_OrderToday(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  1057. {
  1058.     DWORD      dwError    = NOERROR;
  1059.     TCHAR      szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];      // username of person logged on
  1060.     DWORD      dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;  // len of this buffer
  1061.     LPCTSTR    pszStrippedUrl, pszHost;
  1062.     LPCEIPIDL  pcei = NULL;
  1063.     if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  1064.         szUserName[0] = TEXT('');
  1065.     if ((!_pceiWorking) &&
  1066.         (!(_pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY))))
  1067.         return E_OUTOFMEMORY;
  1068.     if (!_hEnum) {
  1069.         // load all the intervals and do some cache maintenance:
  1070.         if (FAILED(_pHCFolder->_ValidateIntervalCache()))
  1071.             return E_OUTOFMEMORY;
  1072.         // get only entries for TODAY (important part)
  1073.         SYSTEMTIME   sysTime;
  1074.         FILETIME     fileTime;
  1075.         GetLocalTime(&sysTime);
  1076.         SystemTimeToFileTime(&sysTime, &fileTime);
  1077.         if (FAILED(_pHCFolder->_GetInterval(&fileTime, FALSE, &_pIntervalCur)))
  1078.             return E_FAIL; // couldn't get interval for Today
  1079.     }
  1080.     DWORD dwBuffSize = MAX_CACHE_ENTRY_INFO_SIZE;
  1081.     while ( (dwError = _FindURLCacheEntry(_pIntervalCur->szPrefix, _pceiWorking, _hEnum,
  1082.                                           &dwBuffSize)) == NOERROR )
  1083.     {
  1084.         dwBuffSize = MAX_CACHE_ENTRY_INFO_SIZE;
  1085.         // Make sure that his cache entry belongs to szUserName
  1086.         if (_FilterUserName(_pceiWorking, _pIntervalCur->szPrefix, szUserName)) {
  1087.             // this guy takes out the "t-marcmi@" part of the URL
  1088.             pszStrippedUrl = _StripHistoryUrlToUrl(_pceiWorking->lpszSourceUrlName);
  1089.             if ((DWORD)lstrlen(pszStrippedUrl) > HOSTPREFIXLEN) {
  1090.                 pszHost = &pszStrippedUrl[HOSTPREFIXLEN];
  1091.                 if (StrCmpNI(c_szHostPrefix, pszStrippedUrl, HOSTPREFIXLEN) == 0)
  1092.                     continue; // this is a HOST placeholder, not a real doc
  1093.             }
  1094.             IUrlHistoryPriv *pUrlHistStg = _pHCFolder->_GetHistStg();
  1095.             STATURL suThis;
  1096.             HRESULT hresLocal = E_FAIL;
  1097.             if (pUrlHistStg) {
  1098.                 hresLocal = pUrlHistStg->QueryUrl(pszStrippedUrl, STATURL_QUERYFLAG_NOURL, &suThis);
  1099.                 pUrlHistStg->Release();
  1100.             }
  1101.             pcei = (LPCEIPIDL) _CreateHCacheFolderPidl(TRUE, _pceiWorking->lpszSourceUrlName,
  1102.                                                        _pceiWorking->LastModifiedTime,
  1103.                                                        (SUCCEEDED(hresLocal) ? &suThis : NULL), 0,
  1104.                                                        _pHCFolder->_GetHitCount(_StripHistoryUrlToUrl(_pceiWorking->lpszSourceUrlName)));
  1105.             if (SUCCEEDED(hresLocal) && suThis.pwcsTitle)
  1106.                 OleFree(suThis.pwcsTitle);
  1107.             break;
  1108.         }
  1109.     }
  1110.     if (pcei && rgelt) {
  1111.         rgelt[0] = (LPITEMIDLIST)pcei;
  1112.         if (pceltFetched)
  1113.             *pceltFetched = 1;
  1114.     }
  1115.     if (dwError == ERROR_NO_MORE_ITEMS) {
  1116.         if (pceltFetched)
  1117.             *pceltFetched = 0;
  1118.         if (_hEnum)
  1119.             FindCloseUrlCache(_hEnum);
  1120.         return S_FALSE;
  1121.     }
  1122.     else if (dwError == NOERROR)
  1123.         return S_OK;
  1124.     else
  1125.         return E_FAIL;
  1126. }
  1127. /***********************************************************************
  1128.   Search Mamagement Stuff:
  1129.   In order to maintian state between binds to the IShellFolder from
  1130.   the desktop, we base our state information for the searches off a
  1131.   global database (linked list) that is keyed by a timestamp generated
  1132.   when the search begins.
  1133.   This FILETIME is in the pidl for the search.
  1134.   ********************************************************************/
  1135. class _CurrentSearches {
  1136. public:
  1137.     LONG      _cRef;
  1138.     FILETIME  _ftSearchKey;
  1139.     LPWSTR    _pwszSearchTarget;
  1140.     IShellFolderSearchableCallback *_psfscOnAsyncSearch;
  1141.     CacheSearchEngine::StreamSearcher _streamsearcher;
  1142.     // Currently doing async search
  1143.     BOOL      _fSearchingAsync;
  1144.     // On next pass, kill this search
  1145.     BOOL      _fKillSwitch;
  1146.     // WARNING: DO NOT access these elements without a critical section!
  1147.     _CurrentSearches  *_pcsNext;
  1148.     _CurrentSearches  *_pcsPrev;
  1149.     static _CurrentSearches* s_pcsCurrentCacheSearchThreads;
  1150.     _CurrentSearches(FILETIME &ftSearchKey, LPCWSTR pwszSrch,
  1151.                      IShellFolderSearchableCallback *psfsc,
  1152.                      _CurrentSearches *pcsNext = s_pcsCurrentCacheSearchThreads) :
  1153.         _streamsearcher(pwszSrch),
  1154.         _fSearchingAsync(FALSE), _fKillSwitch(FALSE), _cRef(1)
  1155.     {
  1156.         _ftSearchKey      = ftSearchKey;
  1157.         _pcsNext          = pcsNext;
  1158.         _pcsPrev          = NULL;
  1159.         if (psfsc)
  1160.             psfsc->AddRef();
  1161.         _psfscOnAsyncSearch = psfsc;
  1162.         SHStrDupW(pwszSrch, &_pwszSearchTarget);
  1163.     }
  1164.     ULONG AddRef() {
  1165.         return InterlockedIncrement(&_cRef);
  1166.     }
  1167.     ULONG Release() {
  1168.         if (InterlockedDecrement(&_cRef))
  1169.             return _cRef;
  1170.         delete this;
  1171.         return 0;
  1172.     }
  1173.     // this will increment the refcount to be decremented by s_RemoveSearch
  1174.     static void s_NewSearch(_CurrentSearches *pcsNew,
  1175.                             _CurrentSearches *&pcsHead = s_pcsCurrentCacheSearchThreads)
  1176.     {
  1177.         ENTERCRITICAL;
  1178.         // make sure we're inserting at the front of the list
  1179.         ASSERT(pcsNew->_pcsNext == pcsHead);
  1180.         ASSERT(pcsNew->_pcsPrev == NULL);
  1181.         pcsNew->AddRef();
  1182.         if (pcsHead)
  1183.             pcsHead->_pcsPrev = pcsNew;
  1184.         pcsHead = pcsNew;
  1185.         LEAVECRITICAL;
  1186.     }
  1187.     static void s_RemoveSearch(_CurrentSearches *pcsRemove,
  1188.                                _CurrentSearches *&pcsHead = s_pcsCurrentCacheSearchThreads);
  1189.     // This searches for the search.
  1190.     // To find this search searcher, use the search searcher searcher :)
  1191.     static _CurrentSearches *s_FindSearch(const FILETIME &ftSearchKey,
  1192.                                           _CurrentSearches *pcsHead = s_pcsCurrentCacheSearchThreads);
  1193. protected:
  1194.     ~_CurrentSearches() {
  1195.         if (_psfscOnAsyncSearch)
  1196.             _psfscOnAsyncSearch->Release();
  1197.         CoTaskMemFree(_pwszSearchTarget);
  1198.     }
  1199. };
  1200. // A linked list of current cache searchers:
  1201. //  For multiple entries to occur in this list, the user would have to be
  1202. //  searching the cache on two or more separate queries simultaneously
  1203. _CurrentSearches *_CurrentSearches::s_pcsCurrentCacheSearchThreads = NULL;
  1204. void _CurrentSearches::s_RemoveSearch(_CurrentSearches *pcsRemove, _CurrentSearches *&pcsHead)
  1205. {
  1206.     ENTERCRITICAL;
  1207.     if (pcsRemove->_pcsPrev)
  1208.         pcsRemove->_pcsPrev->_pcsNext = pcsRemove->_pcsNext;
  1209.     else
  1210.         pcsHead = pcsRemove->_pcsNext;
  1211.     if (pcsRemove->_pcsNext)
  1212.         pcsRemove->_pcsNext->_pcsPrev = pcsRemove->_pcsPrev;
  1213.     pcsRemove->Release();
  1214.     LEAVECRITICAL;
  1215. }
  1216. // Caller: Remember to Release() the returned data!!
  1217. _CurrentSearches *_CurrentSearches::s_FindSearch(const FILETIME &ftSearchKey,
  1218.                                                  _CurrentSearches *pcsHead)
  1219. {
  1220.     ENTERCRITICAL;
  1221.     _CurrentSearches *pcsTemp = pcsHead;
  1222.     _CurrentSearches *pcsRet  = NULL;
  1223.     while (pcsTemp) {
  1224.         if (((pcsTemp->_ftSearchKey).dwLowDateTime  == ftSearchKey.dwLowDateTime) &&
  1225.             ((pcsTemp->_ftSearchKey).dwHighDateTime == ftSearchKey.dwHighDateTime))
  1226.         {
  1227.             pcsRet = pcsTemp;
  1228.             break;
  1229.         }
  1230.         pcsTemp = pcsTemp->_pcsNext;
  1231.     }
  1232.     if (pcsRet)
  1233.         pcsRet->AddRef();
  1234.     LEAVECRITICAL;
  1235.     return pcsRet;
  1236. }
  1237. /**********************************************************************/
  1238. HRESULT CHistCacheFolderEnum::_NextViewPart_OrderSearch(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) {
  1239.     HRESULT hRes      = E_FAIL;
  1240.     ULONG   uFetched  = 0;
  1241.     TCHAR   szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];
  1242.     DWORD   dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;
  1243.     if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  1244.         szUserName[0] = TEXT('');
  1245.     UINT    uUserNameLen = lstrlen(szUserName);
  1246.     if (_pstatenum == NULL) {
  1247.         // This hashtable will eventually be passed off to the background
  1248.         //  cache search thread so that it doesn't return duplicates.
  1249.         ASSERT(NULL == _pshHashTable)  // don't leak a _pshHashTable
  1250.         _pshHashTable = new StrHash(TRUE);
  1251.         if (_pshHashTable) {
  1252.             IUrlHistoryPriv *pUrlHistStg = _pHCFolder->_GetHistStg();
  1253.             if (pUrlHistStg) {
  1254.                 if (SUCCEEDED((hRes = pUrlHistStg->EnumUrls(&_pstatenum))))
  1255.                     _pstatenum->SetFilter(NULL, STATURL_QUERYFLAG_TOPLEVEL);
  1256.                 pUrlHistStg->Release();
  1257.             }
  1258.         }
  1259.     }
  1260.     else
  1261.         hRes = S_OK;
  1262.     if (SUCCEEDED(hRes)) {
  1263.         ASSERT(_pstatenum && _pshHashTable);
  1264.         for (uFetched; uFetched < celt;) {
  1265.             STATURL staturl = { 0 };
  1266.             staturl.cbSize = sizeof(staturl);
  1267.             ULONG   celtFetched = 0;
  1268.             if (SUCCEEDED((hRes = _pstatenum->Next(1, &staturl, &celtFetched)))) {
  1269.                 if (celtFetched) {
  1270.                     ASSERT(celtFetched == 1);
  1271.                     if (staturl.pwcsUrl && (staturl.dwFlags & STATURLFLAG_ISTOPLEVEL)) {
  1272.                         BOOL fMatch = FALSE;
  1273.                         // all this streamsearcher stuff is just like a 'smart' StrStr
  1274.                         CacheSearchEngine::StringStream ssUrl(staturl.pwcsUrl);
  1275.                         if ((!(fMatch =
  1276.                                (_pHCFolder->_pcsCurrentSearch->_streamsearcher).SearchCharStream(ssUrl))) &&
  1277.                             staturl.pwcsTitle)
  1278.                         {
  1279.                             CacheSearchEngine::StringStream ssTitle(staturl.pwcsTitle);
  1280.                             fMatch = (_pHCFolder->_pcsCurrentSearch->_streamsearcher).SearchCharStream(ssTitle);
  1281.                         }
  1282.                         if (fMatch){ // MATCH!
  1283.                             // Now, we have to convert the url to a prefixed (ansi, if necessary) url
  1284.                             UINT   uUrlLen        = lstrlenW(staturl.pwcsUrl);
  1285.                             UINT   uPrefixLen     = HISTPREFIXLEN + uUserNameLen + 1; // '@' and ''
  1286.                             LPTSTR pszPrefixedUrl =
  1287.                                 ((LPTSTR)LocalAlloc(LPTR, (uUrlLen + uPrefixLen + 1) * sizeof(TCHAR)));
  1288.                             if (pszPrefixedUrl){
  1289.                                 wnsprintf(pszPrefixedUrl, uPrefixLen + uUrlLen + 1,
  1290.                                           TEXT("%s%s@%ls"), c_szHistPrefix, szUserName,
  1291.                                           staturl.pwcsUrl);
  1292.                                 LPHEIPIDL pheiTemp =
  1293.                                     _CreateHCacheFolderPidl(TRUE,
  1294.                                                             pszPrefixedUrl, staturl.ftLastVisited,
  1295.                                                             &staturl, 0,
  1296.                                                             _pHCFolder->_GetHitCount(pszPrefixedUrl + uPrefixLen));
  1297.                                 if (pheiTemp) {
  1298.                                     _pshHashTable->insertUnique(pszPrefixedUrl + uPrefixLen, TRUE,
  1299.                                                                 reinterpret_cast<void *>(1));
  1300.                                     rgelt[uFetched++] = (LPITEMIDLIST)pheiTemp;
  1301.                                     hRes = S_OK;
  1302.                                 }
  1303.                                 LocalFree(pszPrefixedUrl);
  1304.                             }
  1305.                         }
  1306.                     }
  1307.                     if (staturl.pwcsUrl)
  1308.                         OleFree(staturl.pwcsUrl);
  1309.                     if (staturl.pwcsTitle)
  1310.                         OleFree(staturl.pwcsTitle);
  1311.                 }
  1312.                 else {
  1313.                     hRes = S_FALSE;
  1314.                     // Addref this for the ThreadProc who then frees it...
  1315.                     AddRef();
  1316. #ifdef DEBUG
  1317.                     // The memory that goes in as an input parameter is all freed on a different thread
  1318.                     // Hence - we need to remove them from the memlist and put them on the mem list
  1319.                     // of the other thread when we get there
  1320.                     remove_from_memlist((LPVOID)this); // Make sure this is Released before exiting
  1321.                                                         // the thread proc
  1322.                     if(_pHCFolder){
  1323.                         remove_from_memlist((LPVOID)_pHCFolder);
  1324.                     }
  1325.                         
  1326.                     if(_pshHashTable){
  1327.                         _pshHashTable->_RemoveHashNodesFromMemList();
  1328.                         remove_from_memlist((LPVOID)_pshHashTable);
  1329.                     }
  1330.                     if(_polFrequentPages){
  1331.                         _polFrequentPages->_RemoveElementsFromMemlist();
  1332.                         remove_from_memlist((LPVOID)_polFrequentPages);
  1333.                     }
  1334.                     if(_pstatenum){
  1335.                         remove_from_memlist((LPVOID)_pstatenum);
  1336.                     }
  1337.                     
  1338.                     
  1339.        
  1340. #endif // DEBUG
  1341.                     SHQueueUserWorkItem((LPTHREAD_START_ROUTINE)s_CacheSearchThreadProc,
  1342.                                         (LPVOID)this,
  1343.                                         0,
  1344.                                         (DWORD_PTR)NULL,
  1345.                                         (DWORD_PTR *)NULL,
  1346.                                         "shdocvw.dll",
  1347.                                         0
  1348.                                         );
  1349.                     break;
  1350.                 }
  1351.             } // succeeded getnext url
  1352.         } //for
  1353.         if (pceltFetched)
  1354.             *pceltFetched = uFetched;
  1355.     } // succeeded initalising
  1356.     return hRes;
  1357. }
  1358. // helper function for s_CacheSearchThreadProc
  1359. BOOL_PTR CHistCacheFolderEnum::s_DoCacheSearch(LPINTERNET_CACHE_ENTRY_INFO pcei,
  1360.                                            LPTSTR pszUserName, UINT uUserNameLen,
  1361.                                            CHistCacheFolderEnum *penum,
  1362.                                            _CurrentSearches *pcsThisThread, IUrlHistoryPriv *pUrlHistStg)
  1363. {
  1364.     BOOL_PTR   fFound = FALSE;
  1365.     LPTSTR pszTextHeader;
  1366.     // The header contains "Content-type: text/*"
  1367.     if (pcei->lpHeaderInfo && (pszTextHeader = StrStrI(pcei->lpHeaderInfo, c_szTextHeader)))
  1368.     {
  1369.         // in some cases, urls in the cache differ from urls in the history
  1370.         //  by only the trailing slash -- we strip it out and test both
  1371.         UINT uUrlLen = lstrlen(pcei->lpszSourceUrlName);
  1372.         if (uUrlLen && (pcei->lpszSourceUrlName[uUrlLen - 1] == TEXT('/')))
  1373.         {
  1374.             pcei->lpszSourceUrlName[uUrlLen - 1] = TEXT('');
  1375.             fFound = (BOOL_PTR)(penum->_pshHashTable->retrieve(pcei->lpszSourceUrlName));
  1376.             pcei->lpszSourceUrlName[uUrlLen - 1] = TEXT('/');
  1377.         }
  1378.         DWORD dwSize = MAX_URLCACHE_ENTRY;
  1379.         // see if its already been found and added...
  1380.         if ((!fFound) && !(penum->_pshHashTable->retrieve(pcei->lpszSourceUrlName)))
  1381.         {
  1382.             BOOL fIsHTML = !StrCmpNI(pszTextHeader + TEXTHEADERLEN, c_szHTML, HTMLLEN);
  1383.             // Now, try to find the url in history...
  1384.             STATURL staturl;
  1385.             HRESULT hresLocal;
  1386.             hresLocal = pUrlHistStg->QueryUrl(pcei->lpszSourceUrlName, STATFLAG_NONAME, &staturl);
  1387.             if (hresLocal == S_OK)
  1388.             {
  1389.                 HANDLE hCacheStream;
  1390.                 hCacheStream = RetrieveUrlCacheEntryStream(pcei->lpszSourceUrlName, pcei, &dwSize, FALSE, 0);
  1391.                 if (hCacheStream)
  1392.                 {
  1393.                     if (CacheSearchEngine::SearchCacheStream(pcsThisThread->_streamsearcher,
  1394.                                                              hCacheStream, fIsHTML)) {
  1395.                         EVAL(UnlockUrlCacheEntryStream(hCacheStream, 0));
  1396.                         // Prefix the url so that we can create a pidl out of it -- for now, we will
  1397.                         //  prefix it with "Visited: ", but "Bogus: " may be more appropriate.
  1398.                         UINT uUrlLen    = lstrlen(pcei->lpszSourceUrlName);
  1399.                         UINT uPrefixLen = HISTPREFIXLEN + uUserNameLen + 1; // '@' and ''
  1400.                         UINT uBuffSize  = uUrlLen + uPrefixLen + 1;
  1401.                         LPTSTR pszPrefixedUrl =
  1402.                             ((LPTSTR)LocalAlloc(LPTR, uBuffSize * sizeof(TCHAR)));
  1403.                         if (pszPrefixedUrl)
  1404.                         {
  1405.                             wnsprintf(pszPrefixedUrl, uBuffSize, TEXT("%s%s@%s"), c_szHistPrefix, pszUserName,
  1406.                                       pcei->lpszSourceUrlName);
  1407.                             // Create a pidl for this url
  1408.                             LPITEMIDLIST pidlFound =
  1409.                                 (LPITEMIDLIST)
  1410.                                 penum->_pHCFolder->_CreateHCacheFolderPidlFromUrl(FALSE, pszPrefixedUrl);
  1411.                             if (pidlFound)
  1412.                             {
  1413.                                 LPITEMIDLIST pidlNotify = ILCombine(penum->_pHCFolder->_pidl, pidlFound);
  1414.                                 if (pidlNotify) 
  1415.                                 {
  1416.                                     // add the item to the results list...
  1417.                                     /* without the flush, the shell will coalesce these and turn
  1418.                                        them info SHChangeNotify(SHCNE_UPDATEDIR,..), which will cause nsc
  1419.                                        to do an EnumObjects(), which will start the search up again and again...
  1420.                                        */
  1421.                                     SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST | SHCNF_FLUSH, pidlNotify, NULL);
  1422.                                     ILFree(pidlNotify);
  1423.                                     fFound = TRUE;
  1424.                                 }
  1425.                                 LocalFree(pidlFound);
  1426.                             }
  1427.                             LocalFree(pszPrefixedUrl);
  1428.                         }
  1429.                     }
  1430.                     else
  1431.                         EVAL(UnlockUrlCacheEntryStream(hCacheStream, 0));
  1432.                 }
  1433.             }
  1434.             else
  1435.                 TraceMsg(DM_CACHESEARCH, "In Cache -- Not In History: %s", pcei->lpszSourceUrlName);
  1436.         }
  1437.     }
  1438.     return fFound;
  1439. }
  1440. DWORD WINAPI CHistCacheFolderEnum::s_CacheSearchThreadProc(CHistCacheFolderEnum *penum)
  1441. {
  1442.     TCHAR   szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];
  1443.     DWORD   dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;
  1444. #ifdef DEBUG
  1445.     // The memory that goes in as an input parameter is all freed on this thread
  1446.     // Hence - we need to put them on this mem list
  1447.     ASSERT(penum);
  1448.     DbgAddToMemList((LPVOID)penum);// Make sure penum is Release'd before exiting
  1449.                                    // the thread proc
  1450.     if(penum->_pHCFolder){
  1451.         DbgAddToMemList((LPVOID)(penum->_pHCFolder));
  1452.     }
  1453.     if(penum->_pshHashTable)
  1454.     {
  1455.         DbgAddToMemList((LPVOID)(penum->_pshHashTable));
  1456.         penum->_pshHashTable->_AddHashNodesFromMemList();
  1457.     }
  1458.     if(penum->_polFrequentPages)
  1459.     {
  1460.         DbgAddToMemList((LPVOID)(penum->_polFrequentPages));   
  1461.         penum->_polFrequentPages->_AddElementsToMemlist();
  1462.     }
  1463.     if(penum->_pstatenum)
  1464.     {
  1465.         DbgAddToMemList((LPVOID)(penum->_pstatenum));
  1466.     }
  1467. #endif // DEBUG
  1468.     if (FAILED(penum->_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  1469.         szUserName[0] = TEXT('');
  1470.     UINT    uUserNameLen = lstrlen(szUserName);
  1471.     BOOL    fNoConflictingSearch = TRUE;
  1472.     _CurrentSearches *pcsThisThread = NULL;
  1473.     IUrlHistoryPriv *pUrlHistStg = penum->_pHCFolder->_GetHistStg();
  1474.     if (pUrlHistStg)
  1475.     {
  1476.         pcsThisThread = _CurrentSearches::s_FindSearch(penum->_pHCFolder->_pcsCurrentSearch->_ftSearchKey);
  1477.         if (pcsThisThread)
  1478.         {
  1479.             // if no one else is doing the same search
  1480.             if (FALSE == InterlockedExchange((LONG *)&(pcsThisThread->_fSearchingAsync), TRUE))
  1481.             {
  1482.                 if (pcsThisThread->_psfscOnAsyncSearch)
  1483.                     pcsThisThread->_psfscOnAsyncSearch->RunBegin(0);
  1484.                 BYTE ab[MAX_URLCACHE_ENTRY];
  1485.                 LPINTERNET_CACHE_ENTRY_INFO pcei = (LPINTERNET_CACHE_ENTRY_INFO)(&ab);
  1486.                 DWORD dwSize = MAX_URLCACHE_ENTRY;
  1487.                 HANDLE hCacheEnum = FindFirstUrlCacheEntry(NULL, pcei, &dwSize);
  1488.                 if (hCacheEnum)
  1489.                 {
  1490.                     while(!(pcsThisThread->_fKillSwitch))
  1491.                     {
  1492.                         s_DoCacheSearch(pcei, szUserName, uUserNameLen, penum, pcsThisThread, pUrlHistStg);
  1493.                         dwSize = MAX_URLCACHE_ENTRY;
  1494.                         if (!FindNextUrlCacheEntry(hCacheEnum, pcei, &dwSize))
  1495.                         {
  1496.                             ASSERT(GetLastError() == ERROR_NO_MORE_ITEMS);
  1497.                             break;
  1498.                         }
  1499.                     }
  1500.                     FindCloseUrlCache(hCacheEnum);
  1501.                 }
  1502.                 if (pcsThisThread->_psfscOnAsyncSearch)
  1503.                     pcsThisThread->_psfscOnAsyncSearch->RunEnd(0);
  1504.                 pcsThisThread->_fSearchingAsync = FALSE; // It's been removed - no chance of
  1505.                                                          // a race condition
  1506.             }
  1507.             pcsThisThread->Release();
  1508.         }
  1509.         ATOMICRELEASE(pUrlHistStg);
  1510.     }
  1511.     ATOMICRELEASE(penum);
  1512.     return 0;
  1513. }
  1514. //
  1515. //  this gets the local host name as known by the shell
  1516. //  by default assume "My Computer" or whatever
  1517. //
  1518. void _GetLocalHost(LPTSTR psz, DWORD cch)
  1519. {
  1520.     *psz = 0;
  1521.     IShellFolder* psf;
  1522.     if (SUCCEEDED(SHGetDesktopFolder(&psf)))
  1523.     {
  1524.         WCHAR sz[GUIDSTR_MAX + 3];
  1525.         sz[0] = sz[1] = TEXT(':');
  1526.         SHStringFromGUIDW(CLSID_MyComputer, sz+2, SIZECHARS(sz)-2);
  1527.         LPITEMIDLIST pidl;
  1528.         if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, sz, NULL, &pidl, NULL)))
  1529.         {
  1530.             STRRET sr;
  1531.             if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sr)))
  1532.                 StrRetToBuf(&sr, pidl, psz, cch);
  1533.             ILFree(pidl);
  1534.         }
  1535.         psf->Release();
  1536.     }
  1537.     if (!*psz)
  1538.         MLLoadString(IDS_NOTNETHOST, psz, cch);
  1539. }
  1540. LPCTSTR CHistCacheFolderEnum::_GetLocalHost(void)
  1541. {
  1542.     if (!*_szLocalHost)
  1543.         ::_GetLocalHost(_szLocalHost, SIZECHARS(_szLocalHost));
  1544.     return _szLocalHost;
  1545. }
  1546. //////////////////////////////////
  1547. //
  1548. // IEnumIDList Methods
  1549. //
  1550. HRESULT CHistCacheFolderEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  1551. {
  1552.     HRESULT hres             = S_FALSE;
  1553.     DWORD   dwBuffSize;
  1554.     DWORD   dwError;
  1555.     LPTSTR  pszSearchPattern = NULL;
  1556.     TCHAR   szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];      // username of person logged on
  1557.     DWORD   dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;  // len of this buffer
  1558.     TCHAR   szHistSearchPattern[PREFIX_SIZE + 1];               // search pattern for history items
  1559.     TCHAR   szHost[INTERNET_MAX_HOST_NAME_LENGTH+1];
  1560.     TraceMsg(DM_HSFOLDER, "hcfe - Next() called.");
  1561.     if (_pHCFolder->_uViewType)
  1562.         return _NextViewPart(celt, rgelt, pceltFetched);
  1563.     if ((IsLeaf(_pHCFolder->_foldertype) && 0 == (SHCONTF_NONFOLDERS & _grfFlags)) ||
  1564.         (!IsLeaf(_pHCFolder->_foldertype) && 0 == (SHCONTF_FOLDERS & _grfFlags)))
  1565.     {
  1566.         dwError = 0xFFFFFFFF;
  1567.         goto exitPoint;
  1568.     }
  1569.     if (FOLDER_TYPE_Hist == _pHCFolder->_foldertype)
  1570.     {
  1571.         return _NextHistInterval(celt, rgelt, pceltFetched);
  1572.     }
  1573.     if (_pceiWorking == NULL)
  1574.     {
  1575.         _pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY);
  1576.         if (_pceiWorking == NULL)
  1577.         {
  1578.             dwError = ERROR_NOT_ENOUGH_MEMORY;
  1579.             goto exitPoint;
  1580.         }
  1581.     }
  1582.     // Set up things to enumerate history items, if appropriate, otherwise,
  1583.     // we'll just pass in NULL and enumerate all items as before.
  1584.     if (IsHistory(_pHCFolder->_foldertype))
  1585.     {
  1586.         if (!_hEnum)
  1587.         {
  1588.             if (FAILED(_pHCFolder->_ValidateIntervalCache()))
  1589.             {
  1590.                 dwError = ERROR_NOT_ENOUGH_MEMORY;
  1591.                 goto exitPoint;
  1592.             }
  1593.         }
  1594.         if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  1595.             szUserName[0] = TEXT('');
  1596.         StrCpyN(szHistSearchPattern, _pHCFolder->_pszCachePrefix, ARRAYSIZE(szHistSearchPattern));
  1597.         // BUGBUG: We can't pass in the whole search pattern that we want,
  1598.         // because FindFirstUrlCacheEntry is busted.  It will only look at the
  1599.         // prefix if there is a cache container for that prefix.  So, we can
  1600.         // pass in "Visited: " and enumerate all the history items in the cache,
  1601.         // but then we need to pull out only the ones with the correct username.
  1602.         // StrCpy(szHistSearchPattern, szUserName);
  1603.         pszSearchPattern = szHistSearchPattern;
  1604.     }
  1605. TryAgain:
  1606.     dwBuffSize = MAX_URLCACHE_ENTRY;
  1607.     dwError = NOERROR;
  1608.     if (!_hEnum) // _hEnum maintains our state as we iterate over all the cache entries
  1609.     {
  1610.        _hEnum = FindFirstUrlCacheEntry(pszSearchPattern, _pceiWorking, &dwBuffSize);
  1611.        if (!_hEnum)
  1612.            dwError = GetLastError();
  1613.     }
  1614.     else if (!FindNextUrlCacheEntry(_hEnum, _pceiWorking, &dwBuffSize))
  1615.     {
  1616.         dwError = GetLastError();
  1617.     }
  1618.     if (NOERROR == dwError)
  1619.     {
  1620.         LPCEIPIDL pcei = NULL;
  1621.         if (!IsHistory(_pHCFolder->_foldertype)) // not a history-type entry
  1622.         {
  1623.             if ((_pceiWorking->CacheEntryType & URLHISTORY_CACHE_ENTRY) == URLHISTORY_CACHE_ENTRY)
  1624.                 goto TryAgain;
  1625.             pcei = _CreateBuffCacheFolderPidl(TRUE, dwBuffSize, _pceiWorking);
  1626.             if (pcei != NULL)
  1627.                 _GetFileTypeInternal(pcei, pcei->szTypeName, ARRAYSIZE(pcei->szTypeName));
  1628.         }
  1629.         else   // the FolderType is a History-type entry
  1630.         {
  1631.             TCHAR szTempStrippedUrl[MAX_URL_STRING];
  1632.             LPCTSTR pszStrippedUrl;
  1633.             BOOL fIsHost;
  1634.             LPCTSTR pszHost;
  1635.         //mm:  Make sure that this cache entry belongs to szUserName (relevant to Win95)
  1636.             if (!_FilterUserName(_pceiWorking, _pHCFolder->_pszCachePrefix, szUserName))
  1637.                 goto TryAgain;
  1638.             StrCpyN(szTempStrippedUrl, _pceiWorking->lpszSourceUrlName, ARRAYSIZE(szTempStrippedUrl));
  1639.             pszStrippedUrl = _StripHistoryUrlToUrl(szTempStrippedUrl);
  1640.             if ((DWORD)lstrlen(pszStrippedUrl) > HOSTPREFIXLEN)
  1641.             {
  1642.                 pszHost = &pszStrippedUrl[HOSTPREFIXLEN];
  1643.                 fIsHost = !StrCmpNI(c_szHostPrefix, pszStrippedUrl, HOSTPREFIXLEN);
  1644.             }
  1645.             else
  1646.             {
  1647.                 fIsHost = FALSE;
  1648.             }
  1649.         //mm:  this is most likely domains:
  1650.             if (FOLDER_TYPE_HistInterval == _pHCFolder->_foldertype) // return unique domains
  1651.             {
  1652.                 if (!fIsHost)
  1653.                     goto TryAgain;
  1654.                 pcei = _CreateIdCacheFolderPidl(TRUE, IDDPIDL_SIGN, pszHost);
  1655.             }
  1656.             else if (NULL != _pHCFolder->_pszDomain) //mm: this must be docs
  1657.             {
  1658.                 TCHAR szSourceUrl[MAX_URL_STRING];
  1659.                 STATURL suThis;
  1660.                 HRESULT hresLocal = E_FAIL;
  1661.                 IUrlHistoryPriv *pUrlHistStg = NULL;
  1662.                 if (fIsHost)
  1663.                     goto TryAgain;
  1664.                 //  Filter domain in history view!
  1665.                 _GetURLHost(_pceiWorking, szHost, INTERNET_MAX_HOST_NAME_LENGTH, _GetLocalHost());
  1666.                 if (StrCmpI(szHost, _pHCFolder->_pszDomain)) //mm: is this in our domain?!
  1667.                     goto TryAgain;
  1668.                 pUrlHistStg = _pHCFolder->_GetHistStg();
  1669.                 if (pUrlHistStg)
  1670.                 {
  1671.                     CHAR szTempUrl[MAX_URL_STRING];
  1672.                     SHTCharToAnsi(pszStrippedUrl, szTempUrl, ARRAYSIZE(szTempUrl));
  1673.                     hresLocal = pUrlHistStg->QueryUrlA(szTempUrl, STATURL_QUERYFLAG_NOURL, &suThis);
  1674.                     pUrlHistStg->Release();
  1675.                 }
  1676.                 StrCpyN(szSourceUrl, _pceiWorking->lpszSourceUrlName, ARRAYSIZE(szSourceUrl));
  1677.                 pcei = (LPCEIPIDL) _CreateHCacheFolderPidl(TRUE,
  1678.                                                            szSourceUrl,
  1679.                                                            _pceiWorking->LastModifiedTime,
  1680.                                                            (SUCCEEDED(hresLocal) ? &suThis : NULL), 0,
  1681.                                                            _pHCFolder->_GetHitCount(_StripHistoryUrlToUrl(szSourceUrl)));
  1682.                 if (SUCCEEDED(hresLocal) && suThis.pwcsTitle)
  1683.                     OleFree(suThis.pwcsTitle);
  1684.             }
  1685.         }
  1686.         if (pcei)
  1687.         {
  1688.             rgelt[0] = (LPITEMIDLIST)pcei;
  1689.            if (pceltFetched)
  1690.                *pceltFetched = 1;
  1691.         }
  1692.         else
  1693.         {
  1694.             dwError = ERROR_NOT_ENOUGH_MEMORY;
  1695.         }
  1696.     }
  1697. exitPoint:
  1698.     if (dwError != NOERROR)
  1699.     {
  1700.         if (_hEnum)
  1701.         {
  1702.             FindCloseUrlCache(_hEnum);
  1703.             _hEnum = NULL;
  1704.         }
  1705.         if (pceltFetched)
  1706.             *pceltFetched = 0;
  1707.         rgelt[0] = NULL;
  1708.         hres = S_FALSE;
  1709.     }
  1710.     else
  1711.     {
  1712.         hres = NOERROR;
  1713.     }
  1714.     return hres;
  1715. }
  1716. HRESULT CHistCacheFolderEnum::Skip(ULONG celt)
  1717. {
  1718.     TraceMsg(DM_HSFOLDER, "hcfe - Skip() called.");
  1719.     return E_NOTIMPL;
  1720. }
  1721. HRESULT CHistCacheFolderEnum::Reset()
  1722. {
  1723.     TraceMsg(DM_HSFOLDER, "hcfe - Reset() called.");
  1724.     return E_NOTIMPL;
  1725. }
  1726. HRESULT CHistCacheFolderEnum::Clone(IEnumIDList **ppenum)
  1727. {
  1728.     TraceMsg(DM_HSFOLDER, "hcfe - Clone() called.");
  1729.     return E_NOTIMPL;
  1730. }
  1731. //////////////////////////////////////////////////////////////////////////////
  1732. //
  1733. // CHistCacheFolder Object
  1734. //
  1735. //////////////////////////////////////////////////////////////////////////////
  1736. CHistCacheFolder::CHistCacheFolder(FOLDER_TYPE FolderType)
  1737. {
  1738.     TraceMsg(DM_HSFOLDER, "hcf - CHistCacheFolder() called.");
  1739.     _cRef = 1;
  1740.     _foldertype = FolderType;
  1741.     ASSERT( _uViewType  == 0 &&
  1742.             _uViewDepth  == 0 &&
  1743.             _pszCachePrefix == NULL &&
  1744.             _pszDomain == NULL &&
  1745.             _cbIntervals == 0 &&
  1746.             _pIntervalCache == NULL &&
  1747.             _fValidatingCache == FALSE &&
  1748.             _dwIntervalCached == 0 &&
  1749.             _ftDayCached.dwHighDateTime == 0 &&
  1750.             _ftDayCached.dwLowDateTime == 0 &&
  1751.             _pidl == NULL );
  1752.     DllAddRef();
  1753. }
  1754. CHistCacheFolder::~CHistCacheFolder()
  1755. {
  1756.     ASSERT(_cRef == 0);                 // should always have zero
  1757.     TraceMsg(DM_HSFOLDER, "hcf - ~CHistCacheFolder() called.");
  1758.     if (_pIntervalCache)
  1759.     {
  1760.         LocalFree(_pIntervalCache);
  1761.     }
  1762.     if (_pszCachePrefix)
  1763.     {
  1764.         LocalFree(_pszCachePrefix);
  1765.     }
  1766.     if (_pszDomain)
  1767.     {
  1768.         LocalFree(_pszDomain);
  1769.     }
  1770.     if (_pidl)
  1771.         ILFree(_pidl);
  1772.     if (_pUrlHistStg)
  1773.     {
  1774.         _pUrlHistStg->Release();
  1775.         _pUrlHistStg = NULL;
  1776.     }
  1777.     if (_pcsCurrentSearch)
  1778.         _pcsCurrentSearch->Release();
  1779.     DllRelease();
  1780. }
  1781. LPITEMIDLIST _Combine_ViewPidl(USHORT usViewType, LPITEMIDLIST pidl)
  1782. {
  1783.     LPITEMIDLIST pidlResult = NULL;
  1784.     LPVIEWPIDL pviewpidl = (LPVIEWPIDL)SHAlloc(sizeof(VIEWPIDL) + sizeof(USHORT));
  1785.     if (pviewpidl)
  1786.     {
  1787.         memset(pviewpidl, 0, sizeof(VIEWPIDL) + sizeof(USHORT));
  1788.         pviewpidl->cb         = sizeof(VIEWPIDL);
  1789.         pviewpidl->usSign     = VIEWPIDL_SIGN;
  1790.         pviewpidl->usViewType = usViewType;
  1791.         ASSERT(pviewpidl->usExtra == 0);//pcei->usSign;
  1792.         if (pidl) 
  1793.         {
  1794.             pidlResult = ILCombine((LPITEMIDLIST)pviewpidl, pidl);
  1795.             SHFree(pviewpidl);
  1796.         }
  1797.         else
  1798.             pidlResult = (LPITEMIDLIST)pviewpidl;
  1799.     }
  1800.     return pidlResult;
  1801. }
  1802. STDMETHODIMP CHistFolder::_GetDetail(LPCITEMIDLIST pidl, UINT iColumn, LPTSTR pszStr, UINT cchStr)
  1803. {
  1804.     *pszStr = 0;
  1805.     switch (iColumn)
  1806.     {
  1807.     case ICOLH_URL_NAME:
  1808.         if (_IsLeaf())
  1809.             StrCpyN(pszStr, _StripHistoryUrlToUrl(HCPidlToSourceUrl(pidl)), cchStr);
  1810.         else
  1811.             _GetURLDispName((LPCEIPIDL)pidl, pszStr, cchStr);
  1812.         break;
  1813.     case ICOLH_URL_TITLE:
  1814.         _GetHistURLDispName((LPHEIPIDL)pidl, pszStr, cchStr);
  1815.         break;
  1816.     case ICOLH_URL_LASTVISITED:
  1817.         FileTimeToDateTimeStringInternal(&((LPHEIPIDL)pidl)->ftModified, pszStr, cchStr, TRUE);
  1818.         break;
  1819.     }
  1820.     return NOERROR;
  1821. }
  1822. HRESULT CHistFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi)
  1823. {
  1824.     HRESULT hres;
  1825.     const COLSPEC *pcol;
  1826.     UINT nCols;
  1827.     if (_foldertype == FOLDER_TYPE_Hist)
  1828.     {
  1829.         pcol = s_HistIntervalFolder_cols;
  1830.         nCols = ARRAYSIZE(s_HistIntervalFolder_cols);
  1831.     }
  1832.     else if (_foldertype == FOLDER_TYPE_HistInterval)
  1833.     {
  1834.         pcol = s_HistHostFolder_cols;
  1835.         nCols = ARRAYSIZE(s_HistHostFolder_cols);
  1836.     }
  1837.     else
  1838.     {
  1839.         pcol = s_HistFolder_cols;
  1840.         nCols = ARRAYSIZE(s_HistFolder_cols);
  1841.     }
  1842.     if (pidl == NULL)
  1843.     {
  1844.         if (iColumn < nCols)
  1845.         {
  1846.             TCHAR szTemp[128];
  1847.             pdi->fmt = pcol[iColumn].iFmt;
  1848.             pdi->cxChar = pcol[iColumn].cchCol;
  1849.             MLLoadString(pcol[iColumn].ids, szTemp, ARRAYSIZE(szTemp));
  1850.             hres = StringToStrRet(szTemp, &pdi->str);
  1851.         }
  1852.         else
  1853.             hres = E_FAIL;  // enum done
  1854.     }
  1855.     else
  1856.     {
  1857.         // Make sure the pidl is dword aligned.
  1858.      if(iColumn >= nCols)
  1859.          hres = E_FAIL;
  1860.      else
  1861.      {
  1862.             BOOL fRealigned;
  1863.             hres = AlignPidl(&pidl, &fRealigned);
  1864.             if (SUCCEEDED(hres) )
  1865.             {
  1866.                 TCHAR szTemp[MAX_URL_STRING];
  1867.                 hres = _GetDetail(pidl, iColumn, szTemp, ARRAYSIZE(szTemp));
  1868.                 if (SUCCEEDED(hres))
  1869.                     hres = StringToStrRet(szTemp, &pdi->str);
  1870.             }
  1871.             if (fRealigned)
  1872.                 FreeRealignedPidl(pidl);
  1873.         }
  1874.     }
  1875.     return hres;
  1876. }
  1877. STDMETHODIMP CCacheFolder::_GetDetail(LPCITEMIDLIST pidl, UINT iColumn, LPTSTR pszStr, UINT cchStr)
  1878. {
  1879.     switch (iColumn) {
  1880.     case ICOLC_URL_SHORTNAME:
  1881.         _GetCacheItemTitle((LPCEIPIDL)pidl, pszStr, cchStr);
  1882.         break;
  1883.     case ICOLC_URL_NAME:
  1884.         StrCpyN(pszStr, HCPidlToSourceUrl(pidl), cchStr);
  1885.         break;
  1886.     case ICOLC_URL_TYPE:
  1887.         StrCpyN(pszStr, ((LPCEIPIDL)pidl)->szTypeName, cchStr);
  1888.         break;
  1889.     case ICOLC_URL_SIZE:
  1890.         StrFormatKBSize(((LPCEIPIDL)pidl)->cei.dwSizeLow, pszStr, cchStr);
  1891.         break;
  1892.     case ICOLC_URL_EXPIRES:
  1893.         FileTimeToDateTimeStringInternal(&((LPCEIPIDL)pidl)->cei.ExpireTime, pszStr, cchStr, FALSE);
  1894.         break;
  1895.     case ICOLC_URL_ACCESSED:
  1896.         FileTimeToDateTimeStringInternal(&((LPCEIPIDL)pidl)->cei.LastAccessTime, pszStr, cchStr, FALSE);
  1897.         break;
  1898.     case ICOLC_URL_MODIFIED:
  1899.         FileTimeToDateTimeStringInternal(&((LPCEIPIDL)pidl)->cei.LastModifiedTime, pszStr, cchStr, FALSE);
  1900.         break;
  1901.     case ICOLC_URL_LASTSYNCED:
  1902.         FileTimeToDateTimeStringInternal(&((LPCEIPIDL)pidl)->cei.LastSyncTime, pszStr, cchStr, FALSE);
  1903.         break;
  1904.     }
  1905.     return NOERROR;
  1906. }
  1907. HRESULT CCacheFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi)
  1908. {
  1909.     HRESULT hres;
  1910.     if (pidl == NULL)
  1911.     {
  1912.         if (iColumn < ICOLC_URL_MAX)
  1913.         {
  1914.             TCHAR szTemp[128];
  1915.             MLLoadString(s_CacheFolder_cols[iColumn].ids, szTemp, ARRAYSIZE(szTemp));
  1916.             pdi->fmt = s_CacheFolder_cols[iColumn].iFmt;
  1917.             pdi->cxChar = s_CacheFolder_cols[iColumn].cchCol;
  1918.             hres = StringToStrRet(szTemp, &pdi->str);
  1919.         }
  1920.         else
  1921.             hres = E_FAIL;  // enum done
  1922.     }
  1923.     else
  1924.     {
  1925.         TCHAR szTemp[MAX_URL_STRING];
  1926.         hres = _GetDetail(pidl, iColumn, szTemp, ARRAYSIZE(szTemp));
  1927.         if (SUCCEEDED(hres))
  1928.             hres = StringToStrRet(szTemp, &pdi->str);
  1929.     }
  1930.     return hres;
  1931. }
  1932. STDAPI HistFolder_CreateInstance(IUnknown* punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  1933. {
  1934.     *ppunk = NULL;                     // null the out param
  1935.     if (punkOuter)
  1936.         return CLASS_E_NOAGGREGATION;
  1937.     CHistFolder *phist = new CHistFolder(FOLDER_TYPE_Hist);
  1938.     if (!phist)
  1939.         return E_OUTOFMEMORY;
  1940.     *ppunk = SAFECAST(phist, IShellFolder2*);
  1941.     return S_OK;
  1942. }
  1943. STDAPI CacheFolder_CreateInstance(IUnknown* punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  1944. {
  1945.     *ppunk = NULL;                     // null the out param
  1946.     if (punkOuter)
  1947.         return CLASS_E_NOAGGREGATION;
  1948.     CCacheFolder *pcache = new CCacheFolder(FOLDER_TYPE_Cache);
  1949.     if (!pcache)
  1950.         return E_OUTOFMEMORY;
  1951.     *ppunk = SAFECAST(pcache, IShellFolder2*);
  1952.     return S_OK;
  1953. }
  1954. HRESULT CHistCacheFolder::QueryInterface(REFIID iid, void **ppv)
  1955. {
  1956.     HRESULT hres;
  1957.     static const QITAB qitCache[] = {
  1958.         QITABENT(CHistCacheFolder, IShellFolder2),
  1959.         QITABENTMULTI(CHistCacheFolder, IShellFolder, IShellFolder2),
  1960.         QITABENT(CHistCacheFolder, IShellIcon),
  1961.         QITABENT(CHistCacheFolder, IPersistFolder2),
  1962.         QITABENTMULTI(CHistCacheFolder, IPersistFolder, IPersistFolder2),
  1963.         QITABENTMULTI(CHistCacheFolder, IPersist, IPersistFolder2),
  1964.         QITABENT(CHistCacheFolder, IContextMenu),
  1965.         { 0 },
  1966.     };
  1967.     static const QITAB qitHist[] = {
  1968.         QITABENT(CHistCacheFolder, IShellFolder2),
  1969.         QITABENTMULTI(CHistCacheFolder, IShellFolder, IShellFolder2),
  1970.         QITABENT(CHistCacheFolder, IShellIcon),
  1971.         QITABENT(CHistCacheFolder, IPersistFolder),
  1972.         QITABENTMULTI(CHistCacheFolder, IPersist, IPersistFolder),
  1973.         QITABENT(CHistCacheFolder, IContextMenu),
  1974.         QITABENT(CHistCacheFolder, IHistSFPrivate),
  1975.         QITABENT(CHistCacheFolder, IShellFolderViewType),
  1976.         QITABENT(CHistCacheFolder, IShellFolderSearchable),
  1977.         { 0 },
  1978.     };
  1979.     if (IsHistoryFolder(_foldertype))
  1980.     {
  1981.         if (IID_IPersistFolder == iid && FOLDER_TYPE_Hist != _foldertype)
  1982.         {
  1983.             hres = E_NOINTERFACE;
  1984.             *ppv = NULL;
  1985.             return hres;
  1986.         }
  1987.         hres = QISearch(this, qitHist, iid, ppv);
  1988.     }
  1989.     else
  1990.     {
  1991.         hres = QISearch(this, qitCache, iid, ppv);
  1992.     }
  1993.     if (FAILED(hres))
  1994.     {
  1995.         if (iid == IID_IShellView)
  1996.         {
  1997.             // this is a total hack... return our view object from this folder
  1998.             //
  1999.             // the desktop.ini file for "Temporary Internet Files" has UICLSID={guid of this object}
  2000.             // this lets us implment only ths IShellView for this folder, leaving the IShellFolder
  2001.             // to the default file system. this enables operations on the pidls that are stored in
  2002.             // this folder that would otherwise faile since our IShellFolder is not as complete
  2003.             // as the default (this is the same thing the font folder does).
  2004.             //
  2005.             // to support this with defview we would either have to do a complete wrapper object
  2006.             // for the view implemenation, or add this hack that hands out the view object, this
  2007.             // assumes we know the order of calls that the shell makes to create this object
  2008.             // and get the IShellView implementation
  2009.             //
  2010.             ASSERT(!_uViewType);
  2011.             hres = HistCacheFolderView_CreateInstance(this, _pidl, ppv);
  2012.         }
  2013.         else if (iid == CLSID_HistFolder)
  2014.         {
  2015.             *ppv = (void *)(CHistCacheFolder *)this;
  2016.             AddRef();
  2017.             hres = S_OK;
  2018.         }
  2019.     }
  2020.     return hres;
  2021. }
  2022. ULONG CHistCacheFolder::AddRef()
  2023. {
  2024.     return InterlockedIncrement(&_cRef);
  2025. }
  2026. ULONG CHistCacheFolder::Release()
  2027. {
  2028.     if (InterlockedDecrement(&_cRef))
  2029.         return _cRef;
  2030.     delete this;
  2031.     return 0;
  2032. }
  2033. HRESULT CHistCacheFolder::_ExtractInfoFromPidl()
  2034. {
  2035.     LPITEMIDLIST pidlThis;
  2036.     HRESULT hres;
  2037.     LPITEMIDLIST pidlLast = NULL;
  2038.     LPITEMIDLIST pidlSecondLast = NULL;
  2039.     ASSERT(!_uViewType);
  2040.     pidlThis = _pidl;
  2041.     while (pidlThis->mkid.cb)
  2042.     {
  2043.         pidlSecondLast = pidlLast;
  2044.         pidlLast = pidlThis;
  2045.         pidlThis = _ILNext(pidlThis);
  2046.     }
  2047.     switch (_foldertype)
  2048.     {
  2049.     case FOLDER_TYPE_Hist:
  2050.         _pidlRest = pidlThis;
  2051.         break;
  2052.     case FOLDER_TYPE_HistInterval:
  2053.         _pidlRest = pidlLast;
  2054.         break;
  2055.     case FOLDER_TYPE_HistDomain:
  2056.         _pidlRest = pidlSecondLast;
  2057.         break;
  2058.     default:
  2059.         _pidlRest = NULL;
  2060.     }
  2061.     hres = NULL == _pidlRest ? E_FAIL:S_OK;
  2062.     pidlThis = _pidlRest;
  2063.     if (SUCCEEDED(hres))
  2064.     {
  2065.         while (pidlThis->mkid.cb)
  2066.         {
  2067.             if (_IsValid_IDPIDL(pidlThis))
  2068.             {
  2069.                 LPCEIPIDL pcei = (LPCEIPIDL)pidlThis;
  2070.                 if (EQUIV_IDSIGN(pcei->usSign,IDIPIDL_SIGN)) // This is our interval, it implies prefix
  2071.                 {
  2072.                     LPCTSTR pszCachePrefix;
  2073.                     if (_foldertype == FOLDER_TYPE_Hist) _foldertype = FOLDER_TYPE_HistInterval;
  2074.                     hres = _LoadIntervalCache();
  2075.                     if (FAILED(hres)) goto exitPoint;
  2076.                     hres = _GetPrefixForInterval(_GetURLTitle((LPCEIPIDL)pidlThis), &pszCachePrefix);
  2077.                     if (FAILED(hres)) goto exitPoint;
  2078.                     hres = SetCachePrefix(pszCachePrefix);
  2079.                     if (FAILED(hres)) goto exitPoint;
  2080.                 }
  2081.                 else                              // This is our domain
  2082.                 {
  2083.                     if (_foldertype == FOLDER_TYPE_HistInterval) _foldertype = FOLDER_TYPE_HistDomain;
  2084.                     SetDomain(_GetURLTitle((LPCEIPIDL)pidlThis));
  2085.                 }
  2086.             }
  2087.             pidlThis = _ILNext(pidlThis);
  2088.         }
  2089.         switch(_foldertype)
  2090.         {
  2091.             case FOLDER_TYPE_HistDomain:
  2092.                 if (_pszDomain == NULL)
  2093.                     hres = E_FAIL;
  2094.                 //FALL THROUGH INTENDED
  2095.             case FOLDER_TYPE_HistInterval:
  2096.                 if (_pszCachePrefix == NULL)
  2097.                     hres = E_FAIL;
  2098.                 break;
  2099.         }
  2100.     }
  2101. exitPoint:
  2102.     return hres;
  2103. }
  2104. void _SetValueSign(HSFINTERVAL *pInterval, FILETIME ftNow)
  2105. {
  2106.     if (_DaysInInterval(pInterval) == 1 && !CompareFileTime(&(pInterval->ftStart), &ftNow))
  2107.     {
  2108.         pInterval->usSign = IDTPIDL_SIGN;
  2109.     }
  2110.     else
  2111.     {
  2112.         pInterval->usSign = IDIPIDL_SIGN;
  2113.     }
  2114. }
  2115. void _SetVersion(HSFINTERVAL *pInterval, LPCSTR szInterval)
  2116. {
  2117.     USHORT usVers = 0;
  2118.     int i;
  2119.     DWORD dwIntervalLen = lstrlenA(szInterval);
  2120.     //  Unknown versions are 0
  2121.     if (dwIntervalLen == INTERVAL_SIZE)
  2122.     {
  2123.         for (i = INTERVAL_PREFIX_LEN; i < INTERVAL_PREFIX_LEN+INTERVAL_VERS_LEN; i++)
  2124.         {
  2125.             if ('0' > szInterval[i] || '9' < szInterval[i])
  2126.             {
  2127.                 usVers = UNK_INTERVAL_VERS;
  2128.                 break;
  2129.             }
  2130.             usVers = usVers * 10 + (szInterval[i] - '0');
  2131.         }
  2132.     }
  2133.     pInterval->usVers = usVers;
  2134. }
  2135. #ifdef UNICODE
  2136. #define _ValueToInterval           _ValueToIntervalW
  2137. #else // UNICODE
  2138. #define _ValueToInterval           _ValueToIntervalA
  2139. #endif // UNICODE
  2140. HRESULT _ValueToIntervalA(LPCSTR szInterval, FILETIME *pftStart, FILETIME *pftEnd)
  2141. {
  2142.     int i;
  2143.     int iBase;
  2144.     HRESULT hres = E_FAIL;
  2145.     SYSTEMTIME sysTime;
  2146.     unsigned int digits[RANGE_LEN];
  2147.     iBase = lstrlenA(szInterval)-RANGE_LEN;
  2148.     for (i = 0; i < RANGE_LEN; i++)
  2149.     {
  2150.         digits[i] = szInterval[i+iBase] - '0';
  2151.         if (digits[i] > 9) goto exitPoint;
  2152.     }
  2153.     ZeroMemory(&sysTime, sizeof(sysTime));
  2154.     sysTime.wYear = digits[0]*1000 + digits[1]*100 + digits[2] * 10 + digits[3];
  2155.     sysTime.wMonth = digits[4] * 10 + digits[5];
  2156.     sysTime.wDay = digits[6] * 10 + digits[7];
  2157.     if (!SystemTimeToFileTime(&sysTime, pftStart)) goto exitPoint;
  2158.     ZeroMemory(&sysTime, sizeof(sysTime));
  2159.     sysTime.wYear = digits[8]*1000 + digits[9]*100 + digits[10] * 10 + digits[11];
  2160.     sysTime.wMonth = digits[12] * 10 + digits[13];
  2161.     sysTime.wDay = digits[14] * 10 + digits[15];
  2162.     if (!SystemTimeToFileTime(&sysTime, pftEnd)) goto exitPoint;
  2163.     //  Intervals are open on the end, so end should be strictly > start
  2164.     if (CompareFileTime(pftStart, pftEnd) >= 0) goto exitPoint;
  2165.     hres = S_OK;
  2166. exitPoint:
  2167.     return hres;
  2168. }
  2169. HRESULT _ValueToIntervalW(LPCWSTR wzInterval, FILETIME *pftStart, FILETIME *pftEnd)
  2170. {
  2171.     CHAR szInterval[MAX_PATH];
  2172.     ASSERT(lstrlenW(wzInterval) < ARRAYSIZE(szInterval));
  2173.     UnicodeToAnsi(wzInterval, szInterval, ARRAYSIZE(szInterval));
  2174.     return _ValueToIntervalA((LPCSTR) szInterval, pftStart, pftEnd);
  2175. }
  2176. HRESULT CHistCacheFolder::_LoadIntervalCache()
  2177. {
  2178.     HRESULT hres;
  2179.     DWORD dwLastModified;
  2180.     DWORD dwValueIndex;
  2181.     DWORD dwPrefixIndex;
  2182.     HSFINTERVAL     *pIntervalCache = NULL;
  2183.     struct {
  2184.         INTERNET_CACHE_CONTAINER_INFOA cInfo;
  2185.         char szBuffer[MAX_PATH+MAX_PATH];
  2186.     } ContainerInfo;
  2187.     DWORD dwContainerInfoSize;
  2188.     CHAR chSave;
  2189.     HANDLE hContainerEnum;
  2190.     BOOL fContinue = TRUE;
  2191.     FILETIME ftNow;
  2192.     SYSTEMTIME st;
  2193.     DWORD dwOptions;
  2194.     GetLocalTime (&st);
  2195.     SystemTimeToFileTime(&st, &ftNow);
  2196.     _FileTimeDeltaDays(&ftNow, &ftNow, 0);
  2197.     dwLastModified = _dwIntervalCached;
  2198.     dwContainerInfoSize = sizeof(ContainerInfo);
  2199.     if (_pIntervalCache == NULL || CompareFileTime(&ftNow, &_ftDayCached))
  2200.     {
  2201.         dwOptions = 0;
  2202.     }
  2203.     else
  2204.     {
  2205.         dwOptions = CACHE_FIND_CONTAINER_RETURN_NOCHANGE;
  2206.     }
  2207.     hContainerEnum = FindFirstUrlCacheContainerA(&dwLastModified,
  2208.                             &ContainerInfo.cInfo,
  2209.                             &dwContainerInfoSize,
  2210.                             dwOptions);
  2211.     if (hContainerEnum == NULL)
  2212.     {
  2213.         DWORD err = GetLastError();
  2214.         if (err == ERROR_NO_MORE_ITEMS)
  2215.         {
  2216.             fContinue = FALSE;
  2217.         }
  2218.         else if (err == ERROR_INTERNET_NO_NEW_CONTAINERS)
  2219.         {
  2220.             hres = S_OK;
  2221.             goto exitPoint;
  2222.         }
  2223.         else
  2224.         {
  2225.             hres = HRESULT_FROM_WIN32(err);
  2226.             goto exitPoint;
  2227.         }
  2228.     }
  2229.     //  Guarantee we return S_OK we have _pIntervalCache even if we haven't
  2230.     //  yet created the interval registry keys.
  2231.     dwPrefixIndex = 0;
  2232.     dwValueIndex = TYPICAL_INTERVALS;
  2233.     pIntervalCache = (HSFINTERVAL *) LocalAlloc(LPTR, dwValueIndex*sizeof(HSFINTERVAL));
  2234.     if (!pIntervalCache)
  2235.     {
  2236.         hres = E_OUTOFMEMORY;
  2237.         goto exitPoint;
  2238.     }
  2239.     //  All of our intervals map to cache containers starting with
  2240.     //  c_szIntervalPrefix followed by YYYYMMDDYYYYMMDD
  2241.     while (fContinue)
  2242.     {
  2243.         chSave = ContainerInfo.cInfo.lpszName[INTERVAL_PREFIX_LEN];
  2244.         ContainerInfo.cInfo.lpszName[INTERVAL_PREFIX_LEN] = '';
  2245.         if (!StrCmpIA(ContainerInfo.cInfo.lpszName, c_szIntervalPrefix))
  2246.         {
  2247.             ContainerInfo.cInfo.lpszName[INTERVAL_PREFIX_LEN] = chSave;
  2248.             DWORD dwCNameLen;
  2249.             if (dwPrefixIndex >= dwValueIndex)
  2250.             {
  2251.                 HSFINTERVAL     *pIntervalCacheNew;
  2252.                 pIntervalCacheNew = (HSFINTERVAL *) LocalReAlloc(pIntervalCache,
  2253.                     (dwValueIndex*2)*sizeof(HSFINTERVAL),
  2254.                     LMEM_ZEROINIT|LMEM_MOVEABLE);
  2255.                 if (pIntervalCacheNew == NULL)
  2256.                 {
  2257.                     hres = E_OUTOFMEMORY;
  2258.                     goto exitPoint;
  2259.                 }
  2260.                 pIntervalCache = pIntervalCacheNew;
  2261.                 dwValueIndex *= 2;
  2262.             }
  2263.             dwCNameLen = lstrlenA(ContainerInfo.cInfo.lpszName);
  2264.             if (dwCNameLen <= INTERVAL_SIZE && dwCNameLen >= INTERVAL_MIN_SIZE &&
  2265.                 lstrlenA(ContainerInfo.cInfo.lpszCachePrefix) == PREFIX_SIZE)
  2266.             {
  2267.                 _SetVersion(&pIntervalCache[dwPrefixIndex], ContainerInfo.cInfo.lpszName);
  2268.                 if (pIntervalCache[dwPrefixIndex].usVers != UNK_INTERVAL_VERS)
  2269.                 {
  2270.                     AnsiToTChar(ContainerInfo.cInfo.lpszCachePrefix, pIntervalCache[dwPrefixIndex].szPrefix, ARRAYSIZE(pIntervalCache[dwPrefixIndex].szPrefix));
  2271.                     hres = _ValueToIntervalA( ContainerInfo.cInfo.lpszName,
  2272.                                              &pIntervalCache[dwPrefixIndex].ftStart,
  2273.                                              &pIntervalCache[dwPrefixIndex].ftEnd);
  2274.                     if (FAILED(hres)) goto exitPoint;
  2275.                     _SetValueSign(&pIntervalCache[dwPrefixIndex], ftNow);
  2276.                     dwPrefixIndex++;
  2277.                 }
  2278.                 else
  2279.                 {
  2280.                     pIntervalCache[dwPrefixIndex].usVers = 0;
  2281.                 }
  2282.             }
  2283.             //
  2284.             // HACK! IE5 bld 807 created containers with prefix length PREFIX_SIZE - 1.
  2285.             // Delete these entries so history shows up for anyone upgrading over this
  2286.             // build.  Delete this code!  (edwardp 8/8/98)
  2287.             //
  2288.             else if (dwCNameLen <= INTERVAL_SIZE && dwCNameLen >= INTERVAL_MIN_SIZE &&
  2289.                      lstrlenA(ContainerInfo.cInfo.lpszCachePrefix) == PREFIX_SIZE - 1)
  2290.             {
  2291.                 DeleteUrlCacheContainerA(ContainerInfo.cInfo.lpszName, 0);
  2292.             }
  2293.         }
  2294.         dwContainerInfoSize = sizeof(ContainerInfo);
  2295.         fContinue = FindNextUrlCacheContainerA(hContainerEnum,
  2296.                             &ContainerInfo.cInfo,
  2297.                             &dwContainerInfoSize);
  2298.     }
  2299.     hres = S_OK;
  2300.     _dwIntervalCached = dwLastModified;
  2301.     _ftDayCached = ftNow;
  2302.     {
  2303.         ENTERCRITICAL;
  2304.         if (_pIntervalCache)
  2305.         {
  2306.             LocalFree(_pIntervalCache);
  2307.             _pIntervalCache = NULL;
  2308.         }
  2309.         _pIntervalCache = pIntervalCache;
  2310.         LEAVECRITICAL;
  2311.     }
  2312.     _cbIntervals = dwPrefixIndex;
  2313.     // because it will be freed by our destructor
  2314.     pIntervalCache  = NULL;
  2315. exitPoint:
  2316.     if (hContainerEnum) FindCloseUrlCache(hContainerEnum);
  2317.     if (pIntervalCache) LocalFree(pIntervalCache);
  2318.     return hres;
  2319. }
  2320. //  Returns true if *pftItem falls in the days *pftStart..*pftEnd inclusive
  2321. BOOL _InInterval(FILETIME *pftStart, FILETIME *pftEnd, FILETIME *pftItem)
  2322. {
  2323.     return (CompareFileTime(pftStart,pftItem) <= 0 && CompareFileTime(pftItem,pftEnd) < 0);
  2324. }
  2325. //  Truncates filetime increments beyond the day and then deltas by Days and converts back
  2326. //  to FILETIME increments
  2327. void _FileTimeDeltaDays(FILETIME *pftBase, FILETIME *pftNew, int Days)
  2328. {
  2329.     _int64 i64Base;
  2330.     i64Base = (((_int64)pftBase->dwHighDateTime) << 32) | pftBase->dwLowDateTime;
  2331.     i64Base /= FILE_SEC_TICKS;
  2332.     i64Base /= DAY_SECS;
  2333.     i64Base += Days;
  2334.     i64Base *= FILE_SEC_TICKS;
  2335.     i64Base *= DAY_SECS;
  2336.     pftNew->dwHighDateTime = (DWORD) ((i64Base >> 32) & 0xFFFFFFFF);
  2337.     pftNew->dwLowDateTime = (DWORD) (i64Base & 0xFFFFFFFF);
  2338. }
  2339. DWORD _DaysInInterval(HSFINTERVAL *pInterval)
  2340. {
  2341.     _int64 i64Start;
  2342.     _int64 i64End;
  2343.     i64Start = (((_int64)pInterval->ftStart.dwHighDateTime) << 32) | pInterval->ftStart.dwLowDateTime;
  2344.     i64Start /= FILE_SEC_TICKS;
  2345.     i64Start /= DAY_SECS;
  2346.     i64End = (((_int64)pInterval->ftEnd.dwHighDateTime) << 32) | pInterval->ftEnd.dwLowDateTime;
  2347.     i64End /= FILE_SEC_TICKS;
  2348.     i64End /= DAY_SECS;
  2349.     // NOTE: the lower bound is closed, upper is open (ie first tick of next day)
  2350.     return (DWORD) (i64End - i64Start);
  2351. }
  2352. //  Returns S_OK if found, S_FALSE if not, error on error
  2353. //  finds weekly interval in preference to daily if both exist
  2354. HRESULT CHistCacheFolder::_GetInterval(FILETIME *pftItem, BOOL fWeekOnly, HSFINTERVAL **ppInterval)
  2355. {
  2356.     HRESULT hres = E_FAIL;
  2357.     HSFINTERVAL *pReturn = NULL;
  2358.     int i;
  2359.     HSFINTERVAL *pDailyInterval = NULL;
  2360.     if (NULL == _pIntervalCache) goto exitPoint;
  2361.     for (i = 0; i < _cbIntervals; i ++)
  2362.     {
  2363.         if (_pIntervalCache[i].usVers == OUR_VERS)
  2364.         {
  2365.             if (_InInterval(&_pIntervalCache[i].ftStart,
  2366.                             &_pIntervalCache[i].ftEnd,
  2367.                             pftItem))
  2368.             {
  2369.                 if (7 != _DaysInInterval(&_pIntervalCache[i]))
  2370.                 {
  2371.                     if (!fWeekOnly)
  2372.                     {
  2373.                         pDailyInterval = &_pIntervalCache[i];
  2374.                     }
  2375.                     continue;
  2376.                 }
  2377.                 else
  2378.                 {
  2379.                     pReturn = &_pIntervalCache[i];
  2380.                     hres = S_OK;
  2381.                     goto exitPoint;
  2382.                 }
  2383.             }
  2384.         }
  2385.     }
  2386.     pReturn = pDailyInterval;
  2387.     hres = pReturn ? S_OK : S_FALSE;
  2388. exitPoint:
  2389.     if (ppInterval) *ppInterval = pReturn;
  2390.     return hres;
  2391. }
  2392. HRESULT CHistCacheFolder::_GetPrefixForInterval(LPCTSTR pszInterval, LPCTSTR *ppszCachePrefix)
  2393. {
  2394.     HRESULT hres = E_FAIL;
  2395.     int i;
  2396.     LPCTSTR pszReturn = NULL;
  2397.     FILETIME ftStart;
  2398.     FILETIME ftEnd;
  2399.     if (NULL == _pIntervalCache) goto exitPoint;
  2400.     hres = _ValueToInterval(pszInterval, &ftStart, &ftEnd);
  2401.     if (FAILED(hres)) goto exitPoint;
  2402.     for (i = 0; i < _cbIntervals; i ++)
  2403.     {
  2404.         if(_pIntervalCache[i].usVers == OUR_VERS)
  2405.         {
  2406.             if (CompareFileTime(&_pIntervalCache[i].ftStart,&ftStart) == 0 &&
  2407.                 CompareFileTime(&_pIntervalCache[i].ftEnd,&ftEnd) == 0)
  2408.             {
  2409.                 pszReturn = _pIntervalCache[i].szPrefix;
  2410.                 hres = S_OK;
  2411.                 break;
  2412.             }
  2413.         }
  2414.     }
  2415.     hres = pszReturn ? S_OK : S_FALSE;
  2416. exitPoint:
  2417.     if (ppszCachePrefix) *ppszCachePrefix = pszReturn;
  2418.     return hres;
  2419. }
  2420. void _KeyForInterval(HSFINTERVAL *pInterval, LPTSTR pszInterval, int cchInterval)
  2421. {
  2422.     SYSTEMTIME stStart;
  2423.     SYSTEMTIME stEnd;
  2424.     CHAR szVers[3];
  2425. #ifndef UNIX
  2426.     CHAR szTempBuff[MAX_PATH];
  2427. #else
  2428.     CHAR szTempBuff[INTERVAL_SIZE+1];
  2429. #endif
  2430.     ASSERT(pInterval->usVers!=UNK_INTERVAL_VERS && pInterval->usVers < 100);
  2431.     if (pInterval->usVers)
  2432.     {
  2433.         wnsprintfA(szVers, ARRAYSIZE(szVers), "%02lu", (ULONG) (pInterval->usVers));
  2434.     }
  2435.     else
  2436.     {
  2437.         szVers[0] = '';
  2438.     }
  2439.     FileTimeToSystemTime(&pInterval->ftStart, &stStart);
  2440.     FileTimeToSystemTime(&pInterval->ftEnd, &stEnd);
  2441.     wnsprintfA(szTempBuff, ARRAYSIZE(szTempBuff),
  2442.              "%s%s%04lu%02lu%02lu%04lu%02lu%02lu",
  2443.              c_szIntervalPrefix,
  2444.              szVers,
  2445.              (ULONG) stStart.wYear,
  2446.              (ULONG) stStart.wMonth,
  2447.              (ULONG) stStart.wDay,
  2448.              (ULONG) stEnd.wYear,
  2449.              (ULONG) stEnd.wMonth,
  2450.              (ULONG) stEnd.wDay);
  2451.     AnsiToTChar(szTempBuff, pszInterval, cchInterval);
  2452. }
  2453. LPITEMIDLIST CHistCacheFolder::_HostPidl(LPCTSTR pszHostUrl, HSFINTERVAL *pInterval)
  2454. {
  2455.     ASSERT(!_uViewType)
  2456.     LPITEMIDLIST pidlReturn;
  2457.     LPITEMIDLIST pidl;
  2458.     struct _HOSTIDL
  2459.     {
  2460.         USHORT cb;
  2461.         USHORT usSign;
  2462. #if defined(UNIX)
  2463.         TCHAR szHost[INTERNET_MAX_HOST_NAME_LENGTH+4];
  2464. #else
  2465.         TCHAR szHost[INTERNET_MAX_HOST_NAME_LENGTH+1];
  2466. #endif
  2467.     } HostIDL;
  2468.     struct _INTERVALIDL
  2469.     {
  2470.         USHORT cb;
  2471.         USHORT usSign;
  2472. #if defined(UNIX)
  2473.         TCHAR szInterval[INTERVAL_SIZE+4];
  2474. #else
  2475.         TCHAR szInterval[INTERVAL_SIZE+1];
  2476. #endif
  2477.         struct _HOSTIDL hostIDL;
  2478.         USHORT cbTrail;
  2479.     } IntervalIDL;
  2480.     LPBYTE pb;
  2481.     USHORT cbSave;
  2482.     ASSERT(_pidlRest);
  2483.     pidl = _pidlRest;
  2484.     cbSave = pidl->mkid.cb;
  2485.     pidl->mkid.cb = 0;
  2486.     ZeroMemory(&IntervalIDL, sizeof(IntervalIDL));
  2487.     IntervalIDL.usSign = pInterval->usSign;
  2488.     _KeyForInterval(pInterval, IntervalIDL.szInterval, ARRAYSIZE(IntervalIDL.szInterval));
  2489.     IntervalIDL.cb = 2*sizeof(USHORT)+ (lstrlen(IntervalIDL.szInterval) + 1) * sizeof(TCHAR);
  2490. #if defined(UNIX)
  2491.     IntervalIDL.cb = ALIGN4(IntervalIDL.cb);
  2492. #endif
  2493.     pb = ((LPBYTE) (&IntervalIDL)) + IntervalIDL.cb;
  2494.     StrCpyN((LPTSTR)(pb+2*sizeof(USHORT)), pszHostUrl,
  2495.             (sizeof(IntervalIDL) - (IntervalIDL.cb + (3 * sizeof(USHORT)))) / sizeof(TCHAR));
  2496.     HostIDL.usSign = (USHORT)IDDPIDL_SIGN;
  2497.     HostIDL.cb = 2*sizeof(USHORT)+(lstrlen((LPTSTR)(pb+2*sizeof(USHORT))) + 1) * sizeof(TCHAR);
  2498. #if defined(UNIX)
  2499.     HostIDL.cb = ALIGN4(HostIDL.cb);
  2500. #endif
  2501.     memcpy(pb, &HostIDL, 2*sizeof(USHORT));
  2502.     *(USHORT *)(&pb[HostIDL.cb]) = 0;  // terminate the HostIDL ItemID
  2503.     pidlReturn = ILCombine(_pidl, (LPITEMIDLIST) (&IntervalIDL));
  2504.     pidl->mkid.cb = cbSave;
  2505.     return pidlReturn;
  2506. }
  2507. // Notify that an event has occured that affects a specific element in
  2508. //  history for special viewtypes
  2509. HRESULT CHistCacheFolder::_ViewType_NotifyEvent(IN LPITEMIDLIST pidlRoot,
  2510.                                                 IN LPITEMIDLIST pidlHost,
  2511.                                                 IN LPITEMIDLIST pidlPage,
  2512.                                                 IN LONG         wEventId)
  2513. {
  2514.     HRESULT hRes = S_OK;
  2515.     ASSERT(pidlRoot && pidlHost && pidlPage);
  2516.     // VIEPWIDL_ORDER_TODAY
  2517.     LPITEMIDLIST pidlToday = _Combine_ViewPidl(VIEWPIDL_ORDER_TODAY, pidlPage);
  2518.     if (pidlToday) {
  2519.         LPITEMIDLIST pidlNotify = ILCombine(pidlRoot, pidlToday);
  2520.         if (pidlNotify) {
  2521.             SHChangeNotify(wEventId, SHCNF_IDLIST | CHANGE_FLAGS, pidlNotify, NULL);
  2522.             ILFree(pidlNotify);
  2523.         }
  2524.         ILFree(pidlToday);
  2525.     }
  2526.     // VIEWPIDL_ORDER_SITE
  2527.     LPITEMIDLIST pidlSite = _Combine_ViewPidl(VIEWPIDL_ORDER_SITE, pidlHost);
  2528.     if (pidlSite) {
  2529.         LPITEMIDLIST pidlSitePage = ILCombine(pidlSite, pidlPage);
  2530.         if (pidlSitePage) {
  2531.             LPITEMIDLIST pidlNotify = ILCombine(pidlRoot, pidlSitePage);
  2532.             if (pidlNotify) {
  2533.                 SHChangeNotify(wEventId, SHCNF_IDLIST | CHANGE_FLAGS, pidlNotify, NULL);
  2534.                 ILFree(pidlNotify);
  2535.             }
  2536.             ILFree(pidlSitePage);
  2537.         }
  2538.         ILFree(pidlSite);
  2539.     }
  2540.     return hRes;
  2541. }
  2542. LPCTSTR CHistCacheFolder::_GetLocalHost(void)
  2543. {
  2544.     if (!*_szLocalHost)
  2545.         ::_GetLocalHost(_szLocalHost, SIZECHARS(_szLocalHost));
  2546.     return _szLocalHost;
  2547. }
  2548. //  NOTE: modifies pszUrl.
  2549. HRESULT CHistCacheFolder::_NotifyWrite(LPTSTR pszUrl, int cchUrl, FILETIME *pftModified,  LPITEMIDLIST * ppidlSelect)
  2550. {
  2551.     HRESULT hres = S_OK;
  2552.     DWORD dwBuffSize = MAX_URLCACHE_ENTRY;
  2553.     USHORT cbSave;
  2554.     LPITEMIDLIST pidl;
  2555.     LPITEMIDLIST pidlNotify;
  2556.     LPITEMIDLIST pidlTemp;
  2557.     LPITEMIDLIST pidlHost;
  2558.     LPHEIPIDL    phei = NULL;
  2559.     HSFINTERVAL *pInterval;
  2560.     FILETIME ftExpires = {0,0};
  2561.     BOOL fNewHost;
  2562.     LPCTSTR pszStrippedUrl = _StripHistoryUrlToUrl(pszUrl);
  2563.     LPCTSTR pszHostUrl = pszStrippedUrl + HOSTPREFIXLEN;
  2564.     DWORD cchFree = cchUrl - (DWORD)(pszStrippedUrl-pszUrl);
  2565.     CHAR szAnsiUrl[MAX_URL_STRING];
  2566.     ASSERT(_pidlRest);
  2567.     pidl = _pidlRest;
  2568.     cbSave = pidl->mkid.cb;
  2569.     pidl->mkid.cb = 0;
  2570.     ///  Should also be able to get hitcount
  2571.     STATURL suThis;
  2572.     HRESULT hResLocal = E_FAIL;
  2573.     IUrlHistoryPriv *pUrlHistStg = _GetHistStg();
  2574.     if (pUrlHistStg) {
  2575.         hResLocal = pUrlHistStg->QueryUrl(_StripHistoryUrlToUrl(pszUrl),
  2576.                                           STATURL_QUERYFLAG_NOURL, &suThis);
  2577.         pUrlHistStg->Release();
  2578.     }
  2579.     phei = _CreateHCacheFolderPidl(FALSE, pszUrl, *pftModified,
  2580.                                    (SUCCEEDED(hResLocal) ? &suThis : NULL), 0,
  2581.                                    _GetHitCount(_StripHistoryUrlToUrl(pszUrl)));
  2582.     if (SUCCEEDED(hResLocal) && suThis.pwcsTitle)
  2583.         OleFree(suThis.pwcsTitle);
  2584.     if (phei == NULL)
  2585.     {
  2586.         hres = E_OUTOFMEMORY;
  2587.         goto exitPoint;
  2588.     }
  2589.     if (cchFree <= HOSTPREFIXLEN)
  2590.     {
  2591.         hres = E_OUTOFMEMORY;
  2592.         goto exitPoint;
  2593.     }
  2594.     StrCpyN((LPTSTR)pszStrippedUrl, c_szHostPrefix, cchFree);    // whack on the PIDL!
  2595.     cchFree -= HOSTPREFIXLEN;
  2596.     _GetURLHostFromUrl(HCPidlToSourceUrl((LPCITEMIDLIST)phei),
  2597.                        (LPTSTR)pszHostUrl, cchFree, _GetLocalHost());
  2598.     //  BUGBUG chrisfra 4/9/97 we could take a small performance hit here and always
  2599.     //  update host entry.  this would allow us to efficiently sort domains by most
  2600.     //  recent access.
  2601.     fNewHost = FALSE;
  2602.     dwBuffSize = MAX_URLCACHE_ENTRY;
  2603.     SHTCharToAnsi(pszUrl, szAnsiUrl, ARRAYSIZE(szAnsiUrl));
  2604.     if (!GetUrlCacheEntryInfoA(szAnsiUrl, NULL, 0))
  2605.     {
  2606.         fNewHost = TRUE;
  2607.         if (!CommitUrlCacheEntryA(szAnsiUrl, NULL, ftExpires, *pftModified,
  2608.                           URLHISTORY_CACHE_ENTRY|STICKY_CACHE_ENTRY,
  2609.                           NULL, 0, NULL, 0))
  2610.         {
  2611.             hres = HRESULT_FROM_WIN32(GetLastError());
  2612.         }
  2613.         if (FAILED(hres))
  2614.             goto exitPoint;
  2615.     }
  2616.     hres = _GetInterval(pftModified, FALSE, &pInterval);
  2617.     if (FAILED(hres))
  2618.         goto exitPoint;
  2619.     pidlTemp = _HostPidl(pszHostUrl, pInterval);
  2620.     if (pidlTemp == NULL)
  2621.     {
  2622.         hres = E_OUTOFMEMORY;
  2623.         goto exitPoint;
  2624.     }
  2625.     // Get just the host part of the pidl
  2626.     pidlHost = ILFindLastID(pidlTemp);
  2627.     ASSERT(pidlHost);
  2628.     if (fNewHost)
  2629.     {
  2630.         SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST | CHANGE_FLAGS, pidlTemp, NULL);
  2631.         // We also need to notify special history views if they are listening:
  2632.         // For now, just "View by Site" is relevant...
  2633.         LPITEMIDLIST pidlViewSuffix = _Combine_ViewPidl(VIEWPIDL_ORDER_SITE, pidlHost);
  2634.         if (pidlViewSuffix) {
  2635.             LPITEMIDLIST pidlNotify = ILCombine(_pidl, pidlViewSuffix);
  2636.             if (pidlNotify) {
  2637.                 SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST | CHANGE_FLAGS, pidlNotify, NULL);
  2638.                 ILFree(pidlNotify);
  2639.             }
  2640.             ILFree(pidlViewSuffix);
  2641.         }
  2642.     }
  2643.     pidlNotify = ILCombine(pidlTemp, (LPITEMIDLIST) phei);
  2644.     if (pidlNotify == NULL)
  2645.     {
  2646.         ILFree(pidlTemp);
  2647.         hres = E_OUTOFMEMORY;
  2648.         goto exitPoint;
  2649.     }
  2650.     // Create (if its not there already) and Rename (if its there)
  2651.     //  Sending both notifys will be faster than trying to figure out
  2652.     //  which one is appropriate
  2653.     SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST | CHANGE_FLAGS, pidlNotify, NULL);
  2654.     // Also notify events for specail viewpidls!
  2655.     _ViewType_NotifyEvent(_pidl, pidlHost, (LPITEMIDLIST)phei, SHCNE_CREATE);
  2656.     if (ppidlSelect)
  2657.     {
  2658.         *ppidlSelect = pidlNotify;
  2659.     }
  2660.     else
  2661.     {
  2662.         ILFree(pidlNotify);
  2663.     }
  2664.     ILFree(pidlTemp);
  2665. exitPoint:
  2666.     if (phei)
  2667.         LocalFree(phei);
  2668.     pidl->mkid.cb = cbSave;
  2669.     return hres;
  2670. }
  2671. HRESULT CHistCacheFolder::_NotifyInterval(HSFINTERVAL *pInterval, LONG lEventID)
  2672. {
  2673.     // special history views are not relevant here...
  2674.     if (_uViewType)
  2675.         return S_FALSE;
  2676.     USHORT cbSave = 0;
  2677.     LPITEMIDLIST pidl;
  2678.     LPITEMIDLIST pidlNotify = NULL;
  2679.     LPITEMIDLIST pidlNotify2 = NULL;
  2680.     LPITEMIDLIST pidlNotify3 = NULL;
  2681.     HRESULT hres = S_OK;
  2682.     struct _INTERVALIDL
  2683.     {
  2684.         USHORT cb;
  2685.         USHORT usSign;
  2686. #if defined(UNIX)
  2687.         TCHAR szInterval[INTERVAL_SIZE+4];
  2688. #else
  2689.         TCHAR szInterval[INTERVAL_SIZE+1];
  2690. #endif
  2691.         USHORT cbTrail;
  2692.     } IntervalIDL,IntervalIDL2;
  2693.     ASSERT(_pidlRest);
  2694.     pidl = _pidlRest;
  2695.     cbSave = pidl->mkid.cb;
  2696.     pidl->mkid.cb = 0;
  2697.     ZeroMemory(&IntervalIDL, sizeof(IntervalIDL));
  2698.     IntervalIDL.usSign = pInterval->usSign;
  2699.     _KeyForInterval(pInterval, IntervalIDL.szInterval, ARRAYSIZE(IntervalIDL.szInterval));
  2700.     IntervalIDL.cb = 2*sizeof(USHORT) + (lstrlen(IntervalIDL.szInterval) + 1)*sizeof(TCHAR);
  2701. #if defined(UNIX)
  2702.     IntervalIDL.cb = ALIGN4(IntervalIDL.cb);
  2703. #endif
  2704.     if (lEventID&SHCNE_RENAMEFOLDER ||  // was TODAY, now is a weekday
  2705.         (lEventID&SHCNE_RMDIR && 1 == _DaysInInterval(pInterval)) ) // one day, maybe TODAY
  2706.     {
  2707.         memcpy(&IntervalIDL2, &IntervalIDL, sizeof(IntervalIDL));
  2708.         IntervalIDL2.usSign = (USHORT)IDTPIDL_SIGN;
  2709.         pidlNotify2 = ILCombine(_pidl, (LPITEMIDLIST) (&IntervalIDL));
  2710.         pidlNotify = ILCombine(_pidl, (LPITEMIDLIST) (&IntervalIDL2));
  2711.         if (pidlNotify2 == NULL)
  2712.         {
  2713.             hres = E_OUTOFMEMORY;
  2714.             goto exitPoint;
  2715.         }
  2716.         if (lEventID&SHCNE_RMDIR)
  2717.         {
  2718.             pidlNotify3 = pidlNotify2;
  2719.             pidlNotify2 = NULL;
  2720.         }
  2721.     }
  2722.     else
  2723.     {
  2724.         pidlNotify = ILCombine(_pidl, (LPITEMIDLIST) (&IntervalIDL));
  2725.     }
  2726.     if (pidlNotify == NULL)
  2727.     {
  2728.         hres = E_OUTOFMEMORY;
  2729.         goto exitPoint;
  2730.     }
  2731.     SHChangeNotify(lEventID, SHCNF_IDLIST|CHANGE_FLAGS, pidlNotify, pidlNotify2);
  2732.     if (pidlNotify3) SHChangeNotify(lEventID, SHCNF_IDLIST|CHANGE_FLAGS, pidlNotify3, NULL);
  2733. exitPoint:
  2734.     ILFree(pidlNotify);
  2735.     ILFree(pidlNotify2);
  2736.     ILFree(pidlNotify3);
  2737.     if (cbSave) pidl->mkid.cb = cbSave;
  2738.     return hres;
  2739. }
  2740. HRESULT CHistCacheFolder::_CreateInterval(FILETIME *pftStart, DWORD dwDays)
  2741. {
  2742.     HSFINTERVAL interval;
  2743.     TCHAR szInterval[INTERVAL_SIZE+1];
  2744.     UINT err;
  2745.     FILETIME ftNow;
  2746.     SYSTEMTIME stNow;
  2747.     CHAR szIntervalAnsi[INTERVAL_SIZE+1], szCachePrefixAnsi[INTERVAL_SIZE+1];
  2748. #define CREATE_OPTIONS (INTERNET_CACHE_CONTAINER_AUTODELETE |  
  2749.                         INTERNET_CACHE_CONTAINER_NOSUBDIRS  |  
  2750.                         INTERNET_CACHE_CONTAINER_NODESKTOPINIT)
  2751.     //  _FileTimeDeltaDays guarantees times just at the 0th tick of the day
  2752.     _FileTimeDeltaDays(pftStart, &interval.ftStart, 0);
  2753.     _FileTimeDeltaDays(pftStart, &interval.ftEnd, dwDays);
  2754.     interval.usVers = OUR_VERS;
  2755.     GetLocalTime(&stNow);
  2756.     SystemTimeToFileTime(&stNow, &ftNow);
  2757.     _FileTimeDeltaDays(&ftNow, &ftNow, 0);
  2758.     _SetValueSign(&interval, ftNow);
  2759.     _KeyForInterval(&interval, szInterval, ARRAYSIZE(szInterval));
  2760.     interval.szPrefix[0] = ':';
  2761.     StrCpyN(&interval.szPrefix[1], &szInterval[INTERVAL_PREFIX_LEN+INTERVAL_VERS_LEN],
  2762.             ARRAYSIZE(interval.szPrefix) - 1);
  2763.     StrCatBuff(interval.szPrefix, TEXT(": "), ARRAYSIZE(interval.szPrefix));
  2764.     SHTCharToAnsi(szInterval, szIntervalAnsi, ARRAYSIZE(szIntervalAnsi));
  2765.     SHTCharToAnsi(interval.szPrefix, szCachePrefixAnsi, ARRAYSIZE(szCachePrefixAnsi));
  2766.     if (CreateUrlCacheContainerA(szIntervalAnsi,   // Name
  2767.                                 szCachePrefixAnsi, // CachePrefix
  2768.                                 NULL,              // Path
  2769.                                 0,                 // Cache Limit
  2770.                                 0,                 // Container Type
  2771.                                 CREATE_OPTIONS,    // Create Options
  2772.                                 NULL,              // Create Buffer
  2773.                                 0))                // Create Buffer size
  2774.     {
  2775.         _NotifyInterval(&interval, SHCNE_MKDIR);
  2776.         err = ERROR_SUCCESS;
  2777.     }
  2778.     else
  2779.     {
  2780.         err = GetLastError();
  2781.     }
  2782.     return ERROR_SUCCESS == err ? S_OK : HRESULT_FROM_WIN32(err);
  2783. }
  2784. HRESULT CHistCacheFolder::_PrefixUrl(LPCTSTR pszStrippedUrl,
  2785.                                      FILETIME *pftLastModifiedTime,
  2786.                                      LPTSTR pszPrefixedUrl,
  2787.                                      DWORD cchPrefixedUrl)
  2788. {
  2789.     HRESULT hres;
  2790.     HSFINTERVAL *pInterval;
  2791.     hres = _GetInterval(pftLastModifiedTime, FALSE, &pInterval);
  2792.     if (S_OK == hres)
  2793.     {
  2794.         if ((DWORD)((lstrlen(pszStrippedUrl) + lstrlen(pInterval->szPrefix) + 1) * sizeof(TCHAR)) > cchPrefixedUrl)
  2795.         {
  2796.             hres = E_OUTOFMEMORY;
  2797.         }
  2798.         else
  2799.         {
  2800.             StrCpyN(pszPrefixedUrl, pInterval->szPrefix, cchPrefixedUrl);
  2801.             StrCatBuff(pszPrefixedUrl, pszStrippedUrl, cchPrefixedUrl);
  2802.         }
  2803.     }
  2804.     return hres;
  2805. }
  2806. HRESULT CHistCacheFolder::_WriteHistory(LPCTSTR pszPrefixedUrl,
  2807.                                         FILETIME ftExpires,
  2808.                                         FILETIME ftModified,
  2809.                                         BOOL fSendNotify,
  2810.                                         LPITEMIDLIST * ppidlSelect)
  2811. {
  2812.     TCHAR szNewPrefixedUrl[INTERNET_MAX_URL_LENGTH+1];
  2813.     HRESULT hres = E_INVALIDARG;
  2814.     LPCTSTR pszUrlMinusContainer;
  2815.     pszUrlMinusContainer = _StripContainerUrlUrl(pszPrefixedUrl);
  2816.     if (pszUrlMinusContainer)
  2817.     {
  2818.         hres = _PrefixUrl(pszUrlMinusContainer,
  2819.                           &ftModified,
  2820.                           szNewPrefixedUrl,
  2821.                           ARRAYSIZE(szNewPrefixedUrl));
  2822.         if (S_OK == hres)
  2823.         {
  2824.             CHAR szAnsiUrl[MAX_URL_STRING+1];
  2825.             SHTCharToAnsi(szNewPrefixedUrl, szAnsiUrl, ARRAYSIZE(szAnsiUrl));
  2826.             if (!CommitUrlCacheEntryA(
  2827.                           szAnsiUrl,
  2828.                           NULL,
  2829.                           ftExpires,
  2830.                           ftModified,
  2831.                           URLHISTORY_CACHE_ENTRY|STICKY_CACHE_ENTRY,
  2832.                           NULL,
  2833.                           0,
  2834.                           NULL,
  2835.                           0))
  2836.             {
  2837.                 hres = HRESULT_FROM_WIN32(GetLastError());
  2838.             }
  2839.             else
  2840.             {
  2841.                 if (fSendNotify) _NotifyWrite(szNewPrefixedUrl,
  2842.                                               ARRAYSIZE(szNewPrefixedUrl),
  2843.                                               &ftModified, ppidlSelect);
  2844.             }
  2845.         }
  2846.     }
  2847.     return hres;
  2848. }
  2849. // This function will update any shell that might be listening to us
  2850. //  to redraw the directory.
  2851. // It will do this by generating a SHCNE_UPDATE for all possible pidl roots
  2852. //  that the shell could have.  Hopefully, this should be sufficient...
  2853. // Specifically, this is meant to be called by ClearHistory.
  2854. HRESULT CHistCacheFolder::_ViewType_NotifyUpdateAll() {
  2855.     LPITEMIDLIST pidlHistory;
  2856.     if (SUCCEEDED(SHGetHistoryPIDL(&pidlHistory)))
  2857.     {
  2858.         for (USHORT us = 1; us <= VIEWPIDL_ORDER_MAX; ++us) {
  2859.             LPITEMIDLIST pidlView;
  2860.             if (SUCCEEDED(CreateSpecialViewPidl(us, &pidlView))) {
  2861.                 LPITEMIDLIST pidlTemp = ILCombine(pidlHistory, pidlView);
  2862.                 if (pidlTemp) {
  2863.                     SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlTemp, NULL);
  2864.                     ILFree(pidlTemp);
  2865.                 }
  2866.                 ILFree(pidlView);
  2867.             }
  2868.         }
  2869.         ILFree(pidlHistory);
  2870.         SHChangeNotifyHandleEvents();
  2871.     }
  2872.     return S_OK;
  2873. }
  2874. //  On a per user basis.
  2875. //  BUGBUG: chrisfra 6/11/97. _DeleteItems of a Time Interval deletes the entire interval.
  2876. //  ClearHistory should probably work the same. Pros of _DeleteEntries is on non-profile,
  2877. //  multi-user machine, other user's history is preserved.  Cons is that on profile based
  2878. //  machine, empty intervals are created.
  2879. HRESULT CHistCacheFolder::ClearHistory()
  2880. {
  2881.     HRESULT hres = S_OK;
  2882.     int i;
  2883.     hres = _ValidateIntervalCache();
  2884.     if (SUCCEEDED(hres))
  2885.     {
  2886.         for (i = 0; i < _cbIntervals; i++)
  2887.         {
  2888. #if 0
  2889.             if (_DeleteEntries(_pIntervalCache[i].szPrefix, NULL, NULL))
  2890.                 hres = S_FALSE;
  2891.             _NotifyInterval(&_pIntervalCache[i], SHCNE_UPDATEDIR);
  2892. #else
  2893.             _DeleteInterval(&_pIntervalCache[i]);
  2894. #endif
  2895.         }
  2896.     }
  2897. #ifndef UNIX
  2898.     _ViewType_NotifyUpdateAll();
  2899. #endif
  2900.     return hres;
  2901. }
  2902. //  ftModified is in "User Perceived", ie local time
  2903. //  stuffed into FILETIME as if it were UNC.  ftExpires is in normal UNC time.
  2904. HRESULT CHistCacheFolder::WriteHistory(LPCTSTR pszPrefixedUrl,
  2905.                                        FILETIME ftExpires,
  2906.                                        FILETIME ftModified,
  2907.                                        LPITEMIDLIST * ppidlSelect)
  2908. {
  2909.     HRESULT hres;
  2910.     hres = _ValidateIntervalCache();
  2911.     if (SUCCEEDED(hres))
  2912.     {
  2913.         hres = _WriteHistory(pszPrefixedUrl,
  2914.                     ftExpires,
  2915.                     ftModified,
  2916.                     TRUE,
  2917.                     ppidlSelect);
  2918.     }
  2919.     return hres;
  2920. }
  2921. //  Makes best efforts attempt to copy old style history items into new containers
  2922. HRESULT CHistCacheFolder::_CopyEntries(LPCTSTR pszHistPrefix)
  2923. {
  2924.     HANDLE              hEnum = NULL;
  2925.     HRESULT             hres;
  2926.     BOOL                fNotCopied = FALSE;
  2927.     LPINTERNET_CACHE_ENTRY_INFO pceiWorking;
  2928.     DWORD               dwBuffSize;
  2929.     LPTSTR              pszSearchPattern = NULL;
  2930.     TCHAR               szHistSearchPattern[65];    // search pattern for history items
  2931.     StrCpyN(szHistSearchPattern, pszHistPrefix, ARRAYSIZE(szHistSearchPattern));
  2932.     // BUGBUG: We can't pass in the whole search pattern that we want,
  2933.     // because FindFirstUrlCacheEntry is busted.  It will only look at the
  2934.     // prefix if there is a cache container for that prefix.  So, we can
  2935.     // pass in "Visited: " and enumerate all the history items in the cache,
  2936.     // but then we need to pull out only the ones with the correct username.
  2937.     // StrCpy(szHistSearchPattern, szUserName);
  2938.     pszSearchPattern = szHistSearchPattern;
  2939.     pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY);
  2940.     if (NULL == pceiWorking)
  2941.     {
  2942.         hres = E_OUTOFMEMORY;
  2943.         goto exitPoint;
  2944.     }
  2945.     hres = _ValidateIntervalCache();
  2946.     if (FAILED(hres)) goto exitPoint;
  2947.     while (SUCCEEDED(hres))
  2948.     {
  2949.         dwBuffSize = MAX_URLCACHE_ENTRY;
  2950.         if (!hEnum)
  2951.         {
  2952.             hEnum = FindFirstUrlCacheEntry(pszSearchPattern, pceiWorking, &dwBuffSize);
  2953.             if (!hEnum)
  2954.             {
  2955.                 goto exitPoint;
  2956.             }
  2957.         }
  2958.         else if (!FindNextUrlCacheEntry(hEnum, pceiWorking, &dwBuffSize))
  2959.         {
  2960.             //  BUGBUG chrisfra 4/3/97 should we distinquish eod vs hard errors?
  2961.             //  old code for cachevu doesn't (see above in enum code)
  2962.             hres = S_OK;
  2963.             goto exitPoint;
  2964.         }
  2965.         if (SUCCEEDED(hres) &&
  2966.             ((pceiWorking->CacheEntryType & URLHISTORY_CACHE_ENTRY) == URLHISTORY_CACHE_ENTRY) &&
  2967.             _FilterPrefix(pceiWorking, (LPTSTR) pszHistPrefix))
  2968.         {
  2969.             hres = _WriteHistory(pceiWorking->lpszSourceUrlName,
  2970.                                  pceiWorking->ExpireTime,
  2971.                                  pceiWorking->LastModifiedTime,
  2972.                                  FALSE,
  2973.                                  NULL);
  2974.             if (S_FALSE == hres) fNotCopied = TRUE;
  2975.         }
  2976.     }
  2977. exitPoint:
  2978.     if (pceiWorking) LocalFree(pceiWorking);
  2979.     if (hEnum)
  2980.     {
  2981.         FindCloseUrlCache(hEnum);
  2982.     }
  2983.     return SUCCEEDED(hres) ? (fNotCopied ? S_FALSE : S_OK) : hres;
  2984. }
  2985. HRESULT CHistCacheFolder::_GetUserName(LPTSTR pszUserName, DWORD cchUserName)
  2986. {
  2987.     HRESULT hres = _EnsureHistStg();
  2988.     if (SUCCEEDED(hres))
  2989.     {
  2990.         hres = _pUrlHistStg->GetUserName(pszUserName, cchUserName);
  2991.     }
  2992.     return hres;
  2993. }
  2994. //  Makes best efforts attempt to delete old history items in container on a per
  2995. //  user basis.  if we get rid of per user - can just empty whole container
  2996. HRESULT CHistCacheFolder::_DeleteEntries(LPCTSTR pszHistPrefix, PFNDELETECALLBACK pfnDeleteFilter, LPVOID pDelData)
  2997. {
  2998.     HANDLE              hEnum = NULL;
  2999.     HRESULT             hres = S_OK;
  3000.     BOOL                fNotDeleted = FALSE;
  3001.     LPINTERNET_CACHE_ENTRY_INFO pceiWorking;
  3002.     DWORD               dwBuffSize;
  3003.     LPTSTR   pszSearchPattern = NULL;
  3004.     TCHAR   szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];      // username of person logged on
  3005.     DWORD   dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;   // len of this buffer
  3006.     TCHAR    szHistSearchPattern[PREFIX_SIZE+1];                 // search pattern for history items
  3007.     LPITEMIDLIST pidlNotify;
  3008.     StrCpyN(szHistSearchPattern, pszHistPrefix, ARRAYSIZE(szHistSearchPattern));
  3009.     if (FAILED(_GetUserName(szUserName, dwUserNameLen)))
  3010.         szUserName[0] = TEXT('');
  3011.     // BUGBUG: We can't pass in the whole search pattern that we want,
  3012.     // because FindFirstUrlCacheEntry is busted.  It will only look at the
  3013.     // prefix if there is a cache container for that prefix.  So, we can
  3014.     // pass in "Visited: " and enumerate all the history items in the cache,
  3015.     // but then we need to pull out only the ones with the correct username.
  3016.     // StrCpy(szHistSearchPattern, szUserName);
  3017.     pszSearchPattern = szHistSearchPattern;
  3018.     pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY);
  3019.     if (NULL == pceiWorking)
  3020.     {
  3021.         hres = E_OUTOFMEMORY;
  3022.         goto exitPoint;
  3023.     }
  3024.     while (SUCCEEDED(hres))
  3025.     {
  3026.         dwBuffSize = MAX_URLCACHE_ENTRY;
  3027.         if (!hEnum)
  3028.         {
  3029.             hEnum = FindFirstUrlCacheEntry(pszSearchPattern, pceiWorking, &dwBuffSize);
  3030.             if (!hEnum)
  3031.             {