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

Windows Kernel

Development Platform:

Visual C++

  1. //
  2. // Copyright (c) 1996  Microsoft Corporation
  3. //
  4. // Module Name: Url History Interfaces
  5. //
  6. // Author:
  7. //    Zeke Lucas (zekel)  10-April-96
  8. //
  9. // !!! Take NOTE: CUrlHistory *MUST* be thread safe! DANGER, WILL ROBINSON, DANGER!
  10. #include "priv.h"
  11. #include "sccls.h"
  12. #include "ishcut.h"
  13. #include <inetreg.h>
  14. #include "iface.h"
  15. #define DM_UHRETRIEVE   0
  16. #define DM_URLCLEANUP   0
  17. #define DM_HISTGENERATE 0
  18. #define DM_HISTPROP     0
  19. #define DM_HISTEXTRA    0
  20. #define DM_HISTCOMMIT   0
  21. #define DM_HISTSPLAT    0
  22. #define DM_HISTMISS     0
  23. #define DM_HISTNLS      0
  24. #define DW_FOREVERLOW (0xFFFFFFFF)
  25. #define DW_FOREVERHIGH (0x7FFFFFFF)
  26. #ifdef UNICODE
  27.     #define VT_LPTSTR    VT_LPWSTR
  28. #else
  29.     #define VT_LPTSTR    VT_LPSTR
  30. #endif
  31. inline UINT DW_ALIGNED(UINT i) {
  32.     return ((i+3) & 0xfffffffc);
  33. }
  34. inline BOOL IS_DW_ALIGNED(UINT i) {
  35.     return ((i & 3)==0);
  36. }
  37. // Old one (beta-2)
  38. typedef struct _HISTDATAOLD
  39. {
  40.     WORD cbSize;
  41.     DWORD dwFlags;
  42.     WORD wTitleOffset;
  43.     WORD aFragsOffset;
  44.     WORD cFrags;            //right now the top five bits are used for Prop_MshtmlMCS
  45.     WORD wPropNameOffset;    
  46.     WORD wMCSIndex;
  47. } HISTDATAOLD, *LPHISTDATAOLD;
  48. // Forward reference
  49. typedef struct HISTEXTRA* LPHISTEXTRA;
  50. // Version 0.01
  51. //
  52. // PID_INTSITE_WHATSNEW         stored as a HISTEXTRA
  53. // PID_INTSITE_AUTHOR           stored as a HISTEXTRA
  54. // PID_INTSITE_LASTVISIT        from lpCEI->LastAccessTime
  55. // PID_INTSITE_LASTMOD          from lpCEI->LastModifiedTime
  56. // PID_INTSITE_VISITCOUNT       dwVisits
  57. // PID_INTSITE_DESCRIPTION      stored as a HISTEXTRA
  58. // PID_INTSITE_COMMENT          stored as a HISTEXTRA
  59. // PID_INTSITE_FLAGS            dwFlags
  60. // PID_INTSITE_CONTENTLEN       (never used)
  61. // PID_INTSITE_CONTENTCODE      (never used)
  62. // PID_INTSITE_RECURSE          (never used)
  63. // PID_INTSITE_WATCH            dwWatch
  64. // PID_INTSITE_SUBSCRIPTION     stored as a HISTEXTRA
  65. // PID_INTSITE_URL              URL itself
  66. // PID_INTSITE_TITLE            Title
  67. // PID_INTSITE_FRAGMENT         Visited Fragment (private)
  68. //
  69. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  70. // HACKHACK:  If you change this data structure, you must talk
  71. //            to Adrian Canter (adrianc) -- we put a copy of it
  72. //            in winineturlcache401imprt.cxx to make importing
  73. //            from old-style cache happen quick n' dirty
  74. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  75. struct _HISTDATA_V001
  76. {
  77.     UINT  cbSize : 16;           // size of this header
  78.     UINT  cbVer  : 16;           // version
  79.     DWORD           dwFlags;    // PID_INTSITE_FLAGS (PIDISF_ flags)
  80.     DWORD           dwWatch;    // PID_INTSITE_WATCH (PIDISM_ flags)
  81.     DWORD           dwVisits;   // PID_INTSITE_VISITCOUNT
  82. };
  83. #define HISTDATA_VER    2
  84. class CHistoryData : public _HISTDATA_V001
  85. {
  86. public:
  87.     LPHISTEXTRA _GetExtra(void)  const {
  88.         return (LPHISTEXTRA)(((BYTE*)this) + this->cbSize);
  89.     } 
  90.     const HISTEXTRA * _FindExtra(UINT idExtra) const;
  91.     HISTEXTRA * _FindExtraForSave(UINT idExtra) {
  92.         return (HISTEXTRA*)_FindExtra(idExtra);
  93.     }
  94.     void _GetTitle(LPTSTR szTitle, UINT cchMax) const;
  95.     BOOL _HasFragment(LPCTSTR pszFragment) const;
  96.     BOOL _IsOldHistory(void) const {
  97.         return (cbSize==SIZEOF(HISTDATAOLD) && cbVer==0);
  98.     };
  99.     static CHistoryData* s_GetHistoryData(LPINTERNET_CACHE_ENTRY_INFO lpCEI);
  100.     static CHistoryData* s_AllocateHeaderInfo(UINT cbExtra, const CHistoryData* phdPrev, ULONG* pcbTotal);
  101.     HISTEXTRA* CopyExtra(HISTEXTRA* phextCur) const;
  102.     UINT GetTotalExtraSize() const;
  103. };
  104. //
  105. //  Right after HISTDATA (always at cbSize), we have optional (typically
  106. // variable length) data which has following data structure. It may have
  107. // more than one but always has a null-terimiator (cbExtra == 0).
  108. //
  109. struct HISTEXTRA
  110. {
  111.     UINT cbExtra : 16;
  112.     UINT idExtra : 8;   // PID_INTSITE_*
  113.     UINT vtExtra : 8;   // VT_*
  114.     BYTE abExtra[1];    // abExtra[cbExtra-4];
  115.     BOOL IsTerminator(void) const {
  116.         return (this->cbExtra==0);
  117.     }
  118.     const HISTEXTRA* GetNextFast(void) const {
  119.         return (LPHISTEXTRA)(((BYTE*)this) + this->cbExtra);
  120.     }
  121.     HISTEXTRA* GetNextFastForSave(void) const {
  122.         return (LPHISTEXTRA)(((BYTE*)this) + this->cbExtra);
  123.     }
  124.     const HISTEXTRA* GetNext(void) const {
  125.         if (this->cbExtra) {
  126.             return (LPHISTEXTRA)(((BYTE*)this) + this->cbExtra);
  127.         }
  128.         return NULL;
  129.     }
  130. };
  131. // We want to make sure that our history binary data is valid so
  132. // we don't crash or something
  133. BOOL ValidateHistoryData(LPINTERNET_CACHE_ENTRY_INFOA pcei)
  134. {
  135.     DWORD cb = 0;
  136.     if (!pcei->lpHeaderInfo)
  137.     {
  138.         ASSERT(pcei->dwHeaderInfoSize==0);
  139.         pcei->dwHeaderInfoSize = 0;
  140.         return TRUE;
  141.     }
  142.     
  143.     // First, let's check HISTDATA
  144.     CHistoryData* phd = (CHistoryData*)pcei->lpHeaderInfo;
  145.     if ((phd->cbSize!=sizeof(_HISTDATA_V001))
  146.         ||
  147.         (phd->cbSize > pcei->dwHeaderInfoSize))
  148.     {
  149. #ifdef DEBUG
  150.         ASSERT(FALSE && "History data corruption detected. Contact AKABIR.");
  151. #else
  152.         // BUG BUG MUST REMOVE THIS FOR RETAIL RTM
  153.         // REACTIVATE AFTER TECHBETA
  154. //        WCHAR wch = *((PWSTR)NULL);
  155. #endif
  156.         pcei->dwHeaderInfoSize = 0;
  157.         pcei->lpHeaderInfo = NULL;
  158.         return FALSE;
  159.     }
  160.     cb += phd->cbSize;
  161.     
  162.     // Now, let's check HISTEXTRA
  163.     LPHISTEXTRA phe = phd->_GetExtra();
  164.     while (!phe->IsTerminator())
  165.     {
  166.         cb += phe->cbExtra;
  167.         if (cb >= pcei->dwHeaderInfoSize)
  168.         {
  169. #ifdef DEBUG
  170.             ASSERT(FALSE && "History data corruption detected. Contact AKABIR.");
  171. #else
  172.             // BUG BUG MUST REMOVE THIS FOR RTM
  173.         // REACTIVATE AFTER TECHBETA
  174. //            WCHAR wch = *((PWSTR)NULL);
  175. #endif
  176.             // Hmm. We're expecting more data than we got. Not good. Prune the rest off.
  177.             // We're adding 1 for the terminator
  178.             pcei->dwHeaderInfoSize = cb - phe->cbExtra + 4;
  179.             phe->cbExtra = 0;
  180.             return FALSE;
  181.         }
  182.         phe = phe->GetNextFastForSave();
  183.     }
  184.     // Add a DWORD for the terminator
  185.     cb += sizeof(DWORD);
  186.     ASSERT(pcei->dwHeaderInfoSize==cb);
  187.     return TRUE;    
  188. }
  189. //
  190. //  Typically, we need 200-300 bytes to retrieve a cached entry in this
  191. // history database. To avoid allocating memory in 99% of cases, we
  192. // allocate 500 bytes in the stack and call LocalAlloc only if we need
  193. // more than that. 
  194. //
  195. #define DEFAULT_CEI_BUFFER_SIZE (500 * sizeof(WCHAR))
  196. const TCHAR c_szHistoryPrefix[] = TEXT("Visited: ");
  197. struct CEI_PREALLOC {
  198.     LPINTERNET_CACHE_ENTRY_INFO pcei;
  199.     LPCTSTR pszFragment;
  200.     TCHAR szPrefixedUrl[MAX_URL_STRING + ARRAYSIZE(c_szHistoryPrefix)];
  201.     union {
  202. #ifdef UNIX
  203.         double alignOn8ByteBoundary;
  204. #endif /* UNIX */
  205.         INTERNET_CACHE_ENTRY_INFO cei;
  206.         BYTE ab[DEFAULT_CEI_BUFFER_SIZE];
  207.     };
  208.     CEI_PREALLOC() : pcei(NULL), pszFragment(NULL) {}
  209.     ~CEI_PREALLOC() {
  210.         if (pcei && pcei != &cei) {
  211.             TraceMsg(DM_TRACE, "CEI_PREALLOC::dtr freeing pcei");
  212.             LocalFree(pcei);
  213.         }
  214.     }
  215. };
  216. #define VER_HISTDATA    1
  217. typedef CHistoryData HISTDATA;
  218. typedef HISTDATA* LPHISTDATA;
  219. //  CUrlHistory manages the other interfaces and handles alot of the basic functions
  220. class   CUrlHistory : public IUrlHistoryPriv
  221. {
  222. public:
  223.     CUrlHistory (void);
  224.     ~CUrlHistory(void);
  225.     // IUnknown methods
  226.     virtual STDMETHODIMP  QueryInterface(REFIID riid, PVOID *ppvObj);
  227.     virtual STDMETHODIMP_(ULONG) AddRef(void);
  228.     virtual STDMETHODIMP_(ULONG) Release(void);
  229.     // IUrlHistoryStg methods
  230.     STDMETHODIMP AddUrl(LPCWSTR pwszUrl, LPCWSTR pwszTitle, DWORD dwFlags);
  231.     STDMETHODIMP DeleteUrl(LPCWSTR pwszUrl, DWORD dwFlags);
  232.     STDMETHODIMP QueryUrl(LPCWSTR pwszUrl, DWORD dwFlags, LPSTATURL lpSTATURL);
  233.     STDMETHODIMP BindToObject(LPCWSTR pwszUrl, REFIID riid, void **ppvOut);
  234.     STDMETHODIMP EnumUrls(IEnumSTATURL **ppEnum);
  235.     // IUrlHistoryStg2 methods
  236.     STDMETHODIMP AddUrlAndNotify(LPCWSTR pwszUrl, LPCWSTR pwszTitle, DWORD dwFlags, BOOL fWriteHistory, IOleCommandTarget *poctNotify, IUnknown *punkSFHistory);
  237.     STDMETHODIMP ClearHistory();
  238.     // IUrlHistoryPriv methods
  239.     STDMETHOD(QueryUrlA)(LPCSTR pszUrl, DWORD dwFlags, LPSTATURL lpSTATURL);
  240.     STDMETHOD(CleanupHistory)(void);
  241.     STDMETHOD_(DWORD,GetDaysToKeep)(void) { return s_GetDaysToKeep(); }
  242.     STDMETHOD(GetProperty)(LPCTSTR pszUrl, PROPID pid, PROPVARIANT* pvarOut);
  243.     STDMETHOD(GetUserName)(LPTSTR pszUserName, DWORD cchUserName);
  244.     STDMETHOD(AddUrlAndNotifyCP)(LPCWSTR pwszUrl, LPCWSTR pwszTitle, DWORD dwFlags, BOOL fWriteHistory, IOleCommandTarget *poctNotify, IUnknown *punkSFHistory, UINT* pcodepage);
  245.    
  246.     static void  s_Init();
  247.     static DWORD   s_GetDaysToKeep(void);
  248. #if defined(ux10) && defined(UNIX)
  249.     static void s_SetDaysToKeep(DWORD dwDays);
  250. #endif
  251. protected:
  252.     void _WriteToHistory(LPCTSTR pszPrefixedurl,
  253.                          FILETIME& ftExpires,
  254.                          IOleCommandTarget *poctNotify,
  255.                          IUnknown *punkSFHistory);
  256.     friend class CEnumSTATURL;
  257.     // friend class CUrlHObj;
  258.     friend class IntsiteProp;
  259.     static HRESULT s_CleanupHistory(void);
  260.     static HRESULT s_EnumUrls(IEnumSTATURL **ppEnum);
  261.     static HRESULT s_DeleteUrl(LPCWSTR pwszUrl, DWORD dwFlags);
  262.     
  263.     static void s_ConvertToPrefixedUrlW(  
  264.                                 IN LPCWSTR pwszUrl,
  265.                                 OUT LPTSTR pszPrefixedUrl,
  266.                                 IN DWORD cchPrefixedUrl, 
  267.                                 OUT LPCTSTR *ppszFragment
  268.                               );
  269.     static HRESULT s_QueryUrlCommon(
  270.                           LPCTSTR lpszPrefixedUrl,
  271.                           LPCTSTR lpszFragment,
  272.                           DWORD dwFlags,
  273.                           LPSTATURL lpSTATURL
  274.                           );
  275.     static void s_RetrievePrefixedUrlInfo(
  276.                 LPCTSTR lpszUrl, CEI_PREALLOC* pbuf);
  277.     static BOOL s_CommitUrlCacheEntry(LPCTSTR pszPrefixedUrl, 
  278.                         LPINTERNET_CACHE_ENTRY_INFO pcei);
  279.     static BOOL s_IsCached(IN LPCTSTR pszUrl)
  280.         { return ::GetUrlCacheEntryInfoEx(pszUrl, NULL, NULL, NULL, NULL, NULL, INTERNET_CACHE_FLAG_ALLOW_COLLISIONS); }
  281.     static HISTDATA* s_GenerateHeaderInfo(
  282.             IN LPCTSTR pszTitle, 
  283.             IN HISTDATA* phdPrev,
  284.             IN LPCTSTR pszFragment,
  285.             OUT LPDWORD pcbHeader
  286.             );
  287.     static HRESULT s_GenerateSTATURL(IN LPINTERNET_CACHE_ENTRY_INFO lpCEI, IN DWORD dwFlags, OUT LPSTATURL lpsu);
  288.     static void s_UpdateIcon(Intshcut* pintshcut, DWORD dwFlags);
  289.     DWORD   _cRef;
  290.     static TCHAR   s_szUserPrefix[INTERNET_MAX_USER_NAME_LENGTH + 1];
  291.     static DWORD   s_cchUserPrefix ;
  292.     static DWORD   s_dwDaysToKeep;
  293.     
  294. };
  295. class CEnumSTATURL      : public IEnumSTATURL
  296. {
  297. public:
  298.     CEnumSTATURL() : _cRef(1) {}
  299.     ~CEnumSTATURL();
  300.     // IUnknown methods
  301.     STDMETHODIMP  QueryInterface(REFIID riid, PVOID *ppvObj);
  302.     STDMETHODIMP_(ULONG) AddRef(void);
  303.     STDMETHODIMP_(ULONG) Release(void);
  304.     //  IEnumXXXX methods
  305.     STDMETHODIMP Next (ULONG celt, LPSTATURL rgelt, ULONG * pceltFetched) ;
  306.     STDMETHODIMP Skip(ULONG celt) ;
  307.     STDMETHODIMP Reset(void) ;
  308.     STDMETHODIMP Clone(IEnumSTATURL ** ppenum) ;
  309.     //  IEnumSTATURL methods
  310.     STDMETHODIMP SetFilter(LPCWSTR poszFilter, DWORD dwFlags) ;
  311. private:
  312.     HRESULT RetrieveFirstUrlInfo(void);
  313.     HRESULT RetrieveNextUrlInfo(void);
  314.     DWORD _cRef;
  315.     //  search object parameters
  316.     LPWSTR m_poszFilter;
  317.     DWORD  m_dwFilter;
  318.     HANDLE m_hEnum;
  319.     TCHAR _szPrefixedUrl[MAX_URL_STRING];
  320.     DWORD m_cchPrefixedUrl;
  321.     LPCTSTR m_lpszFragment;
  322.     LPINTERNET_CACHE_ENTRY_INFO m_lpCEI;
  323.     DWORD m_cbCEI;
  324. };
  325. #if defined(ux10) && defined(UNIX)
  326. //Work around for mmap limitation in hp-ux10. Change the corresponding
  327. //value even in inetcpl/general.cpp
  328. #define MAX_HISTORY_DAYS        30
  329. #endif
  330. #define FILETIME_SEC 10000000
  331. #define SECS_PER_DAY (60 * 60 * 24)
  332. #define CCHHISTORYPREFIX (ARRAYSIZE(c_szHistoryPrefix) - 1)
  333. #define CLEANUP_HISTORY_INTERVAL (24 * 60 * 60 * 1000) // One day, in milliseconds
  334. DWORD g_tCleanupHistory = 0;
  335. #define OFFSET_TO_LPTSTR(p, o) ( (LPTSTR) ( (LPBYTE) (p) + (o) ) )
  336. #define OFFSET_TO_LPBYTE(p, o) ( (LPBYTE) ( (LPBYTE) (p) + (o) ) )
  337. #define OFFSET_TO_LPWORD(p, o) ( (LPWORD) ( (LPBYTE) (p) + (o) ) )
  338. #define LPTSTR_TO_OFFSET(p, s) ( (WORD) ( (LPTSTR) (s) - (LPTSTR) (p) ) )
  339. #define LPBYTE_TO_OFFSET(p, b) ( (WORD) ( (LPBYTE) (b) - (LPBYTE) (p) ) )
  340. // NOTE: BUGBUG chrisfra 3/26/97 , extcachevupriv.h has a duplicate copy of this
  341. // structure and uses it to access cache.  this needs to be covered by procedural or
  342. // object interface and moved to a common location.
  343. // This structure uses the flags bits as follows: if HFL_VERSIONED is true, then
  344. // the rest of the flags word is the version.
  345. #define HFL_VERSIONED (0x80000000)
  346. //
  347. // We store binary data in the lpHeaderInfo field.  CommitUrlCacheEntryW tries
  348. // to convert this data to ansi and messes it up.  To get around this we thunk
  349. // through to the A version CommitUrlCacheEntry.
  350. //
  351. BOOL
  352. CommitUrlCacheEntryBinary(
  353.     IN LPCWSTR  lpszUrlName,
  354.     IN FILETIME ExpireTime,
  355.     IN FILETIME LastModifiedTime,
  356.     IN DWORD CacheEntryType,
  357.     IN LPBYTE lpHeaderInfo,
  358.     IN DWORD dwHeaderSize
  359. )
  360. {
  361.     ASSERT(lpszUrlName);
  362.     CHAR szUrl[MAX_URL_STRING + ARRAYSIZE(c_szHistoryPrefix)];
  363.     SHUnicodeToAnsi(lpszUrlName, szUrl, ARRAYSIZE(szUrl));
  364.     INTERNET_CACHE_ENTRY_INFOA cei;
  365.     cei.lpHeaderInfo = (LPSTR)lpHeaderInfo;
  366.     cei.dwHeaderInfoSize = dwHeaderSize;
  367.     ValidateHistoryData(&cei);
  368.     return CommitUrlCacheEntryA(szUrl, NULL, ExpireTime, LastModifiedTime,
  369.                                 CacheEntryType, lpHeaderInfo, dwHeaderSize,
  370.                                 NULL, NULL);
  371. }
  372. GetUrlCacheEntryInfoBinary(
  373.     IN LPCWSTR lpszUrlName,
  374.     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
  375.     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
  376.     )
  377. {
  378.     ASSERT(lpszUrlName);
  379.     BOOL fRet;
  380.     CHAR szUrl[MAX_URL_STRING + ARRAYSIZE(c_szHistoryPrefix)];
  381.     SHUnicodeToAnsi(lpszUrlName, szUrl, ARRAYSIZE(szUrl));
  382.     //
  383.     // Warning!  This doesn't convert any of thge string parameters in
  384.     // lpCacheEntryInfo back to unicode.  History only uses
  385.     // lpCacheEntryInfo->lpHeaderInfo so this isn't a problem.
  386.     //
  387.     fRet =  GetUrlCacheEntryInfoA(szUrl,
  388.                                  (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
  389.                                  lpdwCacheEntryInfoBufferSize);
  390.     //
  391.     // Set unused out paramters to NULL incase someone tries to use them
  392.     //
  393.     lpCacheEntryInfo->lpszSourceUrlName = NULL;
  394.     lpCacheEntryInfo->lpszLocalFileName = NULL;
  395.     lpCacheEntryInfo->lpszFileExtension = NULL;
  396.     if (fRet)
  397.     {
  398.         ValidateHistoryData((LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo);
  399.     }
  400.     return fRet;
  401. }
  402. //
  403. // Warning!  This function converts cei structures for use by history.  It is
  404. // not a generic conversion.  It converts the minimum data required by history.
  405. //
  406. int
  407. CacheEntryInfoAToCacheEntryInfoW(
  408.     LPINTERNET_CACHE_ENTRY_INFOA pceiA,
  409.     LPINTERNET_CACHE_ENTRY_INFOW pceiW,
  410.     int cbceiW
  411.     )
  412. {
  413.     int nRet;
  414.     ASSERT(pceiA->lpszSourceUrlName);
  415.     int cchSourceUrlName = lstrlenA(pceiA->lpszSourceUrlName) + 1;
  416.     int cbRequired = sizeof(INTERNET_CACHE_ENTRY_INFOA) +
  417.                      pceiA->dwHeaderInfoSize + 
  418.                      cchSourceUrlName * sizeof(WCHAR);
  419.     if (cbRequired <= cbceiW)
  420.     {
  421.         ASSERT(sizeof(*pceiA) == sizeof(*pceiW));
  422.         // Copy the structure.
  423.         *pceiW = *(INTERNET_CACHE_ENTRY_INFOW*)pceiA;
  424.         // Append the binary data.  Note dwHeaderInfoSize is already copied.
  425.         pceiW->lpHeaderInfo = (LPWSTR)(pceiW + 1);
  426.         memcpy(pceiW->lpHeaderInfo, pceiA->lpHeaderInfo, pceiA->dwHeaderInfoSize);
  427.         // Append the source url name.
  428.         pceiW->lpszSourceUrlName = (LPWSTR)((BYTE*)(pceiW + 1) + pceiW->dwHeaderInfoSize);
  429.         SHAnsiToUnicode(pceiA->lpszSourceUrlName, pceiW->lpszSourceUrlName,
  430.                         cchSourceUrlName);
  431.         // Null out bogus pointers so we'll fault if someone deref's them
  432.         pceiW->lpszLocalFileName = NULL;
  433.         pceiW->lpszFileExtension = NULL;
  434.         nRet = 0;
  435.     }
  436.     else
  437.     {
  438.         nRet = cbRequired;
  439.     }
  440.     return nRet;
  441. }
  442. HANDLE
  443. FindFirstUrlCacheEntryBinary(
  444.     IN LPCWSTR lpszUrlSearchPattern,
  445.     OUT LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
  446.     IN OUT LPDWORD lpdwFirstCacheEntryInfoBufferSize
  447.     )
  448. {
  449.     ASSERT(NULL != lpszUrlSearchPattern);
  450.     ASSERT(NULL != lpFirstCacheEntryInfo);
  451.     ASSERT(NULL != lpdwFirstCacheEntryInfoBufferSize);
  452.     HANDLE hRet;
  453.     CHAR szPattern[MAX_PATH];
  454.     ASSERT(lstrlenW(lpszUrlSearchPattern) < ARRAYSIZE(szPattern));
  455.     SHUnicodeToAnsi(lpszUrlSearchPattern, szPattern, ARRAYSIZE(szPattern));
  456.     BYTE ab[MAX_CACHE_ENTRY_INFO_SIZE];
  457.     INTERNET_CACHE_ENTRY_INFOA* pceiA = (INTERNET_CACHE_ENTRY_INFOA*)ab;
  458.     DWORD dwSize;
  459.     BOOL fAllocated = FALSE;
  460.     pceiA->dwStructSize = dwSize = sizeof(ab);
  461.     hRet = FindFirstUrlCacheEntryA(szPattern, pceiA, &dwSize);
  462.     if (NULL == hRet && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  463.     {
  464.         pceiA = (INTERNET_CACHE_ENTRY_INFOA*)LocalAlloc(LPTR, dwSize);
  465.         if (pceiA)
  466.         {
  467.             fAllocated = TRUE;
  468.             pceiA->dwStructSize = dwSize;
  469.             hRet = FindFirstUrlCacheEntryA(szPattern, pceiA, &dwSize);
  470.             
  471.             ASSERT(hRet || GetLastError() != ERROR_INSUFFICIENT_BUFFER);
  472.         }
  473.         else
  474.         {
  475.             SetLastError(ERROR_OUTOFMEMORY);
  476.         }
  477.     }
  478.     
  479.     if (hRet)
  480.     {
  481.         int nRet;
  482.         ValidateHistoryData(pceiA);
  483.         nRet = CacheEntryInfoAToCacheEntryInfoW(pceiA, lpFirstCacheEntryInfo,
  484.                                                 *lpdwFirstCacheEntryInfoBufferSize);
  485.         if (nRet)
  486.         {
  487.             FindCloseUrlCache(hRet);
  488.             hRet = NULL;
  489.             *lpdwFirstCacheEntryInfoBufferSize = nRet;
  490.             SetLastError(ERROR_INSUFFICIENT_BUFFER);
  491.         }
  492.     }
  493.     if (fAllocated)
  494.         LocalFree(pceiA);
  495.     return hRet;
  496. }
  497. BOOL
  498. FindNextUrlCacheEntryBinary(
  499.     IN HANDLE hEnumHandle,
  500.     OUT LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
  501.     IN OUT LPDWORD lpdwNextCacheEntryInfoBufferSize
  502.     )
  503. {
  504.     ASSERT(NULL != hEnumHandle);
  505.     ASSERT(NULL != lpNextCacheEntryInfo);
  506.     ASSERT(NULL != lpdwNextCacheEntryInfoBufferSize);
  507.     BOOL fRet;
  508.     BYTE ab[MAX_CACHE_ENTRY_INFO_SIZE];
  509.     INTERNET_CACHE_ENTRY_INFOA* pceiA = (INTERNET_CACHE_ENTRY_INFOA*)ab;
  510.     DWORD dwSize;
  511.     BOOL fAllocated = FALSE;
  512.     pceiA->dwStructSize = dwSize = sizeof(ab);
  513.     fRet = FindNextUrlCacheEntryA(hEnumHandle, pceiA, &dwSize);
  514.     
  515.     if (!fRet && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  516.     {
  517.         pceiA = (INTERNET_CACHE_ENTRY_INFOA*)LocalAlloc(LPTR, dwSize);
  518.         if (pceiA)
  519.         {
  520.             fAllocated = TRUE;
  521.             pceiA->dwStructSize = dwSize;
  522.             fRet = FindNextUrlCacheEntryA(hEnumHandle, pceiA, &dwSize);
  523.             
  524.             ASSERT(fRet || GetLastError() != ERROR_INSUFFICIENT_BUFFER);
  525.         }
  526.         else
  527.         {
  528.             SetLastError(ERROR_OUTOFMEMORY);
  529.         }
  530.     }
  531.     if (fRet)
  532.     {
  533.         int nRet;
  534.         ValidateHistoryData(pceiA);
  535.         nRet = CacheEntryInfoAToCacheEntryInfoW(pceiA, lpNextCacheEntryInfo,
  536.                                                 *lpdwNextCacheEntryInfoBufferSize);
  537.         if (nRet)
  538.         {
  539.             fRet = FALSE;
  540.             *lpdwNextCacheEntryInfoBufferSize = nRet;
  541.             SetLastError(ERROR_INSUFFICIENT_BUFFER);
  542.         }
  543.     }
  544.     if (fAllocated)
  545.         LocalFree(pceiA);
  546.   
  547.     return fRet;
  548. }
  549. #define DEFAULT_DAYS_TO_KEEP    21
  550. static const TCHAR c_szRegValDaysToKeep[] = TEXT("DaysToKeep");
  551. static const TCHAR c_szRegValDirectory[] = TEXT("Directory");
  552. #ifdef UNIX
  553. #define DIR_SEPARATOR_CHAR  TEXT('/')
  554. #else
  555. #define DIR_SEPARATOR_CHAR  TEXT('\')
  556. #endif
  557. #ifdef UNICODE
  558. #define SHGETFOLDERPATH "SHGetFolderPathW"
  559. #else
  560. #define SHGETFOLDERPATH "SHGetFolderPathA"
  561. #endif
  562. #undef SHGetFolderPath
  563. typedef HRESULT (*PFNSHGETFOLDERPATH)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
  564. HMODULE g_hmodShfolder = NULL;
  565. HRESULT SHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
  566. {
  567.     if (!g_hmodShfolder)
  568.     {
  569.         // if we are on NT5 skip shfolder and go straight to shell32.dll
  570.         g_hmodShfolder = LoadLibrary( IsOS( OS_NT5 )? TEXT("shell32.dll") : TEXT("shfolder.dll"));
  571.     }
  572.     HRESULT hr = E_FAIL;
  573.     if (g_hmodShfolder) 
  574.     {
  575.         PFNSHGETFOLDERPATH pfn = (PFNSHGETFOLDERPATH)GetProcAddress(g_hmodShfolder, SHGETFOLDERPATH);
  576.         if (pfn)
  577.         {
  578.             hr = pfn(hwnd, csidl, hToken, dwFlags, pszPath);
  579.         }
  580.     }
  581.     return hr;
  582. }
  583. HRESULT SHGetHistoryPIDL(LPITEMIDLIST *ppidlHistory)
  584. {
  585.     *ppidlHistory = NULL;
  586.     TCHAR szHistory[MAX_PATH];
  587.     szHistory[0] = 0;
  588.     HRESULT hres = SHGetFolderPath(NULL, CSIDL_HISTORY | CSIDL_FLAG_CREATE, NULL, 0, szHistory);
  589.     if (hres != S_OK)
  590.     {
  591.         GetHistoryFolderPath(szHistory, ARRAYSIZE(szHistory));
  592.         PathRemoveFileSpec(szHistory);  // get the trailing slash
  593.         PathRemoveFileSpec(szHistory);  // trim the "content.ie5" junk
  594.     }
  595.     if (szHistory[0])
  596.     {
  597.         TCHAR szIniFile[MAX_PATH];
  598.         PathCombine(szIniFile, szHistory, TEXT("desktop.ini"));
  599.         if (GetFileAttributes(szIniFile) == -1)
  600.         {
  601.             DWORD dwAttrib = GetFileAttributes(szHistory);
  602.             dwAttrib &= ~FILE_ATTRIBUTE_HIDDEN;
  603.             dwAttrib |=  FILE_ATTRIBUTE_SYSTEM;
  604.             // make sure system, but not hidden
  605.             SetFileAttributes(szHistory, dwAttrib);
  606.             WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("ConfirmFileOp"), TEXT("0"), szIniFile);
  607.             WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID"), TEXT("{FF393560-C2A7-11CF-BFF4-444553540000}"), szIniFile);
  608.         }
  609.         IShellFolder *psfDesktop;
  610.         hres = SHGetDesktopFolder(&psfDesktop);
  611.         if (SUCCEEDED(hres)) 
  612.         {
  613.             hres = psfDesktop->ParseDisplayName(NULL, NULL, 
  614.                                     szHistory, NULL, ppidlHistory, NULL); 
  615.             psfDesktop->Release();
  616.         }
  617.     }
  618.     else
  619.         hres = E_FAIL;
  620.     return hres;
  621. }
  622. //
  623. // This function is called from hist/hsfolder.cpp
  624. //
  625. HRESULT CUrlHistory::GetUserName(LPTSTR pszUserName, DWORD cchUserName)
  626. {
  627.     s_Init();
  628.     
  629.     if (cchUserName < s_cchUserPrefix)
  630.     {
  631.         return E_FAIL;
  632.     }
  633.     CopyMemory(pszUserName, s_szUserPrefix, (s_cchUserPrefix-1) * sizeof(TCHAR));
  634.     pszUserName[s_cchUserPrefix-1] = 0;
  635.     return S_OK;
  636. }
  637. #if defined (ux10) && defined(UNIX)
  638. void CUrlHistory::s_SetDaysToKeep(DWORD dwDays)
  639. {
  640.     HKEY hk;
  641.     DWORD dwDisp;
  642.     DWORD Error = RegCreateKeyEx(
  643.                                  HKEY_CURRENT_USER,
  644.                                  REGSTR_PATH_URLHISTORY,
  645.                                  0, NULL, 0,
  646.                                  KEY_WRITE,
  647.                                  NULL,
  648.                                  &hk,
  649.                                  &dwDisp);
  650.     if(ERROR_SUCCESS != Error)
  651.     {
  652.         ASSERT(FALSE);
  653.         return;
  654.     }
  655.     Error = RegSetValueEx(
  656.                           hk,
  657.                           c_szRegValDaysToKeep,
  658.                           0,
  659.                           REG_DWORD,
  660.                           (LPBYTE) &dwDays,
  661.                           sizeof(dwDays));
  662.     ASSERT(ERROR_SUCCESS == Error);
  663.     RegCloseKey(hk);
  664.     return;
  665. }
  666. #endif
  667. //
  668. // This function is called from hist/hsfolder.cpp
  669. //
  670. DWORD CUrlHistory::s_GetDaysToKeep(void)
  671. {
  672.     HKEY hk;
  673.     DWORD cbDays = SIZEOF(DWORD);
  674.     DWORD dwDays = DEFAULT_DAYS_TO_KEEP;
  675.     DWORD dwType;
  676.     DWORD Error = RegOpenKeyEx(
  677.                                HKEY_CURRENT_USER,
  678.                                REGSTR_PATH_URLHISTORY,
  679.                                0,
  680.                                KEY_READ,
  681.                                &hk);
  682.     if(Error)
  683.     {
  684.         Error = RegOpenKeyEx(
  685.                              HKEY_LOCAL_MACHINE,
  686.                              REGSTR_PATH_URLHISTORY,
  687.                              0,
  688.                              KEY_READ,
  689.                              &hk);
  690.     }
  691.     if(!Error)
  692.     {
  693.         Error = RegQueryValueEx(
  694.                                 hk,
  695.                                 c_szRegValDaysToKeep,
  696.                                 0,
  697.                                 &dwType,
  698.                                 (LPBYTE) &dwDays,
  699.                                 &cbDays);
  700.         RegCloseKey(hk);
  701.     }
  702.     return dwDays;
  703. }
  704. IUrlHistoryPriv* g_puhUrlHistory = NULL;
  705. void CUrlHistory_CleanUp()
  706. {
  707.     // Release will clean up the global
  708.     ENTERCRITICAL;
  709.     if (g_puhUrlHistory)
  710.         g_puhUrlHistory->Release();
  711.     LEAVECRITICAL;
  712. }
  713. STDAPI CUrlHistory_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  714. {
  715.     HRESULT hr = E_OUTOFMEMORY;
  716.     *ppunk = NULL;
  717.     // !!! Take NOTE: CUrlHistory *MUST* be thread safe!
  718.     // aggregation checking is handled in class factory
  719.     ENTERCRITICAL;
  720.     
  721.     if (!g_puhUrlHistory) 
  722.     {
  723.         CUrlHistory *pcuh = new CUrlHistory;
  724.         if (pcuh) 
  725.         {
  726.             g_puhUrlHistory = SAFECAST(pcuh, IUrlHistoryPriv *);
  727. #ifdef DEBUG
  728.             // The memory tracking code thinks this psf will leak 
  729.             remove_from_memlist(g_puhUrlHistory);
  730. #endif
  731.         }
  732.     }
  733.     if (g_puhUrlHistory)
  734.     {
  735.         *ppunk = SAFECAST(g_puhUrlHistory, IUnknown*);
  736.         g_puhUrlHistory->AddRef();
  737.         hr = S_OK;
  738.     }
  739.     LEAVECRITICAL;
  740.     return hr;
  741. }
  742. //
  743. //  Public members of CUrlHistory
  744. //
  745. CUrlHistory::CUrlHistory(void) : _cRef(1)
  746. {
  747.     //
  748.     // Update s_dwDaysToKeep for each call
  749.     //
  750.     s_dwDaysToKeep = s_GetDaysToKeep();
  751.     
  752. #if defined(ux10) && defined(UNIX)
  753. //Work around for mmap limitation in hp-ux10
  754.     if (s_dwDaysToKeep > MAX_HISTORY_DAYS)
  755.     {
  756.        s_dwDaysToKeep = MAX_HISTORY_DAYS;
  757.        s_SetDaysToKeep(s_dwDaysToKeep);
  758.     }
  759. #endif
  760. #ifdef DEBUG
  761.     if (g_dwPrototype & 0x00000020) {
  762.         s_CleanupHistory();
  763.     }
  764. #endif
  765.     DllAddRef();
  766. }
  767. CUrlHistory::~CUrlHistory(void)
  768. {
  769.     DllRelease();
  770. }
  771. HRESULT LoadHistoryShellFolder(IUnknown *punk, IHistSFPrivate **ppsfpHistory)
  772. {
  773.     HRESULT hr;
  774.     *ppsfpHistory = NULL;
  775.     if (punk)
  776.     {
  777.         hr = punk->QueryInterface(IID_IHistSFPrivate, (void **)ppsfpHistory);
  778.     }
  779.     else
  780.     {
  781.         LPITEMIDLIST pidlHistory;
  782.         hr = SHGetHistoryPIDL(&pidlHistory);
  783.         if (SUCCEEDED(hr))
  784.         {
  785.             hr = SHBindToObject(NULL, IID_IHistSFPrivate, pidlHistory, (void **)ppsfpHistory);
  786.             ILFree(pidlHistory);
  787.         }
  788.     }
  789.     return hr;
  790. }
  791. //  ClearHistory on a per user basis.  moved from inetcpl to facilitate changes in
  792. //  implementation.
  793. HRESULT CUrlHistory::ClearHistory()
  794. {
  795.     HRESULT hr;
  796.     IEnumSTATURL *penum;
  797.     IHistSFPrivate *psfpHistory = NULL;
  798.     hr = THR(EnumUrls(&penum));
  799.     if (SUCCEEDED(hr))
  800.     {
  801.         penum->SetFilter(NULL, STATURL_QUERYFLAG_NOTITLE);
  802.         ULONG cFetched;
  803.         STATURL rsu[1] = {{sizeof(STATURL), NULL, NULL}};
  804.         while (SUCCEEDED(penum->Next(1, rsu, &cFetched)) && cFetched)
  805.         {
  806.             ASSERT(rsu[0].pwcsUrl);
  807.             hr = THR(DeleteUrl(rsu[0].pwcsUrl, URLFLAG_DONT_DELETE_SUBSCRIBED));
  808.             OleFree(rsu[0].pwcsUrl);
  809.             rsu[0].pwcsUrl = NULL;
  810.             ASSERT(!rsu[0].pwcsTitle);
  811.         }
  812.         penum->Release();
  813.     }
  814.     hr = LoadHistoryShellFolder(NULL, &psfpHistory);
  815.     if (SUCCEEDED(hr))
  816.     {
  817.         hr = psfpHistory->ClearHistory();
  818.         psfpHistory->Release();
  819.     }
  820.     return hr;
  821. }
  822. extern void _FileTimeDeltaDays(FILETIME *pftBase, FILETIME *pftNew, int Days);
  823. HRESULT CUrlHistory::s_CleanupHistory(void)
  824. {
  825.     TraceMsg(DM_URLCLEANUP, "CUH::s_CleanupHistory called");
  826.     HRESULT hr;
  827.     DWORD tCurrent = GetTickCount();
  828.     if (!g_tCleanupHistory || (tCurrent > g_tCleanupHistory + CLEANUP_HISTORY_INTERVAL)) {
  829.         g_tCleanupHistory = tCurrent;
  830.     } else {
  831. #ifdef DEBUG
  832.         if (!(g_dwPrototype & 0x00000020))
  833. #endif
  834.         return S_OK;
  835.     }
  836.     SYSTEMTIME st;
  837.     FILETIME ftNow;
  838.     FILETIME ftOldest;
  839.     GetSystemTime(&st);
  840.     SystemTimeToFileTime(&st, &ftNow);
  841.     //  Note, due to fact we show whole weeks, we need to keep item around an extra
  842.     //  7 days.  Note, we force checking current days to keep so that we aren't out
  843.     //  of synch with history view
  844.     _FileTimeDeltaDays(&ftNow, &ftOldest, -((int)s_GetDaysToKeep() + 7));
  845.     IEnumSTATURL * penum = NULL;
  846.     if (SUCCEEDED(s_EnumUrls(&penum)))
  847.     {
  848.         STATURL rsu[1] = {{sizeof(STATURL), NULL, NULL}};
  849.         ULONG cFetched = 0;
  850.         penum->SetFilter(NULL, STATURL_QUERYFLAG_NOTITLE);
  851.         while (S_OK == penum->Next(1, rsu, &cFetched))
  852.         {
  853.             ASSERT(cFetched);
  854.             ASSERT(rsu[0].pwcsUrl);
  855.             ASSERT(rsu[0].pwcsTitle==NULL);
  856. #ifdef DEBUG
  857.             TCHAR szUrl[MAX_URL_STRING];
  858.             SHUnicodeToTChar(rsu[0].pwcsUrl, szUrl, ARRAYSIZE(szUrl));
  859. #endif
  860.             // check to see if expires is not special && ftLastUpdated is earlier
  861.             // than we need
  862.             if (CompareFileTime(&(rsu[0].ftLastUpdated), &ftOldest) < 0 &&
  863.                 (rsu[0].ftExpires.dwLowDateTime != DW_FOREVERLOW ||
  864.                  rsu[0].ftExpires.dwHighDateTime != DW_FOREVERHIGH))
  865.             {
  866.                 hr = THR(s_DeleteUrl(rsu[0].pwcsUrl, 0));
  867. #ifdef DEBUG
  868.                 TraceMsg(DM_URLCLEANUP, "CUH::s_Cleanup deleting %s", szUrl);
  869. #endif
  870.             } else {
  871. #ifdef DEBUG
  872.                 TraceMsg(DM_URLCLEANUP, "CUH::s_Cleanup keeping  %s", szUrl);
  873. #endif
  874.             }
  875.             CoTaskMemFree(rsu[0].pwcsUrl);
  876.             rsu[0].pwcsUrl = NULL;
  877.             cFetched = 0;
  878.             
  879.             ASSERT(!rsu[0].pwcsTitle);
  880.         }
  881.         penum->Release();
  882.     }
  883.     else 
  884.         ASSERT(FALSE);
  885.     TraceMsg(DM_URLCLEANUP, "CUH::s_CleanupHistory (expensive!) just called");
  886.     return S_OK;
  887. }
  888. HRESULT CUrlHistory::CleanupHistory()
  889. {
  890.     return CUrlHistory::s_CleanupHistory();
  891. }
  892. TCHAR CUrlHistory::s_szUserPrefix[INTERNET_MAX_USER_NAME_LENGTH + 1] = TEXT("");
  893. DWORD CUrlHistory::s_cchUserPrefix = 0;
  894. DWORD CUrlHistory::s_dwDaysToKeep = 0;
  895. void CUrlHistory::s_Init(void)
  896. {
  897.     // Cache the user name only once per process
  898.     if (!s_cchUserPrefix)
  899.     {
  900.         ENTERCRITICAL;
  901.         // Maybe it changed since entering the crit sec.
  902.         // This really happened to me (BryanSt)
  903.         // We do it twice for perf reasons.
  904.         if (!s_cchUserPrefix)
  905.         {
  906.             ASSERT(s_szUserPrefix[0] == '');
  907.             s_cchUserPrefix = ARRAYSIZE(s_szUserPrefix);
  908.             //  Get the current user or set to default
  909.             ::GetUserName(s_szUserPrefix, &s_cchUserPrefix);
  910.             StrCatBuff(s_szUserPrefix, TEXT("@"), ARRAYSIZE(s_szUserPrefix));
  911.             s_cchUserPrefix = lstrlen(s_szUserPrefix);
  912.         }
  913.         LEAVECRITICAL;
  914.     }
  915. }
  916. HRESULT CUrlHistory::QueryInterface(REFIID riid, PVOID *ppvObj)
  917. {
  918.     HRESULT hr = E_NOINTERFACE;
  919.     *ppvObj = NULL;
  920.     if (IsEqualIID(riid, IID_IUnknown) || 
  921.         IsEqualIID(riid, IID_IUrlHistoryStg2) ||
  922.         IsEqualIID(riid, IID_IUrlHistoryPriv) ||
  923.          IsEqualIID(riid, IID_IUrlHistoryStg))
  924.     {
  925.         AddRef();
  926.         *ppvObj = (LPVOID) SAFECAST(this, IUrlHistoryPriv *);
  927.         hr = S_OK;
  928.     }
  929.     else if (IsEqualIID(riid, CLSID_CUrlHistory))
  930.     {
  931.         AddRef();
  932.         *ppvObj = (LPVOID) this;
  933.         hr = S_OK;
  934.     }
  935.     return hr;
  936. }
  937. ULONG CUrlHistory::AddRef(void)
  938. {
  939.     _cRef++;
  940.     return _cRef;
  941. }
  942. ULONG CUrlHistory::Release(void)
  943. {
  944.     ASSERT(_cRef > 0);
  945.     _cRef--;
  946.     if (!_cRef)
  947.     {
  948.         //time to go bye bye
  949.         ENTERCRITICAL;
  950.         g_puhUrlHistory = NULL;
  951.         LEAVECRITICAL;
  952.         delete this;
  953.         return 0;
  954.     }
  955.     return _cRef;
  956. }
  957. //
  958. // Converts a normal URL to a URL with the correct cache prefix.     
  959. // it also finds a fragment (local Anchor) if it exists in the URL.  
  960. //                                                                   
  961. // if the URL is invalid, then the returned lplpszPrefixedUrl is just
  962. // the prefix.  this is used primarily for doing enumeration.        
  963. //
  964. void CUrlHistory::s_ConvertToPrefixedUrlW(
  965.                                        IN LPCWSTR pszUrl,
  966.                                        OUT LPTSTR pszPrefixedUrl,
  967.                                        IN DWORD cchPrefixedUrl,
  968.                                        OUT LPCTSTR *ppszFragment
  969.                                        )
  970. {
  971.     //
  972.     // Make it sure that s_cchUserPrefix is initialized.
  973.     //
  974.     s_Init();
  975.     //  Prefix + UserPrefix + '@'
  976.     ASSERT(pszPrefixedUrl && ppszFragment);
  977.     //  clear the out params
  978.     pszPrefixedUrl[0] = L'';
  979.     *ppszFragment = NULL;
  980.     //  if there is no URL, send back a default case
  981.     //  this is just for EnumObjects
  982.     if (!pszUrl || !*pszUrl)
  983.     {
  984.         wnsprintf(pszPrefixedUrl, cchPrefixedUrl, L"%s%s", c_szHistoryPrefix, s_szUserPrefix);
  985.     }
  986.     else
  987.     {
  988.         int slen;
  989.         int nScheme;
  990.         LPWSTR pszFragment;
  991.         wnsprintf(pszPrefixedUrl, cchPrefixedUrl, L"%s%s", c_szHistoryPrefix, s_szUserPrefix);
  992.         slen = lstrlen(pszPrefixedUrl);
  993.         StrCpyN(pszPrefixedUrl + slen, pszUrl, MAX_URL_STRING-slen);
  994.         // Only strip the anchor fragment if it's not JAVASCRIPT: or VBSCRIPT:, because a # could not an
  995.         // anchor but a string to be evaluated by a script engine like #00ff00 for an RGB color.
  996.         nScheme = GetUrlSchemeW(pszPrefixedUrl);      
  997.         if (nScheme == URL_SCHEME_JAVASCRIPT || nScheme == URL_SCHEME_VBSCRIPT)
  998.         {
  999.             pszFragment = NULL;
  1000.         }
  1001.         else
  1002.         {
  1003.             //  locate local anchor fragment if possible
  1004.             pszFragment = StrChr(pszPrefixedUrl, L'#');
  1005.         }
  1006.         if(pszFragment)     
  1007.         {
  1008.             //  kill the '#' so that lpszPrefixedUrl is isolated
  1009.             *pszFragment = L'';
  1010.             *ppszFragment = pszFragment+1;
  1011.         }
  1012.         //  check for trailing slash and eliminate
  1013.         LPWSTR pszT = CharPrev(pszPrefixedUrl, pszPrefixedUrl + lstrlen(pszPrefixedUrl));
  1014.         if (pszT[0] == L'/') {
  1015.             TraceMsg(DM_HISTNLS, "CUH::s_Convert removing the trailing slash of %s", pszPrefixedUrl);
  1016.             ASSERT(lstrlen(pszT)==1);
  1017.             pszT[0] = L'';
  1018.         }
  1019.     }
  1020. }
  1021. //
  1022. // Basically a wrapper function for RetreiveUrlCacheEntryInfo 
  1023. // meant to be called with the prefixed Url.                  
  1024. // it handles allocating the buffer and reallocating if necessary.
  1025. //
  1026. void CUrlHistory::s_RetrievePrefixedUrlInfo(
  1027.         LPCTSTR pszUrl, CEI_PREALLOC* pbuf)
  1028. {
  1029.     TraceMsg(DM_UHRETRIEVE, "CURLHistory::s_RetrievePrefixUrlInfo called (%s)", pszUrl);
  1030.     s_Init();
  1031.     DWORD cbCEI = SIZEOF(pbuf->ab);
  1032.     pbuf->pcei = &pbuf->cei;
  1033.     BOOL fSuccess = GetUrlCacheEntryInfoBinary(pszUrl, pbuf->pcei, &cbCEI);
  1034.     if (!fSuccess) {
  1035.         pbuf->pcei = NULL;
  1036.         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  1037.             TraceMsg(DM_TRACE, "CUH::s_RetrievePUI not enough buffer. Allocate! (%d)", cbCEI);
  1038.             pbuf->pcei = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, cbCEI);
  1039.             if (pbuf->pcei) {
  1040.                 fSuccess = GetUrlCacheEntryInfoBinary(pszUrl, pbuf->pcei, &cbCEI);
  1041.                 if (!fSuccess) {
  1042.                     TraceMsg(DM_HISTMISS, "CUH::s_Retrieve (%s) failed %x (on second attempt)",
  1043.                              pszUrl, GetLastError());
  1044.                     LocalFree(pbuf->pcei);
  1045.                     pbuf->pcei = NULL;
  1046.                     SetLastError(ERROR_FILE_NOT_FOUND);
  1047.                 } 
  1048.             }
  1049.         } else {
  1050.             TraceMsg(DM_HISTMISS, "CUH::s_Retrieve (%s) failed %x (on first attempt)",
  1051.                      pszUrl, GetLastError());
  1052.             SetLastError(ERROR_FILE_NOT_FOUND);
  1053.         }
  1054.     }
  1055. }
  1056. //
  1057. // Return the total of a double-null terminated string (including the
  1058. // terminating null).
  1059. //
  1060. UINT lstrzlen(LPCTSTR pszz)
  1061. {
  1062.     for (LPCTSTR psz=pszz; *psz; psz += lstrlen(psz) + 1) ;
  1063.     return (unsigned int)(psz+1-pszz);
  1064. }
  1065.     /*++
  1066.      Routine Description:
  1067.      this creates a buffer that holds a HISTDATA, and everything the HISTDATAs offsets
  1068.      point to.  it only sets those offsets that are passed in.
  1069.      Arguments:
  1070.      lpszTitle Title to place in the buffer
  1071.      lpBase this is the base of the offsets in aFrags
  1072.      aFrags an array of offsets to the fragments to place in the buffer
  1073.      cFrags number of fragments in aFrags
  1074.      lpszNewFrag this is an additional fragment to add in the new buffer
  1075.      pcbHeader this is a pointer to the final size of the buffer returned
  1076.      NOTE: any of the arguments except pcbHeader may be NULL.
  1077.      if lpBase is NULL, then aFrags must also be NULL. this is CALLERs responsibility!
  1078.      if a parameter is NULL, then it just isnt added to the buffer.
  1079.      Return Value:
  1080.      POINTER
  1081.      Success - a valid pointer to a buffer that must be freed.
  1082.      Failure - NULL. this only fails with ERROR_NOT_ENOUGH_MEMORY
  1083.      NOTE:  Caller must free the returned pointer.  *pcbHeader only set upon successful return.
  1084.      --*/
  1085. HISTDATA* CUrlHistory::s_GenerateHeaderInfo(
  1086.                                   IN LPCTSTR pszTitle,
  1087.                                   IN HISTDATA* phdPrev,
  1088.                                   IN LPCTSTR pszFragment,
  1089.                                   OUT LPDWORD pcbHeader
  1090.                                   )
  1091. {
  1092.     DWORD cbHeader = 0;
  1093.     UINT cbHistExtra = 0;
  1094.     HISTEXTRA* phextPrev;
  1095.     // Get the size for title
  1096.     UINT cchTitle = 0;
  1097.     if (pszTitle[0]) {
  1098.         cchTitle = lstrlen(pszTitle) + 1;
  1099.         cbHistExtra += DW_ALIGNED(SIZEOF(HISTEXTRA) + (cchTitle * sizeof(TCHAR)));
  1100.         if (phdPrev && (phextPrev = phdPrev->_FindExtraForSave(PID_INTSITE_TITLE))!=NULL) {
  1101.             phextPrev->vtExtra = VT_EMPTY;
  1102.         }
  1103.     }
  1104.     // Get the size of fragments
  1105.     UINT cchFragsPrev = 0;
  1106.     UINT cchFragment = 0;
  1107.     if (pszFragment) {
  1108.         cchFragment = lstrlen(pszFragment) + 2;  // Double NULL terminated
  1109.         if (phdPrev && (phextPrev=phdPrev->_FindExtraForSave(PID_INTSITE_FRAGMENT))!=NULL) {
  1110.             cchFragsPrev = lstrzlen((LPCTSTR)phextPrev->abExtra) - 1; // lstrzlen includes both terminating nulls
  1111.                                                                       // -1 since cchFragment already accounts
  1112.                                                                       // for double terminating NULLs.
  1113.             ASSERT(cchFragsPrev != (UINT)-1);
  1114.             phextPrev->vtExtra = VT_EMPTY;
  1115.         }
  1116.         cbHistExtra += DW_ALIGNED(SIZEOF(HISTEXTRA) + (cchFragsPrev + cchFragment) * sizeof(TCHAR));
  1117.     }
  1118.     // Get the size of other extra
  1119.     if (phdPrev) {
  1120.         cbHistExtra += phdPrev->GetTotalExtraSize();
  1121.     }
  1122.     // Allocate it
  1123.     CHistoryData* phdNew = CHistoryData::s_AllocateHeaderInfo(
  1124.                                 cbHistExtra, phdPrev,
  1125.                                 &cbHeader);
  1126.     if (phdNew) {
  1127.         HISTEXTRA* phext = phdNew->_GetExtra();
  1128.         // Append title
  1129.         if (pszTitle[0]) {
  1130.             phext->cbExtra = DW_ALIGNED((cchTitle * sizeof(TCHAR)) + SIZEOF(HISTEXTRA));
  1131.             phext->idExtra = PID_INTSITE_TITLE;
  1132.             phext->vtExtra = VT_LPTSTR; 
  1133.             StrCpyN((LPTSTR)phext->abExtra, pszTitle, cchTitle);
  1134.             phext = phext->GetNextFastForSave();
  1135.         }
  1136.         // Append fragment     
  1137.         if (pszFragment) {
  1138.             // Copy pszFragment to the top.
  1139.             StrCpyN((LPTSTR)phext->abExtra, pszFragment, cchFragment);
  1140.             // Double NULL terminate.  Note cchFragment = strlen + 2
  1141.             *(((LPTSTR)phext->abExtra) + cchFragment - 1) = TEXT('');
  1142.             // Copy existing fragments if any
  1143.             if (cchFragsPrev) {
  1144.                 ASSERT(phdPrev);
  1145.                 phextPrev = phdPrev->_FindExtraForSave(PID_INTSITE_FRAGMENT);
  1146.                 ASSERT(phextPrev);
  1147.                 if (phextPrev) {
  1148.                     ASSERT(IS_DW_ALIGNED(phextPrev->cbExtra));
  1149.                     memcpy(phext->abExtra + ((cchFragment - 1) * sizeof(TCHAR)), phextPrev->abExtra,
  1150.                            (cchFragsPrev + 1) * sizeof(TCHAR));
  1151.                 }
  1152.             }
  1153.             ASSERT(lstrzlen((LPCTSTR)phext->abExtra) == cchFragsPrev + cchFragment);
  1154.             phext->cbExtra += DW_ALIGNED(SIZEOF(HISTEXTRA) + (cchFragsPrev + cchFragment) * sizeof(TCHAR));
  1155.             phext->idExtra = PID_INTSITE_FRAGMENT;
  1156.             phext->vtExtra = VT_NULL;    // HACK (means internal)
  1157.             phext = phext->GetNextFastForSave();
  1158.         }
  1159.         // Migrate extra data from previous one
  1160.         if (phdPrev) {
  1161.             phext = phdPrev->CopyExtra(phext);
  1162.         }
  1163.         ASSERT( phext->cbExtra == 0); // terminator
  1164.         ASSERT( (LPBYTE)phdNew+cbHeader == (LPBYTE)phext+SIZEOF(DWORD) );
  1165.         ASSERT( cbHistExtra == phdNew->GetTotalExtraSize() );
  1166.     }
  1167.     *pcbHeader = cbHeader;
  1168.     TraceMsg(DM_HISTGENERATE, "CUH::s_GenerateHeader allocated %d bytes (%d extra)",
  1169.              cbHeader, cbHistExtra);
  1170.     return phdNew;
  1171. }
  1172. // BUGBUG: Move this to UTIL.CPP
  1173. LPWSTR AllocOleStrFromTChar(LPCTSTR psz)
  1174. {
  1175.     DWORD cch = lstrlen(psz) + 1;
  1176.     LPWSTR pwsz = (LPWSTR)CoTaskMemAlloc(cch * SIZEOF(WCHAR));
  1177.     if (pwsz) {
  1178.         SHTCharToUnicode(psz, pwsz, cch);
  1179.     }
  1180.     return pwsz;
  1181. }
  1182. HRESULT CUrlHistory::s_GenerateSTATURL(
  1183.                                IN LPINTERNET_CACHE_ENTRY_INFO lpCEI,
  1184.                                IN DWORD dwFlags,
  1185.                                OUT LPSTATURL lpSTATURL)
  1186. {
  1187.     ASSERT(lpCEI);
  1188.     ASSERT(lpSTATURL);
  1189.     if (!lpCEI || !lpSTATURL)
  1190.         return E_INVALIDARG;
  1191.     HRESULT hr = S_OK;
  1192.     LPHISTDATA phd =  CHistoryData::s_GetHistoryData(lpCEI);
  1193.     LPCTSTR pszUrl = lpCEI->lpszSourceUrlName + s_cchUserPrefix + CCHHISTORYPREFIX;
  1194.     ZeroMemory(lpSTATURL, SIZEOF(STATURL));
  1195.     lpSTATURL->ftLastUpdated = lpCEI->LastModifiedTime;
  1196.     lpSTATURL->ftExpires = lpCEI->ExpireTime;
  1197.     lpSTATURL->ftLastVisited = lpCEI->LastSyncTime;
  1198.     if(dwFlags & STATURL_QUERYFLAG_ISCACHED)
  1199.     {
  1200.         if (s_IsCached(pszUrl))
  1201.             lpSTATURL->dwFlags |= STATURLFLAG_ISCACHED;
  1202.     }
  1203.     if (dwFlags & STATURL_QUERYFLAG_TOPLEVEL)
  1204.     {
  1205.         if (phd) {
  1206.             if (phd->dwFlags & PIDISF_HISTORY)
  1207.             {
  1208.                 lpSTATURL->dwFlags |= STATURLFLAG_ISTOPLEVEL;
  1209.             }
  1210.         }
  1211.     }
  1212.     if (!(dwFlags & STATFLAG_NONAME))
  1213.     {
  1214.         if (!(dwFlags & STATURL_QUERYFLAG_NOURL))
  1215.         {
  1216.             //  set the Url
  1217.             lpSTATURL->pwcsUrl = AllocOleStrFromTChar(pszUrl);
  1218.             if (lpSTATURL->pwcsUrl == NULL) {
  1219.                 hr = E_OUTOFMEMORY;
  1220.             }
  1221.         }
  1222.         if (!(dwFlags & STATURL_QUERYFLAG_NOTITLE))
  1223.         {
  1224.             //  is there a title to set?
  1225.             if (phd)
  1226.             {
  1227.                 const HISTEXTRA* phextTitle = phd->_FindExtra(PID_INTSITE_TITLE);
  1228.                 if (phextTitle && phextTitle->vtExtra == VT_LPTSTR) {
  1229.                     lpSTATURL->pwcsTitle = AllocOleStrFromTChar((LPCTSTR)phextTitle->abExtra);
  1230.                     if (lpSTATURL->pwcsTitle == NULL) {
  1231.                         if (lpSTATURL->pwcsUrl)
  1232.                             CoTaskMemFree(lpSTATURL->pwcsUrl);
  1233.                         lpSTATURL->pwcsUrl = NULL;
  1234.                         hr = E_OUTOFMEMORY;
  1235.                     }
  1236.                 }
  1237.             }
  1238.         }
  1239.     }
  1240.     ASSERT(SUCCEEDED(hr) || (lpSTATURL->pwcsUrl==NULL && lpSTATURL->pwcsTitle==NULL));
  1241.     return hr;
  1242. }
  1243.     /*++
  1244.      Routine Description:
  1245.      Places the specified URL into the history.
  1246.      If it does not exist, then it is created.  If it does exist it is overwritten.
  1247.      Arguments:
  1248.      pwszUrl - The URL in question.
  1249.      pwszTitle - pointer to the friendly title that should be associated
  1250.      with this URL. If NULL, no title will be added.
  1251.      dwFlags             - Sets options for storage type and durability
  1252.      Not implemented yet
  1253.      Return Value:
  1254.      HRESULT
  1255.      Success - S_OK
  1256.      Failure - E_ hresult
  1257.      --*/
  1258. HRESULT CUrlHistory::AddUrl(
  1259.                          IN LPCWSTR pwszUrl, // Full URL to be added
  1260.                          IN LPCWSTR pwszTitle,
  1261.                          IN DWORD dwFlags                // Storage options
  1262.                          )
  1263. {
  1264.     return AddUrlAndNotify(pwszUrl, pwszTitle, dwFlags, TRUE, NULL, NULL);
  1265. }
  1266. BOOL CUrlHistory::s_CommitUrlCacheEntry(LPCTSTR pszPrefixedUrl, 
  1267.                         LPINTERNET_CACHE_ENTRY_INFO pcei)
  1268. {
  1269.     if (s_dwDaysToKeep==0) {
  1270.         s_dwDaysToKeep = s_GetDaysToKeep();
  1271.     }
  1272.     //
  1273.     // prepare the expire time
  1274.     //
  1275.     SYSTEMTIME st;
  1276.     GetSystemTime(&st);
  1277.     SystemTimeToFileTime(&st, &pcei->LastModifiedTime);
  1278.     //
  1279.     // Assume the normal expiration date ... we add 6 days to expiration date
  1280.     // to make sure when we show the oldest week, we'll still have data for days
  1281.     // that are past the s_dwDaysToKeep
  1282.     //
  1283.     LONGLONG llExpireHorizon = SECS_PER_DAY * (s_dwDaysToKeep + 6);
  1284.     llExpireHorizon *= FILETIME_SEC;
  1285.     pcei->ExpireTime.dwLowDateTime = pcei->LastModifiedTime.dwLowDateTime + (DWORD) (llExpireHorizon % 0xFFFFFFFF);
  1286.     pcei->ExpireTime.dwHighDateTime = pcei->LastModifiedTime.dwHighDateTime + (DWORD) (llExpireHorizon / 0xFFFFFFFF);
  1287.     //
  1288.     // Check if it's subscribed
  1289.     //
  1290.     CHistoryData* phd =  CHistoryData::s_GetHistoryData(pcei);
  1291.     if (phd && phd->_FindExtra(PID_INTSITE_SUBSCRIPTION)) {
  1292.         //
  1293.         // It's subscribed. Keep it forever (until unsubscribed). 
  1294.         //
  1295.         TraceMsg(DM_URLCLEANUP, "CUH::s_CommitUrlCacheEntry found subscription key %s", pszPrefixedUrl);
  1296.         pcei->ExpireTime.dwLowDateTime = DW_FOREVERLOW;
  1297.         pcei->ExpireTime.dwHighDateTime = DW_FOREVERHIGH;
  1298.     }
  1299. #ifdef DEBUG
  1300.     LPCTSTR pszTitle = TEXT("(no extra data)");
  1301.     if (phd) {
  1302.         const HISTEXTRA* phext = phd->_FindExtra(PID_INTSITE_TITLE);
  1303.         if (phext && phext->vtExtra==VT_LPTSTR) {
  1304.             pszTitle = (LPCTSTR)phext->abExtra;
  1305.         } else {
  1306.             pszTitle = TEXT("(no title property)");
  1307.         }
  1308.         TraceMsg(DM_HISTCOMMIT, "CURL::s_C calling Commit for %s with %s",
  1309.             pszPrefixedUrl, pszTitle);
  1310.     }
  1311. #endif
  1312.     return CommitUrlCacheEntryBinary(pszPrefixedUrl,
  1313.                                      pcei->ExpireTime,
  1314.                                      pcei->LastModifiedTime,
  1315.                                      pcei->CacheEntryType | URLHISTORY_CACHE_ENTRY,
  1316.                                      (LPBYTE)pcei->lpHeaderInfo,
  1317.                                      pcei->dwHeaderInfoSize);
  1318. }
  1319. void CUrlHistory::_WriteToHistory(LPCTSTR pszPrefixedUrl, FILETIME& ftExpires, IOleCommandTarget *poctNotify, IUnknown *punkSFHistory)
  1320. {
  1321.     IHistSFPrivate *psfpHistory;
  1322.     HRESULT hr = LoadHistoryShellFolder(punkSFHistory, &psfpHistory);
  1323.     if (SUCCEEDED(hr))
  1324.     {
  1325.         LPITEMIDLIST pidlNotify = NULL;
  1326.         //
  1327.         // prepare the local mod time
  1328.         //
  1329.         SYSTEMTIME st;
  1330.         GetLocalTime (&st);
  1331.     
  1332.         FILETIME ftLocModified; // new history written in "User Perceived Time"
  1333.         SystemTimeToFileTime(&st, &ftLocModified);
  1334.         hr = psfpHistory->WriteHistory(pszPrefixedUrl,
  1335.                                 ftExpires,
  1336.                                 ftLocModified,
  1337.                                 poctNotify ? &pidlNotify : NULL);
  1338.         if (pidlNotify)
  1339.         {
  1340.             VARIANTARG var;
  1341.             InitVariantFromIDList(&var, pidlNotify);
  1342.             poctNotify->Exec(&CGID_Explorer, SBCMDID_SELECTHISTPIDL, OLECMDEXECOPT_PROMPTUSER, &var, NULL);
  1343.             ILFree(pidlNotify);
  1344.             VariantClear(&var);
  1345.         }
  1346.         psfpHistory->Release();
  1347.     }
  1348.     // if we made it to here, we win!
  1349. }
  1350. void CUrlHistory::s_UpdateIcon(Intshcut* pintshcut, DWORD dwFlags)
  1351. {
  1352.     TCHAR szPath[MAX_PATH];
  1353.     int niIcon;
  1354.     UINT uFlags;
  1355.     // mask off the other stuff so we get consistent results.
  1356.     dwFlags &= PIDISF_RECENTLYCHANGED;
  1357.     
  1358.     // Get the old icon location 
  1359.     pintshcut->GetIconLocationFromFlags(0, szPath, SIZECHARS(szPath),
  1360.         &niIcon, &uFlags, dwFlags);
  1361.     
  1362.     // property.
  1363. //    int icachedImage = SHLookupIconIndex(PathFindFileName(szPath), niIcon, uFlags);
  1364.     int icachedImage = Shell_GetCachedImageIndex(PathFindFileName(szPath), niIcon, uFlags);
  1365.     TraceMsg(DM_HISTSPLAT, "CUH::s_UpdateIcon splat flag is changed for %s (%d)",
  1366.             szPath, icachedImage);
  1367.     SHUpdateImage( PathFindFileName(szPath), niIcon, uFlags, icachedImage );
  1368. }
  1369. HRESULT CUrlHistory::AddUrlAndNotify(
  1370.                          IN LPCWSTR pwszUrl, // Full URL to be added
  1371.                          IN LPCWSTR pwszTitle,
  1372.                          IN DWORD dwFlags,                // Storage options
  1373.                          IN BOOL fWriteHistory,         // Write History ShellFolder
  1374.                          IN IOleCommandTarget *poctNotify,
  1375.                          IN IUnknown *punkSFHistory)
  1376. {
  1377.     return AddUrlAndNotifyCP(pwszUrl, pwszTitle, dwFlags, fWriteHistory,
  1378.                            poctNotify, punkSFHistory, NULL);
  1379. }
  1380. HRESULT CUrlHistory::AddUrlAndNotifyCP(
  1381.                          IN LPCWSTR pwszUrl, // Full URL to be added
  1382.                          IN LPCWSTR pwszTitle,
  1383.                          IN DWORD dwFlags,                // Storage options
  1384.                          IN BOOL fWriteHistory,         // Write History ShellFolder
  1385.                          IN IOleCommandTarget *poctNotify,
  1386.                          IN IUnknown *punkSFHistory,
  1387.                          UINT* pcodepage)
  1388. {
  1389.     if (pcodepage) {
  1390.         *pcodepage = CP_ACP;    // this is default.
  1391.     }
  1392.     HRESULT hr = S_OK;
  1393.     LPCWSTR pwszTitleToStore = pwszTitle;
  1394.     //  check to make sure we got an URL
  1395.     if (!pwszUrl || !pwszUrl[0])
  1396.     {
  1397.         return E_INVALIDARG;
  1398.     }
  1399.     if (pwszTitleToStore && 0 == StrCmpIW(pwszTitleToStore, pwszUrl))
  1400.     {
  1401.         //  Suppress redundant title data
  1402.         pwszTitleToStore = NULL;
  1403.     }
  1404.     CEI_PREALLOC buf;
  1405.     INTERNET_CACHE_ENTRY_INFO cei = { 0 };
  1406.     // Wininet URL cache only supports 8-bit ANSI, so we need to encode any characters
  1407.     // which can't be converted by the system code page, in order to allow Unicode
  1408.     // filenames in History.  The URLs will remain in the encoded form through most of
  1409.     // the History code paths, with only the display and navigation code needing to be
  1410.     // aware of the UTF8
  1411.     LPCTSTR pszUrlSource = pwszUrl; // points to the URL that we decide to use
  1412.     TCHAR szEncodedUrl[MAX_URL_STRING];
  1413.     BOOL bUsedDefaultChar;
  1414.     
  1415.     // Find out if any of the chars will get scrambled.  We can use our szEncodedUrl 
  1416.     // buffer to store he multibyte result because we don't actually want it
  1417.     
  1418.     WideCharToMultiByte(CP_ACP, 0, pwszUrl, -1, 
  1419.         (LPSTR) szEncodedUrl, sizeof(szEncodedUrl), NULL, &bUsedDefaultChar);
  1420.     
  1421.     if (bUsedDefaultChar)
  1422.     {
  1423.         // one or more chars couldn't be converted, so we store the UTF8 escaped string
  1424.         StrCpyN(szEncodedUrl, pwszUrl, ARRAYSIZE(szEncodedUrl));
  1425.         ConvertToUtf8Escaped(szEncodedUrl, ARRAYSIZE(szEncodedUrl));
  1426.         pszUrlSource = szEncodedUrl;
  1427.     }
  1428.     s_ConvertToPrefixedUrlW(pszUrlSource, buf.szPrefixedUrl, ARRAYSIZE(buf.szPrefixedUrl), &buf.pszFragment);
  1429.     s_RetrievePrefixedUrlInfo(buf.szPrefixedUrl, &buf);
  1430.     LPHISTDATA phdPrev = NULL;
  1431.     TCHAR szTitle[MAX_PATH];
  1432.     szTitle[0] = '';
  1433.     //
  1434.     // if there is already an entry for this Url, then we will reuse some of the
  1435.     // settings.  retrieve the relevant info if possible.
  1436.     //
  1437.     if (buf.pcei)
  1438.     {
  1439.         cei = *buf.pcei;   // copy the existing one
  1440.         phdPrev = CHistoryData::s_GetHistoryData(buf.pcei);
  1441.         if (pwszTitle==NULL && phdPrev) {
  1442.             phdPrev->_GetTitle(szTitle, ARRAYSIZE(szTitle));
  1443.         }
  1444.         if (pcodepage && phdPrev) {
  1445.             //
  1446.             // NOTES: This is the best place get the codepage stored
  1447.             //  in this URL history.
  1448.             //
  1449.             const HISTEXTRA* phextCP =phdPrev->_FindExtra(PID_INTSITE_CODEPAGE);
  1450.             if (phextCP && phextCP->vtExtra == VT_UI4) {
  1451.                 *pcodepage = *(DWORD*)phextCP->abExtra;
  1452.                 TraceMsg(DM_TRACE, "CUH::AddAndNotify this URL has CP=%d",
  1453.                          *pcodepage);
  1454.             }
  1455.         }
  1456.     } else {
  1457.         cei.CacheEntryType = NORMAL_CACHE_ENTRY;
  1458.         ASSERT(cei.dwHeaderInfoSize == 0);
  1459.     }
  1460.     //
  1461.     // search for a fragment if necessary
  1462.     //
  1463.     if (buf.pszFragment && phdPrev)
  1464.     {
  1465.         if (phdPrev->_HasFragment(buf.pszFragment)) {
  1466.             buf.pszFragment = NULL;
  1467.         }
  1468.     }
  1469.     // Override the title if specified.
  1470.     if (pwszTitleToStore) {
  1471.         // GetDisplayableTitle puts szTitle[0] = '' if it's
  1472.         // not displayable with shell codepage
  1473.         StrCpyN(szTitle, pwszTitleToStore, ARRAYSIZE(szTitle));
  1474.     } 
  1475.     CHistoryData* phdNew = s_GenerateHeaderInfo(
  1476.                szTitle, phdPrev, buf.pszFragment, &cei.dwHeaderInfoSize);
  1477.     if (phdNew) {
  1478.         cei.lpHeaderInfo = (LPTSTR)phdNew;
  1479.         if (fWriteHistory && !UrlIsNoHistoryW(pwszUrl)) {
  1480.             phdNew->dwFlags |= PIDISF_HISTORY;
  1481.         }
  1482.         BOOL fUpdateIcon = FALSE;
  1483.     
  1484.         if (phdNew->dwFlags & PIDISF_RECENTLYCHANGED) {
  1485.             fUpdateIcon = TRUE;
  1486.             phdNew->dwFlags &= ~PIDISF_RECENTLYCHANGED;
  1487.         }
  1488.     
  1489.         if (s_CommitUrlCacheEntry(buf.szPrefixedUrl, &cei))
  1490.         {
  1491.             if (fUpdateIcon) {
  1492.                 TraceMsg(DM_HISTSPLAT, "CUH::AddAndNotify remove splat!");
  1493.                 // BUGBUG: This is a temporary hack to make splat update
  1494.                 //  work as bad as previously.
  1495.                 Intshcut* pintshcut = new Intshcut();
  1496.                 if (pintshcut) {
  1497.                     pintshcut->SetURL(pwszUrl ,0);
  1498.                     s_UpdateIcon(pintshcut, PIDISF_RECENTLYCHANGED);
  1499.                     pintshcut->Release();
  1500.                 }
  1501.             }
  1502.     
  1503.             //
  1504.             //  When we have successfully updated the global history and
  1505.             // we have updated something in the HISTDATA, update the
  1506.             // date-based history as well.
  1507.             //
  1508.             //
  1509.             // Cache IShellFolder for the history folder if we don't have
  1510.             // it yet.
  1511.             //
  1512.             if (fWriteHistory && !UrlIsNoHistoryW(pwszUrl))
  1513.             {
  1514.                 _WriteToHistory(buf.szPrefixedUrl, cei.ExpireTime, poctNotify, punkSFHistory);
  1515.             }
  1516.         }
  1517.         else
  1518.         {
  1519.             hr = HRESULT_FROM_WIN32(GetLastError ());
  1520.         }
  1521.         LocalFree(phdNew);
  1522.     }
  1523. #ifdef DEBUG
  1524.     if (g_dwPrototype & 0x00000020) {
  1525.         TCHAR szUrl[MAX_URL_STRING];
  1526.         SHUnicodeToTChar(pwszUrl, szUrl, ARRAYSIZE(szUrl));
  1527.         PROPVARIANT var = { 0 };
  1528.         HRESULT hrT = GetProperty(szUrl, PID_INTSITE_SUBSCRIPTION, &var);
  1529.         if (SUCCEEDED(hrT)) {
  1530.             TraceMsg(DM_TRACE, "CUH::AddAndNotify got property vt=%d lVal=%x",
  1531.                         var.vt, var.lVal);
  1532.             PropVariantClear(&var);
  1533.         } else {
  1534.             TraceMsg(DM_TRACE, "CUH::AddAndNotify failed to get property (%x)", hrT);
  1535.         }
  1536.     }
  1537. #endif
  1538.     return hr;
  1539. }
  1540. HRESULT CUrlHistory::QueryUrl(
  1541.                           IN LPCWSTR pwszUrl,
  1542.                           IN DWORD dwFlags,
  1543.                           OUT LPSTATURL lpSTATURL
  1544.                           )
  1545. {
  1546.     if (!pwszUrl || !pwszUrl[0])
  1547.     {
  1548.         return E_INVALIDARG;
  1549.     }
  1550.     if (lpSTATURL)
  1551.     {
  1552.         lpSTATURL->pwcsUrl = NULL;
  1553.         lpSTATURL->pwcsTitle = NULL;
  1554.     }
  1555.     LPCTSTR pszFragment;
  1556.     TCHAR szPrefixedUrl[MAX_URL_STRING];
  1557.     s_ConvertToPrefixedUrlW(pwszUrl, szPrefixedUrl, ARRAYSIZE(szPrefixedUrl), &pszFragment);
  1558.     return s_QueryUrlCommon(szPrefixedUrl, pszFragment, dwFlags, lpSTATURL);
  1559. }
  1560. HRESULT CUrlHistory::s_QueryUrlCommon(
  1561.                           IN LPCTSTR lpszPrefixedUrl,
  1562.                           LPCTSTR lpszFragment,
  1563.                           IN DWORD dwFlags,
  1564.                           OUT LPSTATURL lpSTATURL
  1565.                           )
  1566.     /*++
  1567.      Routine Description:
  1568.      Checks to see if Url is a valid History item
  1569.      Arguments:
  1570.      pwszUrl - The URL in question.
  1571.      dwFlags             - Flags on the query
  1572.      lpSTATURL           - points to a STATURL storage structure
  1573.      If this is NULL, then a S_OK means the URL was found.
  1574.      Return Value:
  1575.      HRESULT
  1576.      Success - S_OK, Item found and STATURL filled
  1577.      Failure - valid E_ code
  1578.      HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) indicates the URL is not available
  1579.      --*/
  1580. {
  1581.     HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1582.     LPHISTDATA phd = NULL;
  1583.     CEI_PREALLOC buf;
  1584.     //
  1585.     //  if there is no data required, and there are no fragments
  1586.     //  we dont need to get a copy of the CEI
  1587.     //
  1588.     if(!lpSTATURL && !lpszFragment)
  1589.     {
  1590.         if(s_IsCached(lpszPrefixedUrl))
  1591.             hr = S_OK;
  1592.         else
  1593.             hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1594.         goto quit;
  1595.     }
  1596.     s_RetrievePrefixedUrlInfo(lpszPrefixedUrl, &buf);
  1597.     if (buf.pcei)
  1598.     {
  1599.         DEBUG_CODE(DWORD cbNHI = buf.pcei->dwHeaderInfoSize;)
  1600.         phd = CHistoryData::s_GetHistoryData(buf.pcei);
  1601.         hr = S_OK;
  1602.     }
  1603.     else
  1604.     {
  1605.         hr = HRESULT_FROM_WIN32(GetLastError ());
  1606.         goto quit;
  1607.     }
  1608.     //
  1609.     // Need to check for local anchor fragments
  1610.     //
  1611.     if (lpszFragment)
  1612.     {
  1613.         if (phd && phd->_HasFragment(lpszFragment))
  1614.         {
  1615.             hr = S_OK;
  1616.         }
  1617.     } else {
  1618.         hr = S_OK;
  1619.     }
  1620.     //  check to see if we should fill the STATURL
  1621.     if (S_OK == hr && lpSTATURL) {
  1622.         hr = s_GenerateSTATURL(buf.pcei, dwFlags, lpSTATURL);
  1623.     }
  1624. quit:
  1625.     if (S_OK != hr && lpSTATURL)
  1626.     {
  1627.         if (lpSTATURL->pwcsUrl)
  1628.             LocalFree(lpSTATURL->pwcsUrl);
  1629.         if (lpSTATURL->pwcsTitle)
  1630.             LocalFree(lpSTATURL->pwcsTitle);
  1631.     }
  1632.     return hr;
  1633. }
  1634. HRESULT CUrlHistory::QueryUrlA(LPCSTR pszUrl, DWORD dwFlags, LPSTATURL lpSTATURL)
  1635. {
  1636.     TCHAR szPrefixedUrl[MAX_URL_STRING];
  1637.     LPCTSTR lpszFragment = NULL;
  1638.     HRESULT hr = S_OK;
  1639.     if (!pszUrl || !pszUrl[0]) {
  1640.         return E_INVALIDARG;
  1641.     }
  1642.     if (lpSTATURL)
  1643.     {
  1644.         lpSTATURL->pwcsUrl = NULL;
  1645.         lpSTATURL->pwcsTitle = NULL;
  1646.     }
  1647.     TCHAR szUrl[MAX_URL_STRING];
  1648.     SHAnsiToUnicode(pszUrl, szUrl, ARRAYSIZE(szUrl));
  1649.     CUrlHistory::s_ConvertToPrefixedUrlW(szUrl, szPrefixedUrl, ARRAYSIZE(szPrefixedUrl), &lpszFragment);
  1650.     return CUrlHistory::s_QueryUrlCommon(szPrefixedUrl, lpszFragment, dwFlags, lpSTATURL);
  1651. }
  1652. HRESULT CUrlHistory::s_DeleteUrl(LPCWSTR pwszUrl, DWORD dwFlags)
  1653. {
  1654.     DWORD Error = ERROR_SUCCESS;
  1655.     TCHAR szPrefixedUrl[MAX_URL_STRING];
  1656.     LPCTSTR lpszFragment;
  1657.     BOOL  fDoDelete = TRUE;
  1658.     
  1659.     if (!pwszUrl || !pwszUrl[0]) {
  1660.         return E_INVALIDARG;
  1661.     }
  1662.     s_ConvertToPrefixedUrlW(pwszUrl, szPrefixedUrl, ARRAYSIZE(szPrefixedUrl), &lpszFragment);
  1663.     // don't delete it if its not a subscription
  1664.     if (dwFlags & URLFLAG_DONT_DELETE_SUBSCRIBED) {
  1665.         CEI_PREALLOC buf;
  1666.         // query to find out if its a subscription
  1667.         s_RetrievePrefixedUrlInfo(szPrefixedUrl, &buf);
  1668.         if (buf.pcei &&
  1669.             //  Hack alert (chrisfra) avoid deleting subscriptions, etc!
  1670.             ((buf.pcei)->ExpireTime.dwLowDateTime  == DW_FOREVERLOW) &&
  1671.             ((buf.pcei)->ExpireTime.dwHighDateTime == DW_FOREVERHIGH))
  1672.         {
  1673.             fDoDelete = FALSE;
  1674.             // re-write it as a non-history item and just a subscription
  1675.             CHistoryData *phdPrev = CHistoryData::s_GetHistoryData(buf.pcei);
  1676.             if (phdPrev) // offset into pcei structure
  1677.             {
  1678.                 phdPrev->dwFlags &= ~PIDISF_HISTORY;
  1679.                 s_CommitUrlCacheEntry(szPrefixedUrl, buf.pcei);
  1680.             }
  1681.             else {
  1682.                 // I'd rather return ERROR_OUT_OF_PAPER...
  1683.                 Error = ERROR_FILE_NOT_FOUND;
  1684.             }
  1685.         }
  1686.     }
  1687.     if (fDoDelete) {
  1688.         if(!::DeleteUrlCacheEntry(szPrefixedUrl))
  1689.             Error = GetLastError();
  1690.     }
  1691.     return HRESULT_FROM_WIN32(Error);
  1692. }
  1693. HRESULT CUrlHistory::DeleteUrl(LPCWSTR pwszUrl, DWORD dwFlags)
  1694. {
  1695.     return s_DeleteUrl(pwszUrl, dwFlags);
  1696. }
  1697. HRESULT CUrlHistory::BindToObject (LPCWSTR pwszUrl, REFIID riid, void **ppvOut)
  1698. {
  1699.     *ppvOut = NULL;
  1700.     return E_NOTIMPL;
  1701. #if 0
  1702.     HRESULT hr;
  1703.     if(!ppvOut || !pwszUrl || !*pwszUrl)
  1704.         return E_INVALIDARG;
  1705.     *ppvOut = NULL;
  1706.     CUrlHObj *pcuho = new CUrlHObj (this);
  1707.     if (!pcuho)
  1708.         return E_OUTOFMEMORY;
  1709.     hr = pcuho->QueryInterface(riid, ppvOut);
  1710.     pcuho->Release();
  1711.     if (SUCCEEDED(hr))
  1712.     {
  1713.         hr = pcuho->Init(pwszUrl);
  1714.         if (FAILED(hr))
  1715.         {
  1716.             pcuho->Release();
  1717.             *ppvOut = NULL;
  1718.         }
  1719.     }
  1720.     
  1721.     return hr;
  1722. #endif
  1723. }
  1724. HRESULT CUrlHistory::s_EnumUrls(IEnumSTATURL **ppEnum)
  1725. {
  1726.     HRESULT hres = E_OUTOFMEMORY;
  1727.     *ppEnum = NULL;
  1728.     CEnumSTATURL *penum = new CEnumSTATURL();
  1729.     if (penum)
  1730.     {
  1731.         *ppEnum = (IEnumSTATURL *)penum;
  1732.         hres = S_OK;
  1733.     }
  1734.     return hres;
  1735. }
  1736. HRESULT CUrlHistory::EnumUrls(IEnumSTATURL **ppEnum)
  1737. {
  1738.     return s_EnumUrls(ppEnum);
  1739. }
  1740. HRESULT CUrlHistory::GetProperty(LPCTSTR pszURL, PROPID pid, PROPVARIANT* pvarOut)
  1741. {
  1742.     HRESULT hres = E_FAIL;  // assume error
  1743.     pvarOut->vt = VT_EMPTY;
  1744.     CEI_PREALLOC buf;
  1745.     CUrlHistory::s_ConvertToPrefixedUrlW(pszURL, buf.szPrefixedUrl, ARRAYSIZE(buf.szPrefixedUrl), &buf.pszFragment);
  1746.     CUrlHistory::s_RetrievePrefixedUrlInfo(buf.szPrefixedUrl, &buf);
  1747.     if (buf.pcei) {
  1748.         CHistoryData* phdPrev =  CHistoryData::s_GetHistoryData(buf.pcei);
  1749.         if (phdPrev) {
  1750.             const HISTEXTRA* phextPrev;
  1751.             switch(pid) {
  1752.             case PID_INTSITE_FLAGS:
  1753.                 pvarOut->vt = VT_UI4;
  1754.                 pvarOut->lVal = phdPrev->dwFlags;
  1755.                 hres = S_OK;
  1756.                 break;
  1757.         
  1758.             case PID_INTSITE_LASTVISIT:
  1759.                 pvarOut->vt = VT_FILETIME;
  1760.                 pvarOut->filetime = buf.pcei->LastAccessTime;
  1761.                 hres = S_OK;
  1762.                 break;
  1763.             case PID_INTSITE_LASTMOD:
  1764.                 pvarOut->vt = VT_FILETIME;
  1765.                 pvarOut->filetime = buf.pcei->LastModifiedTime;
  1766.                 hres = S_OK;
  1767.                 break;
  1768.             case PID_INTSITE_WATCH:
  1769.                 pvarOut->vt = VT_UI4;
  1770.                 pvarOut->lVal = phdPrev->dwWatch;
  1771.                 hres = S_OK;
  1772.                 break;
  1773.             case PID_INTSITE_VISITCOUNT:
  1774.                 pvarOut->vt   = VT_UI4;
  1775.                 pvarOut->lVal = buf.pcei->dwHitRate;
  1776.                 hres = S_OK;
  1777.                 break;
  1778.             default:
  1779.                 phextPrev = phdPrev->_FindExtra(pid);
  1780.                 LPCWSTR pwsz;
  1781.                 if (phextPrev) {
  1782.                     WCHAR wszBuf[MAX_URL_STRING];
  1783.                     switch(phextPrev->vtExtra) {
  1784.                     case VT_UI4:
  1785.                     case VT_I4:
  1786.                         pvarOut->vt = phextPrev->vtExtra;
  1787.                         pvarOut->lVal = *(DWORD*)phextPrev->abExtra;
  1788.                         hres = S_OK;
  1789.                         break;
  1790.                     case VT_LPSTR:
  1791.                         AnsiToUnicode((LPCSTR)phextPrev->abExtra, wszBuf, ARRAYSIZE(wszBuf));
  1792.                         pwsz = wszBuf;
  1793.                         goto Return_LPWSTR;
  1794.                     case VT_LPWSTR:
  1795.                         pwsz = (LPWSTR)phextPrev->abExtra;
  1796. Return_LPWSTR:
  1797.                         int cch = lstrlenW(pwsz)+1;
  1798.                         pvarOut->pwszVal = (LPWSTR)CoTaskMemAlloc(cch * SIZEOF(WCHAR));
  1799.                         if (pvarOut->pwszVal) {
  1800.                             StrCpyNW(pvarOut->pwszVal, pwsz, cch);
  1801.                             pvarOut->vt = VT_LPWSTR;
  1802.                             hres = S_OK;
  1803.                         } else {
  1804.                             hres = E_OUTOFMEMORY;
  1805.                         }
  1806.                         break;
  1807.                     }
  1808.                 }
  1809.                 break;
  1810.             }
  1811.         }
  1812.     }
  1813.     return hres;
  1814. }
  1815. //
  1816. //  IEnumSTATURL methods
  1817. //
  1818. CEnumSTATURL::~CEnumSTATURL()
  1819. {
  1820.     if(m_lpCEI)
  1821.         LocalFree(m_lpCEI);
  1822.     if(m_hEnum)
  1823.         FindCloseUrlCache(m_hEnum);
  1824.     return;
  1825. }
  1826. HRESULT CEnumSTATURL::QueryInterface(REFIID riid, PVOID *ppvObj)
  1827. {
  1828.     if (!ppvObj)
  1829.         return E_INVALIDARG;
  1830.     *ppvObj = NULL;
  1831.     if (IsEqualIID(IID_IUnknown, riid))
  1832.     {
  1833.         *ppvObj = (IUnknown *) this;
  1834.         AddRef();
  1835.         return S_OK;
  1836.     }
  1837.     else if (IsEqualIID(IID_IEnumSTATURL, riid))
  1838.     {
  1839.         *ppvObj = (IEnumSTATURL *) this;
  1840.         AddRef();
  1841.         return S_OK;
  1842.     }
  1843.     return E_NOINTERFACE;
  1844. }
  1845. ULONG CEnumSTATURL::AddRef(void)
  1846. {
  1847.     _cRef++;
  1848.     return _cRef;
  1849. }
  1850. ULONG CEnumSTATURL::Release(void)
  1851. {
  1852.     _cRef--;
  1853.     if (!_cRef)
  1854.     {
  1855.         delete this;
  1856.         return 0;
  1857.     }
  1858.     return _cRef;
  1859. }
  1860. HRESULT CEnumSTATURL::RetrieveFirstUrlInfo()
  1861. {
  1862.     HRESULT hr = S_OK;
  1863.     ASSERT(!m_lpCEI);
  1864.     m_cbCEI = DEFAULT_CEI_BUFFER_SIZE;
  1865.     m_lpCEI = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, DEFAULT_CEI_BUFFER_SIZE);
  1866.     if (!m_lpCEI)
  1867.     {
  1868.         hr = E_OUTOFMEMORY;
  1869.         goto quit;
  1870.     }
  1871.     while (TRUE)
  1872.     {
  1873.         m_hEnum = FindFirstUrlCacheEntryBinary(_szPrefixedUrl,
  1874.                                                    m_lpCEI,
  1875.                                                    &m_cbCEI);
  1876.         if (!m_hEnum)
  1877.         {
  1878.             DWORD Error = GetLastError ();
  1879.             LocalFree(m_lpCEI);
  1880.             m_lpCEI = NULL;
  1881.             if (Error == ERROR_INSUFFICIENT_BUFFER)
  1882.             {
  1883.                 m_lpCEI = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, m_cbCEI);
  1884.                 if (!m_lpCEI)
  1885.                 {
  1886.                     hr = E_OUTOFMEMORY;
  1887.                     break;
  1888.                 }
  1889.             }
  1890.             else
  1891.             {
  1892.                 if (ERROR_NO_MORE_ITEMS == Error)
  1893.                     hr = S_FALSE;
  1894.                 else
  1895.                     hr = HRESULT_FROM_WIN32(Error);
  1896.                 break;
  1897.             }
  1898.         }
  1899.         else break;
  1900.     }
  1901. quit:
  1902.     m_cbCEI = max(m_cbCEI, DEFAULT_CEI_BUFFER_SIZE);
  1903.     return hr;
  1904. }
  1905. // This function should not becaused if the previous call failed
  1906. // and ::Reset() was never called.
  1907. HRESULT CEnumSTATURL::RetrieveNextUrlInfo()
  1908. {
  1909.     HRESULT hr = S_OK;
  1910.     BOOL ok;
  1911.     ASSERT(m_hEnum);
  1912.     while (TRUE)
  1913.     {
  1914.         ok = FindNextUrlCacheEntryBinary(m_hEnum,
  1915.                                              m_lpCEI,
  1916.                                              &m_cbCEI);
  1917.         if (!ok)
  1918.         {
  1919.             DWORD Error = GetLastError ();
  1920.             if (m_lpCEI)
  1921.             {
  1922.                 LocalFree(m_lpCEI);
  1923.                 m_lpCEI = NULL;
  1924.             }
  1925.             if (Error == ERROR_INSUFFICIENT_BUFFER)
  1926.             {
  1927.                 m_lpCEI = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, m_cbCEI);
  1928.                 if (!m_lpCEI)
  1929.                 {
  1930.                     hr = E_OUTOFMEMORY;
  1931.                     break;
  1932.                 }
  1933.             }
  1934.             else
  1935.             {
  1936.                 if (ERROR_NO_MORE_ITEMS == Error)
  1937.                     hr = S_FALSE;
  1938.                 else
  1939.                     hr = HRESULT_FROM_WIN32(Error);
  1940.                 break;
  1941.             }
  1942.         }
  1943.         else break;
  1944.     }
  1945.     m_cbCEI = max(m_cbCEI, DEFAULT_CEI_BUFFER_SIZE);
  1946.     return hr;
  1947. }
  1948. HRESULT CEnumSTATURL::Next(ULONG celt, LPSTATURL rgelt, ULONG * pceltFetched)
  1949.     /*++
  1950.      Routine Description:
  1951.      Searches through the History looking for URLs that match the search pattern,
  1952.      and copies the STATURL into the buffer.
  1953.      Arguments:
  1954.      Return Value:
  1955.      --*/
  1956. {
  1957.     HRESULT hr = S_OK;
  1958.     BOOL found = FALSE;
  1959.     LPHISTDATA phd = NULL;
  1960.     if(pceltFetched)
  1961.         *pceltFetched = 0;
  1962.     if(!celt)
  1963.         goto quit;
  1964.     if (!m_hEnum)
  1965.     {
  1966.         //must handle new enumerator
  1967.         CUrlHistory::s_ConvertToPrefixedUrlW(m_poszFilter, _szPrefixedUrl, ARRAYSIZE(_szPrefixedUrl), &m_lpszFragment);
  1968.         //loop until we get our first handle or bag out
  1969.         hr = RetrieveFirstUrlInfo();
  1970.         if (S_OK != hr || !m_lpCEI)
  1971.             goto quit;
  1972.         m_cchPrefixedUrl = lstrlen(_szPrefixedUrl);
  1973.         while(StrCmpN(_szPrefixedUrl, m_lpCEI->lpszSourceUrlName, m_cchPrefixedUrl))
  1974.         {
  1975.             hr = RetrieveNextUrlInfo();
  1976.             if(S_OK != hr || !m_lpCEI)
  1977.                 goto quit;
  1978.         }
  1979.     }
  1980.     else
  1981.     {
  1982.         do
  1983.         {
  1984.             hr = RetrieveNextUrlInfo();
  1985.             if (S_OK != hr || !m_lpCEI)
  1986.                 goto quit;
  1987.         } while(StrCmpN(_szPrefixedUrl, m_lpCEI->lpszSourceUrlName, m_cchPrefixedUrl));
  1988.     }
  1989.     hr = CUrlHistory::s_GenerateSTATURL(m_lpCEI, m_dwFilter, rgelt);
  1990.     if(SUCCEEDED(hr) && pceltFetched)
  1991.         (*pceltFetched)++;
  1992. quit:
  1993.     if (pceltFetched) {
  1994.         ASSERT((0 == *pceltFetched && (S_FALSE == hr || FAILED(hr))) ||
  1995.                (*pceltFetched && S_OK == hr));
  1996.     }
  1997.     return hr;
  1998. }
  1999. HISTEXTRA* CHistoryData::CopyExtra(HISTEXTRA* phextCur) const
  2000. {
  2001.     const HISTEXTRA* phext;
  2002.     for (phext = _GetExtra();
  2003.          !phext->IsTerminator();
  2004.          phext = phext->GetNextFast())
  2005.     {
  2006.         if (phext->vtExtra != VT_EMPTY) {
  2007.             TraceMsg(DM_HISTEXTRA, "CHD::CopyExtra copying vt=%d id=%d %d bytes",
  2008.                     phext->vtExtra, phext->idExtra, phext->cbExtra);
  2009.             memcpy(phextCur, phext, phext->cbExtra);
  2010.             phextCur = phextCur->GetNextFastForSave();
  2011.         } else {
  2012.             TraceMsg(DM_HISTEXTRA, "CHD::CopyExtra skipping vt=%d id=%d %d bytes",
  2013.                     phext->vtExtra, phext->idExtra, phext->cbExtra);
  2014.         }
  2015.     }
  2016.     return phextCur;
  2017. }
  2018. CHistoryData* CHistoryData::s_AllocateHeaderInfo(UINT cbExtra, const HISTDATA* phdPrev, ULONG* pcbTotal)
  2019. {
  2020.     DWORD cbTotal = SIZEOF(HISTDATA) + SIZEOF(DWORD) + cbExtra;
  2021.     LPHISTDATA phdNew = (LPHISTDATA)LocalAlloc(LPTR, cbTotal);
  2022.     if (phdNew) {
  2023.         if (phdPrev) {
  2024.             *phdNew = *phdPrev; // Copy all the field
  2025.         }
  2026.         phdNew->cbSize = SIZEOF(HISTDATA);
  2027.         phdNew->cbVer = HISTDATA_VER;
  2028.         *pcbTotal = cbTotal;
  2029.     }
  2030.     return phdNew;
  2031. }
  2032. //
  2033. // Returns the total size of extra data (exclude VT_EMPTY)
  2034. //
  2035. UINT CHistoryData::GetTotalExtraSize() const
  2036. {
  2037.     const HISTEXTRA* phext;
  2038.     UINT cbTotal = 0;
  2039.     for (phext = _GetExtra();
  2040.          !phext->IsTerminator();
  2041.          phext = phext->GetNextFast())
  2042.     {
  2043.         if (phext->vtExtra != VT_EMPTY) {
  2044.             cbTotal += phext->cbExtra;
  2045.         }
  2046.     }
  2047.     return cbTotal;
  2048. }
  2049. HRESULT CEnumSTATURL::Skip(ULONG celt)
  2050. {
  2051.     return E_NOTIMPL;
  2052. }
  2053. HRESULT CEnumSTATURL::Reset(void)
  2054. {
  2055.     if(m_hEnum)
  2056.     {
  2057.         FindCloseUrlCache(m_hEnum);
  2058.         m_hEnum = NULL;
  2059.     }
  2060.     if(m_poszFilter)
  2061.     {
  2062.         LocalFree(m_poszFilter);
  2063.         m_poszFilter = NULL;
  2064.     }
  2065.     if(m_lpCEI)
  2066.     {
  2067.         LocalFree(m_lpCEI);
  2068.         m_lpCEI = NULL;
  2069.     }
  2070.     m_dwFilter = 0;
  2071.     return S_OK;
  2072. }
  2073. HRESULT CEnumSTATURL::Clone(IEnumSTATURL ** ppenum)
  2074. {
  2075.     return E_NOTIMPL;
  2076. }
  2077. //  IEnumSTATURL methods
  2078. HRESULT CEnumSTATURL::SetFilter(LPCWSTR poszFilter, DWORD dwFlags)
  2079. {
  2080.     HRESULT hr = S_OK;
  2081.     if(poszFilter)
  2082.     {
  2083.         m_poszFilter = StrDupW(poszFilter);
  2084.         if (!m_poszFilter)
  2085.         {
  2086.             hr = E_OUTOFMEMORY;
  2087.             goto quit;
  2088.         }
  2089.     }
  2090.     m_dwFilter = dwFlags;
  2091. quit:
  2092.     return hr;
  2093. }
  2094. const HISTEXTRA * CHistoryData::_FindExtra(UINT idExtra) const
  2095. {
  2096.     for (const HISTEXTRA* phext = _GetExtra();
  2097.          !phext->IsTerminator();
  2098.          phext = phext->GetNextFastForSave())
  2099.     {
  2100.         if (phext->idExtra == idExtra) {
  2101.             return phext;
  2102.         }
  2103.     }
  2104.     return NULL;
  2105. }
  2106. CHistoryData* CHistoryData::s_GetHistoryData(LPINTERNET_CACHE_ENTRY_INFO lpCEI)
  2107. {
  2108.     CHistoryData* phd = (CHistoryData*)lpCEI->lpHeaderInfo;
  2109.     if (phd && phd->_IsOldHistory()) {
  2110.         TraceMsg(DM_TRACE, "CHistoryData::GetHistoryData found old header. Ignore");
  2111.         phd = NULL;
  2112.     }
  2113.     if (phd && phd->cbVer != HISTDATA_VER) {
  2114.         TraceMsg(DM_TRACE, "CHistoryData::GetHistoryData found old header (%d). Ignore",
  2115.                  phd->cbVer);
  2116.         phd = NULL;
  2117.     }
  2118.     return phd;
  2119. }
  2120. BOOL CHistoryData::_HasFragment(LPCTSTR pszFragment) const
  2121. {
  2122.     BOOL fHas = FALSE;
  2123.     const HISTEXTRA* phext = _FindExtra(PID_INTSITE_FRAGMENT);
  2124.     if (phext) {
  2125.         for (LPCTSTR psz=(LPCTSTR)(phext->abExtra); *psz ; psz += lstrlen(psz)+1) {
  2126.             if (StrCmp(psz, pszFragment)==0) {
  2127.                 fHas = TRUE;
  2128.                 break;
  2129.             }
  2130.         }
  2131.     }
  2132.     return fHas;
  2133. }
  2134. void CHistoryData::_GetTitle(LPTSTR szTitle, UINT cchMax) const
  2135. {
  2136.     szTitle[0] = '';
  2137.     const HISTEXTRA* phext = _FindExtra(PID_INTSITE_TITLE);
  2138.     if (phext && phext->vtExtra == VT_LPSTR) {
  2139.         StrCpyN(szTitle, (LPCTSTR)phext->abExtra, cchMax);
  2140.     }
  2141. }
  2142. #ifdef USE_NEW_HISTORYDATA
  2143. #include "urlprop2.cpp"
  2144. #endif // USE_NEW_HISTORYDATA