2724 lines
75 KiB
C++
2724 lines
75 KiB
C++
//
|
|
// Copyright (c) 1996 Microsoft Corporation
|
|
//
|
|
// Module Name: Url History Interfaces
|
|
//
|
|
// Author:
|
|
// Zeke Lucas (zekel) 10-April-96
|
|
//
|
|
|
|
// !!! Take NOTE: CUrlHistory *MUST* be thread safe! DANGER, WILL ROBINSON, DANGER!
|
|
|
|
#include "priv.h"
|
|
#include "sccls.h"
|
|
#include "ishcut.h"
|
|
#include <inetreg.h>
|
|
#include <varutil.h>
|
|
#include "iface.h"
|
|
#include "util.h"
|
|
|
|
#define DM_UHRETRIEVE 0
|
|
#define DM_URLCLEANUP 0
|
|
#define DM_HISTGENERATE 0
|
|
#define DM_HISTPROP 0
|
|
#define DM_HISTEXTRA 0
|
|
#define DM_HISTCOMMIT 0
|
|
#define DM_HISTSPLAT 0
|
|
#define DM_HISTMISS 0
|
|
#define DM_HISTNLS 0
|
|
|
|
#define DW_FOREVERLOW (0xFFFFFFFF)
|
|
#define DW_FOREVERHIGH (0x7FFFFFFF)
|
|
|
|
#ifdef UNICODE
|
|
#define VT_LPTSTR VT_LPWSTR
|
|
#else
|
|
#define VT_LPTSTR VT_LPSTR
|
|
#endif
|
|
|
|
inline UINT DW_ALIGNED(UINT i) {
|
|
return ((i+3) & 0xfffffffc);
|
|
}
|
|
|
|
inline BOOL IS_DW_ALIGNED(UINT i) {
|
|
return ((i & 3)==0);
|
|
}
|
|
|
|
// Old one (beta-2)
|
|
typedef struct _HISTDATAOLD
|
|
{
|
|
WORD cbSize;
|
|
DWORD dwFlags;
|
|
WORD wTitleOffset;
|
|
WORD aFragsOffset;
|
|
WORD cFrags; //right now the top five bits are used for Prop_MshtmlMCS
|
|
WORD wPropNameOffset;
|
|
WORD wMCSIndex;
|
|
} HISTDATAOLD, *LPHISTDATAOLD;
|
|
|
|
// Forward reference
|
|
typedef struct HISTEXTRA* LPHISTEXTRA;
|
|
|
|
// Version 0.01
|
|
//
|
|
// PID_INTSITE_WHATSNEW stored as a HISTEXTRA
|
|
// PID_INTSITE_AUTHOR stored as a HISTEXTRA
|
|
// PID_INTSITE_LASTVISIT from lpCEI->LastAccessTime
|
|
// PID_INTSITE_LASTMOD from lpCEI->LastModifiedTime
|
|
// PID_INTSITE_VISITCOUNT dwVisits
|
|
// PID_INTSITE_DESCRIPTION stored as a HISTEXTRA
|
|
// PID_INTSITE_COMMENT stored as a HISTEXTRA
|
|
// PID_INTSITE_FLAGS dwFlags
|
|
// PID_INTSITE_CONTENTLEN (never used)
|
|
// PID_INTSITE_CONTENTCODE (never used)
|
|
// PID_INTSITE_RECURSE (never used)
|
|
// PID_INTSITE_WATCH dwWatch
|
|
// PID_INTSITE_SUBSCRIPTION stored as a HISTEXTRA
|
|
// PID_INTSITE_URL URL itself
|
|
// PID_INTSITE_TITLE Title
|
|
// PID_INTSITE_FRAGMENT Visited Fragment (private)
|
|
//
|
|
|
|
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// HACKHACK: If you change this data structure, you must talk
|
|
// to Adrian Canter (adrianc) -- we put a copy of it
|
|
// in wininet\urlcache\401imprt.cxx to make importing
|
|
// from old-style cache happen quick n' dirty
|
|
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
struct _HISTDATA_V001
|
|
{
|
|
UINT cbSize : 16; // size of this header
|
|
UINT cbVer : 16; // version
|
|
DWORD dwFlags; // PID_INTSITE_FLAGS (PIDISF_ flags)
|
|
DWORD dwWatch; // PID_INTSITE_WATCH (PIDISM_ flags)
|
|
DWORD dwVisits; // PID_INTSITE_VISITCOUNT
|
|
};
|
|
|
|
#define HISTDATA_VER 2
|
|
|
|
class CHistoryData : public _HISTDATA_V001
|
|
{
|
|
public:
|
|
LPHISTEXTRA _GetExtra(void) const {
|
|
ASSERT( this->cbSize == sizeof(_HISTDATA_V001) );
|
|
ASSERT( this->cbVer == HISTDATA_VER );
|
|
return (LPHISTEXTRA)(((BYTE*)this) + this->cbSize);
|
|
}
|
|
|
|
const HISTEXTRA * _FindExtra(UINT idExtra) const;
|
|
HISTEXTRA * _FindExtraForSave(UINT idExtra) {
|
|
return (HISTEXTRA*)_FindExtra(idExtra);
|
|
}
|
|
void _GetTitle(LPTSTR szTitle, UINT cchMax) const;
|
|
BOOL _HasFragment(LPCTSTR pszFragment) const;
|
|
BOOL _IsOldHistory(void) const {
|
|
return (cbSize==SIZEOF(HISTDATAOLD) && cbVer==0);
|
|
};
|
|
|
|
static CHistoryData* s_GetHistoryData(LPINTERNET_CACHE_ENTRY_INFO lpCEI);
|
|
static CHistoryData* s_AllocateHeaderInfo(UINT cbExtra, const CHistoryData* phdPrev, ULONG* pcbTotal);
|
|
|
|
HISTEXTRA* CopyExtra(HISTEXTRA* phextCur) const;
|
|
UINT GetTotalExtraSize() const;
|
|
};
|
|
|
|
|
|
//
|
|
// Right after HISTDATA (always at cbSize), we have optional (typically
|
|
// variable length) data which has following data structure. It may have
|
|
// more than one but always has a null-terimiator (cbExtra == 0).
|
|
//
|
|
struct HISTEXTRA
|
|
{
|
|
UINT cbExtra : 16;
|
|
UINT idExtra : 8; // PID_INTSITE_*
|
|
UINT vtExtra : 8; // VT_*
|
|
BYTE abExtra[1]; // abExtra[cbExtra-4];
|
|
|
|
BOOL IsTerminator(void) const {
|
|
return (this->cbExtra==0);
|
|
}
|
|
|
|
const HISTEXTRA* GetNextFast(void) const {
|
|
ASSERT( ! IsTerminator() );
|
|
return (LPHISTEXTRA)(((BYTE*)this) + this->cbExtra);
|
|
}
|
|
|
|
HISTEXTRA* GetNextFastForSave(void) const {
|
|
ASSERT( ! IsTerminator() );
|
|
return (LPHISTEXTRA)(((BYTE*)this) + this->cbExtra);
|
|
}
|
|
|
|
const HISTEXTRA* GetNext(void) const {
|
|
if (this->cbExtra) {
|
|
return (LPHISTEXTRA)(((BYTE*)this) + this->cbExtra);
|
|
}
|
|
return NULL;
|
|
}
|
|
};
|
|
|
|
|
|
// We want to make sure that our history binary data is valid so
|
|
// we don't crash or something
|
|
BOOL ValidateHistoryData(LPINTERNET_CACHE_ENTRY_INFOA pcei)
|
|
{
|
|
DWORD cb = 0;
|
|
|
|
if (!pcei->lpHeaderInfo)
|
|
{
|
|
ASSERT(pcei->dwHeaderInfoSize==0);
|
|
pcei->dwHeaderInfoSize = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
// First, let's check HISTDATA
|
|
CHistoryData* phd = (CHistoryData*)pcei->lpHeaderInfo;
|
|
if ((phd->cbSize!=sizeof(_HISTDATA_V001))
|
|
||
|
|
(phd->cbSize > pcei->dwHeaderInfoSize))
|
|
{
|
|
pcei->dwHeaderInfoSize = 0;
|
|
pcei->lpHeaderInfo = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
cb += phd->cbSize;
|
|
|
|
// Now, let's check HISTEXTRA
|
|
LPHISTEXTRA phe = phd->_GetExtra();
|
|
while (phe && !phe->IsTerminator())
|
|
{
|
|
cb += phe->cbExtra;
|
|
if (cb >= pcei->dwHeaderInfoSize)
|
|
{
|
|
// Hmm. We're expecting more data than we got. Not good. Prune the rest off.
|
|
// We're adding 1 for the terminator
|
|
pcei->dwHeaderInfoSize = cb - phe->cbExtra + 4;
|
|
phe->cbExtra = 0;
|
|
return FALSE;
|
|
}
|
|
phe = phe->GetNextFastForSave();
|
|
}
|
|
|
|
// Add a DWORD for the terminator
|
|
cb += sizeof(DWORD);
|
|
// ASSERT(pcei->dwHeaderInfoSize==cb);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Typically, we need 200-300 bytes to retrieve a cached entry in this
|
|
// history database. To avoid allocating memory in 99% of cases, we
|
|
// allocate 500 bytes in the stack and call LocalAlloc only if we need
|
|
// more than that.
|
|
//
|
|
#define DEFAULT_CEI_BUFFER_SIZE (500 * sizeof(WCHAR))
|
|
|
|
const TCHAR c_szHistoryPrefix[] = TEXT("Visited: ");
|
|
|
|
struct CEI_PREALLOC {
|
|
LPINTERNET_CACHE_ENTRY_INFO pcei;
|
|
|
|
LPCTSTR pszFragment;
|
|
TCHAR szPrefixedUrl[MAX_URL_STRING + ARRAYSIZE(c_szHistoryPrefix)];
|
|
|
|
union {
|
|
#ifdef UNIX
|
|
double alignOn8ByteBoundary;
|
|
#endif /* UNIX */
|
|
INTERNET_CACHE_ENTRY_INFO cei;
|
|
BYTE ab[DEFAULT_CEI_BUFFER_SIZE];
|
|
};
|
|
|
|
CEI_PREALLOC() : pcei(NULL), pszFragment(NULL) {}
|
|
~CEI_PREALLOC() {
|
|
if (pcei && pcei != &cei) {
|
|
TraceMsg(DM_TRACE, "CEI_PREALLOC::dtr freeing pcei");
|
|
LocalFree(pcei);
|
|
pcei = NULL;
|
|
}
|
|
}
|
|
};
|
|
|
|
#define VER_HISTDATA 1
|
|
|
|
typedef CHistoryData HISTDATA;
|
|
|
|
typedef HISTDATA* LPHISTDATA;
|
|
|
|
// CUrlHistory manages the other interfaces and handles alot of the basic functions
|
|
class CUrlHistory : public IUrlHistoryPriv
|
|
{
|
|
public:
|
|
CUrlHistory (void);
|
|
~CUrlHistory(void);
|
|
|
|
// IUnknown methods
|
|
|
|
virtual STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppvObj);
|
|
virtual STDMETHODIMP_(ULONG) AddRef(void);
|
|
virtual STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IUrlHistoryStg methods
|
|
STDMETHODIMP AddUrl(LPCWSTR pwszUrl, LPCWSTR pwszTitle, DWORD dwFlags);
|
|
STDMETHODIMP DeleteUrl(LPCWSTR pwszUrl, DWORD dwFlags);
|
|
STDMETHODIMP QueryUrl(LPCWSTR pwszUrl, DWORD dwFlags, LPSTATURL lpSTATURL);
|
|
STDMETHODIMP BindToObject(LPCWSTR pwszUrl, REFIID riid, void **ppvOut);
|
|
STDMETHODIMP EnumUrls(IEnumSTATURL **ppEnum);
|
|
|
|
// IUrlHistoryStg2 methods
|
|
STDMETHODIMP AddUrlAndNotify(LPCWSTR pwszUrl, LPCWSTR pwszTitle, DWORD dwFlags, BOOL fWriteHistory, IOleCommandTarget *poctNotify, IUnknown *punkSFHistory);
|
|
STDMETHODIMP ClearHistory();
|
|
|
|
// IUrlHistoryPriv methods
|
|
STDMETHOD(QueryUrlA)(LPCSTR pszUrl, DWORD dwFlags, LPSTATURL lpSTATURL);
|
|
STDMETHOD(CleanupHistory)(void);
|
|
STDMETHOD_(DWORD,GetDaysToKeep)(void) { return s_GetDaysToKeep(); }
|
|
STDMETHOD(GetProperty)(LPCTSTR pszUrl, PROPID pid, PROPVARIANT* pvarOut);
|
|
STDMETHOD(GetUserName)(LPTSTR pszUserName, DWORD cchUserName);
|
|
STDMETHOD(AddUrlAndNotifyCP)(LPCWSTR pwszUrl, LPCWSTR pwszTitle, DWORD dwFlags, BOOL fWriteHistory, IOleCommandTarget *poctNotify, IUnknown *punkSFHistory, UINT* pcodepage);
|
|
|
|
|
|
static void s_Init();
|
|
static DWORD s_GetDaysToKeep(void);
|
|
#if defined(ux10) && defined(UNIX)
|
|
static void s_SetDaysToKeep(DWORD dwDays);
|
|
#endif
|
|
|
|
|
|
protected:
|
|
|
|
void _WriteToHistory(LPCTSTR pszPrefixedurl,
|
|
FILETIME& ftExpires,
|
|
IOleCommandTarget *poctNotify,
|
|
IUnknown *punkSFHistory);
|
|
|
|
friend class CEnumSTATURL;
|
|
// friend class CUrlHObj;
|
|
friend class IntsiteProp;
|
|
|
|
static HRESULT s_CleanupHistory(void);
|
|
static HRESULT s_EnumUrls(IEnumSTATURL **ppEnum);
|
|
static HRESULT s_DeleteUrl(LPCWSTR pwszUrl, DWORD dwFlags);
|
|
|
|
static void s_ConvertToPrefixedUrlW(
|
|
IN LPCWSTR pwszUrl,
|
|
OUT LPTSTR pszPrefixedUrl,
|
|
IN DWORD cchPrefixedUrl,
|
|
OUT LPCTSTR *ppszFragment
|
|
);
|
|
|
|
static HRESULT s_QueryUrlCommon(
|
|
LPCTSTR lpszPrefixedUrl,
|
|
LPCTSTR lpszFragment,
|
|
DWORD dwFlags,
|
|
LPSTATURL lpSTATURL
|
|
);
|
|
|
|
static void s_RetrievePrefixedUrlInfo(
|
|
LPCTSTR lpszUrl, CEI_PREALLOC* pbuf);
|
|
static BOOL s_CommitUrlCacheEntry(LPCTSTR pszPrefixedUrl,
|
|
LPINTERNET_CACHE_ENTRY_INFO pcei);
|
|
|
|
static BOOL s_IsCached(IN LPCTSTR pszUrl)
|
|
{ return ::GetUrlCacheEntryInfoEx(pszUrl, NULL, NULL, NULL, NULL, NULL, INTERNET_CACHE_FLAG_ALLOW_COLLISIONS); }
|
|
|
|
static HISTDATA* s_GenerateHeaderInfo(
|
|
IN LPCTSTR pszTitle,
|
|
IN HISTDATA* phdPrev,
|
|
IN LPCTSTR pszFragment,
|
|
OUT LPDWORD pcbHeader
|
|
);
|
|
|
|
static HRESULT s_GenerateSTATURL(IN PCTSTR pszUrl, IN LPINTERNET_CACHE_ENTRY_INFO lpCEI, IN DWORD dwFlags, OUT LPSTATURL lpsu);
|
|
static void s_UpdateIcon(Intshcut* pintshcut, DWORD dwFlags);
|
|
|
|
DWORD _cRef;
|
|
static TCHAR s_szUserPrefix[INTERNET_MAX_USER_NAME_LENGTH + 1];
|
|
static DWORD s_cchUserPrefix ;
|
|
static DWORD s_dwDaysToKeep;
|
|
|
|
};
|
|
|
|
|
|
class CEnumSTATURL : public IEnumSTATURL
|
|
{
|
|
public:
|
|
|
|
CEnumSTATURL() : _cRef(1) {}
|
|
~CEnumSTATURL();
|
|
|
|
// IUnknown methods
|
|
|
|
STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppvObj);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IEnumXXXX methods
|
|
|
|
STDMETHODIMP Next (ULONG celt, LPSTATURL rgelt, ULONG * pceltFetched) ;
|
|
STDMETHODIMP Skip(ULONG celt) ;
|
|
STDMETHODIMP Reset(void) ;
|
|
STDMETHODIMP Clone(IEnumSTATURL ** ppenum) ;
|
|
|
|
// IEnumSTATURL methods
|
|
|
|
STDMETHODIMP SetFilter(LPCWSTR poszFilter, DWORD dwFlags) ;
|
|
|
|
private:
|
|
|
|
HRESULT RetrieveFirstUrlInfo(void);
|
|
HRESULT RetrieveNextUrlInfo(void);
|
|
|
|
DWORD _cRef;
|
|
|
|
// search object parameters
|
|
LPWSTR m_poszFilter;
|
|
DWORD m_dwFilter;
|
|
|
|
HANDLE m_hEnum;
|
|
TCHAR _szPrefixedUrl[MAX_URL_STRING];
|
|
DWORD m_cchPrefixedUrl;
|
|
LPCTSTR m_lpszFragment;
|
|
LPINTERNET_CACHE_ENTRY_INFO m_lpCEI;
|
|
DWORD m_cbCEI;
|
|
|
|
};
|
|
|
|
|
|
#if defined(ux10) && defined(UNIX)
|
|
//Work around for mmap limitation in hp-ux10. Change the corresponding
|
|
//value even in inetcpl/general.cpp
|
|
#define MAX_HISTORY_DAYS 30
|
|
#endif
|
|
|
|
#define FILETIME_SEC 10000000
|
|
#define SECS_PER_DAY (60 * 60 * 24)
|
|
|
|
#define CCHHISTORYPREFIX (ARRAYSIZE(c_szHistoryPrefix) - 1)
|
|
#define CLEANUP_HISTORY_INTERVAL (24 * 60 * 60 * 1000) // One day, in milliseconds
|
|
|
|
DWORD g_tCleanupHistory = 0;
|
|
|
|
|
|
#define OFFSET_TO_LPTSTR(p, o) ( (LPTSTR) ( (LPBYTE) (p) + (o) ) )
|
|
#define OFFSET_TO_LPBYTE(p, o) ( (LPBYTE) ( (LPBYTE) (p) + (o) ) )
|
|
#define OFFSET_TO_LPWORD(p, o) ( (LPWORD) ( (LPBYTE) (p) + (o) ) )
|
|
|
|
#define LPTSTR_TO_OFFSET(p, s) ( (WORD) ( (LPTSTR) (s) - (LPTSTR) (p) ) )
|
|
#define LPBYTE_TO_OFFSET(p, b) ( (WORD) ( (LPBYTE) (b) - (LPBYTE) (p) ) )
|
|
|
|
// NOTE: REARCHITECT chrisfra 3/26/97 , ext\cachevu\priv.h has a duplicate copy of this
|
|
// structure and uses it to access cache. this needs to be covered by procedural or
|
|
// object interface and moved to a common location.
|
|
|
|
// This structure uses the flags bits as follows: if HFL_VERSIONED is true, then
|
|
// the rest of the flags word is the version.
|
|
|
|
#define HFL_VERSIONED (0x80000000)
|
|
|
|
|
|
//
|
|
// We store binary data in the lpHeaderInfo field. CommitUrlCacheEntryW tries
|
|
// to convert this data to ansi and messes it up. To get around this we thunk
|
|
// through to the A version CommitUrlCacheEntry.
|
|
//
|
|
|
|
BOOL
|
|
CommitUrlCacheEntryBinary(
|
|
IN LPCWSTR lpszUrlName,
|
|
IN FILETIME ExpireTime,
|
|
IN FILETIME LastModifiedTime,
|
|
IN DWORD CacheEntryType,
|
|
IN LPBYTE lpHeaderInfo,
|
|
IN DWORD dwHeaderSize
|
|
)
|
|
{
|
|
ASSERT(lpszUrlName);
|
|
|
|
CHAR szUrl[MAX_URL_STRING + ARRAYSIZE(c_szHistoryPrefix)];
|
|
|
|
SHUnicodeToAnsi(lpszUrlName, szUrl, ARRAYSIZE(szUrl));
|
|
|
|
INTERNET_CACHE_ENTRY_INFOA cei;
|
|
cei.lpHeaderInfo = (LPSTR)lpHeaderInfo;
|
|
cei.dwHeaderInfoSize = dwHeaderSize;
|
|
ValidateHistoryData(&cei);
|
|
|
|
return CommitUrlCacheEntryA(szUrl, NULL, ExpireTime, LastModifiedTime,
|
|
CacheEntryType, lpHeaderInfo, dwHeaderSize,
|
|
NULL, NULL);
|
|
}
|
|
|
|
GetUrlCacheEntryInfoBinary(
|
|
IN LPCWSTR lpszUrlName,
|
|
OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
|
|
IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
|
|
)
|
|
{
|
|
ASSERT(lpszUrlName);
|
|
|
|
BOOL fRet;
|
|
|
|
CHAR szUrl[MAX_URL_STRING + ARRAYSIZE(c_szHistoryPrefix)];
|
|
|
|
SHUnicodeToAnsi(lpszUrlName, szUrl, ARRAYSIZE(szUrl));
|
|
|
|
//
|
|
// Warning! This doesn't convert any of thge string parameters in
|
|
// lpCacheEntryInfo back to unicode. History only uses
|
|
// lpCacheEntryInfo->lpHeaderInfo so this isn't a problem.
|
|
//
|
|
|
|
fRet = GetUrlCacheEntryInfoA(szUrl,
|
|
(LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
|
|
lpdwCacheEntryInfoBufferSize);
|
|
|
|
//
|
|
// Set unused out paramters to NULL incase someone tries to use them
|
|
//
|
|
|
|
lpCacheEntryInfo->lpszSourceUrlName = NULL;
|
|
lpCacheEntryInfo->lpszLocalFileName = NULL;
|
|
lpCacheEntryInfo->lpszFileExtension = NULL;
|
|
|
|
if (fRet)
|
|
{
|
|
ValidateHistoryData((LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo);
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
//
|
|
// Warning! This function converts cei structures for use by history. It is
|
|
// not a generic conversion. It converts the minimum data required by history.
|
|
//
|
|
int
|
|
CacheEntryInfoAToCacheEntryInfoW(
|
|
LPINTERNET_CACHE_ENTRY_INFOA pceiA,
|
|
LPINTERNET_CACHE_ENTRY_INFOW pceiW,
|
|
int cbceiW
|
|
)
|
|
{
|
|
int nRet;
|
|
|
|
ASSERT(pceiA->lpszSourceUrlName);
|
|
int cchSourceUrlName = lstrlenA(pceiA->lpszSourceUrlName) + 1;
|
|
|
|
int cbRequired = sizeof(INTERNET_CACHE_ENTRY_INFOA) +
|
|
pceiA->dwHeaderInfoSize +
|
|
cchSourceUrlName * sizeof(WCHAR);
|
|
|
|
if (cbRequired <= cbceiW)
|
|
{
|
|
ASSERT(sizeof(*pceiA) == sizeof(*pceiW));
|
|
|
|
// Copy the structure.
|
|
*pceiW = *(INTERNET_CACHE_ENTRY_INFOW*)pceiA;
|
|
|
|
// Append the binary data. Note dwHeaderInfoSize is already copied.
|
|
pceiW->lpHeaderInfo = (LPWSTR)(pceiW + 1);
|
|
memcpy(pceiW->lpHeaderInfo, pceiA->lpHeaderInfo, pceiA->dwHeaderInfoSize);
|
|
|
|
// Append the source url name.
|
|
pceiW->lpszSourceUrlName = (LPWSTR)((BYTE*)(pceiW + 1) + pceiW->dwHeaderInfoSize);
|
|
SHAnsiToUnicode(pceiA->lpszSourceUrlName, pceiW->lpszSourceUrlName,
|
|
cchSourceUrlName);
|
|
|
|
// Null out bogus pointers so we'll fault if someone deref's them
|
|
pceiW->lpszLocalFileName = NULL;
|
|
pceiW->lpszFileExtension = NULL;
|
|
|
|
nRet = 0;
|
|
}
|
|
else
|
|
{
|
|
nRet = cbRequired;
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
HANDLE
|
|
FindFirstUrlCacheEntryBinary(
|
|
IN LPCWSTR lpszUrlSearchPattern,
|
|
OUT LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
|
|
IN OUT LPDWORD lpdwFirstCacheEntryInfoBufferSize
|
|
)
|
|
{
|
|
ASSERT(NULL != lpszUrlSearchPattern);
|
|
ASSERT(NULL != lpFirstCacheEntryInfo);
|
|
ASSERT(NULL != lpdwFirstCacheEntryInfoBufferSize);
|
|
|
|
HANDLE hRet;
|
|
|
|
CHAR szPattern[MAX_PATH];
|
|
|
|
ASSERT(lstrlenW(lpszUrlSearchPattern) < ARRAYSIZE(szPattern));
|
|
SHUnicodeToAnsi(lpszUrlSearchPattern, szPattern, ARRAYSIZE(szPattern));
|
|
|
|
BYTE ab[MAX_CACHE_ENTRY_INFO_SIZE];
|
|
INTERNET_CACHE_ENTRY_INFOA* pceiA = (INTERNET_CACHE_ENTRY_INFOA*)ab;
|
|
DWORD dwSize;
|
|
BOOL fAllocated = FALSE;
|
|
|
|
pceiA->dwStructSize = dwSize = sizeof(ab);
|
|
|
|
hRet = FindFirstUrlCacheEntryA(szPattern, pceiA, &dwSize);
|
|
|
|
if (NULL == hRet && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
pceiA = (INTERNET_CACHE_ENTRY_INFOA*)LocalAlloc(LPTR, dwSize);
|
|
|
|
if (pceiA)
|
|
{
|
|
fAllocated = TRUE;
|
|
|
|
pceiA->dwStructSize = dwSize;
|
|
|
|
hRet = FindFirstUrlCacheEntryA(szPattern, pceiA, &dwSize);
|
|
|
|
ASSERT(hRet || GetLastError() != ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
if (hRet)
|
|
{
|
|
int nRet;
|
|
|
|
ValidateHistoryData(pceiA);
|
|
nRet = CacheEntryInfoAToCacheEntryInfoW(pceiA, lpFirstCacheEntryInfo,
|
|
*lpdwFirstCacheEntryInfoBufferSize);
|
|
|
|
if (nRet)
|
|
{
|
|
FindCloseUrlCache(hRet);
|
|
hRet = NULL;
|
|
*lpdwFirstCacheEntryInfoBufferSize = nRet;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
}
|
|
|
|
if (fAllocated)
|
|
{
|
|
LocalFree(pceiA);
|
|
pceiA = NULL;
|
|
}
|
|
|
|
return hRet;
|
|
}
|
|
|
|
BOOL
|
|
FindNextUrlCacheEntryBinary(
|
|
IN HANDLE hEnumHandle,
|
|
OUT LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
|
|
IN OUT LPDWORD lpdwNextCacheEntryInfoBufferSize
|
|
)
|
|
{
|
|
ASSERT(NULL != hEnumHandle);
|
|
ASSERT(NULL != lpNextCacheEntryInfo);
|
|
ASSERT(NULL != lpdwNextCacheEntryInfoBufferSize);
|
|
|
|
BOOL fRet;
|
|
|
|
BYTE ab[MAX_CACHE_ENTRY_INFO_SIZE];
|
|
INTERNET_CACHE_ENTRY_INFOA* pceiA = (INTERNET_CACHE_ENTRY_INFOA*)ab;
|
|
DWORD dwSize;
|
|
BOOL fAllocated = FALSE;
|
|
|
|
pceiA->dwStructSize = dwSize = sizeof(ab);
|
|
|
|
fRet = FindNextUrlCacheEntryA(hEnumHandle, pceiA, &dwSize);
|
|
|
|
if (!fRet && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
pceiA = (INTERNET_CACHE_ENTRY_INFOA*)LocalAlloc(LPTR, dwSize);
|
|
|
|
if (pceiA)
|
|
{
|
|
fAllocated = TRUE;
|
|
|
|
pceiA->dwStructSize = dwSize;
|
|
|
|
fRet = FindNextUrlCacheEntryA(hEnumHandle, pceiA, &dwSize);
|
|
|
|
ASSERT(fRet || GetLastError() != ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
if (fRet)
|
|
{
|
|
int nRet;
|
|
|
|
ValidateHistoryData(pceiA);
|
|
nRet = CacheEntryInfoAToCacheEntryInfoW(pceiA, lpNextCacheEntryInfo,
|
|
*lpdwNextCacheEntryInfoBufferSize);
|
|
|
|
if (nRet)
|
|
{
|
|
fRet = FALSE;
|
|
*lpdwNextCacheEntryInfoBufferSize = nRet;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
}
|
|
|
|
if (fAllocated)
|
|
{
|
|
LocalFree(pceiA);
|
|
pceiA = NULL;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
#define DEFAULT_DAYS_TO_KEEP 21
|
|
static const TCHAR c_szRegValDaysToKeep[] = TEXT("DaysToKeep");
|
|
static const TCHAR c_szRegValDirectory[] = TEXT("Directory");
|
|
|
|
#ifdef UNIX
|
|
#define DIR_SEPARATOR_CHAR TEXT('/')
|
|
#else
|
|
#define DIR_SEPARATOR_CHAR TEXT('\\')
|
|
#endif
|
|
|
|
|
|
// ** this has been moved to util.cpp 07.28.2000 **
|
|
// thunk to shell32.SHGetFolderPath() so this code works downlevel
|
|
// HRESULT SHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
|
|
// ** this has been moved to util.cpp 07.28.2000 **
|
|
|
|
HRESULT SHGetHistoryPIDL(LPITEMIDLIST *ppidlHistory)
|
|
{
|
|
*ppidlHistory = NULL;
|
|
|
|
TCHAR szHistory[MAX_PATH];
|
|
|
|
szHistory[0] = 0;
|
|
|
|
HRESULT hres = SHGetFolderPathD(NULL, CSIDL_HISTORY | CSIDL_FLAG_CREATE, NULL, 0, szHistory);
|
|
if (hres != S_OK)
|
|
{
|
|
GetHistoryFolderPath(szHistory, ARRAYSIZE(szHistory));
|
|
PathRemoveFileSpec(szHistory); // get the trailing slash
|
|
PathRemoveFileSpec(szHistory); // trim the "content.ie5" junk
|
|
}
|
|
|
|
if (szHistory[0])
|
|
{
|
|
TCHAR szIniFile[MAX_PATH];
|
|
PathCombine(szIniFile, szHistory, TEXT("desktop.ini"));
|
|
|
|
if (GetFileAttributes(szIniFile) == -1)
|
|
{
|
|
DWORD dwAttrib = GetFileAttributes(szHistory);
|
|
dwAttrib &= ~FILE_ATTRIBUTE_HIDDEN;
|
|
dwAttrib |= FILE_ATTRIBUTE_SYSTEM;
|
|
|
|
// make sure system, but not hidden
|
|
SetFileAttributes(szHistory, dwAttrib);
|
|
|
|
WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("ConfirmFileOp"), TEXT("0"), szIniFile);
|
|
WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID"), TEXT("{FF393560-C2A7-11CF-BFF4-444553540000}"), szIniFile);
|
|
}
|
|
|
|
IShellFolder *psfDesktop;
|
|
hres = SHGetDesktopFolder(&psfDesktop);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = psfDesktop->ParseDisplayName(NULL, NULL,
|
|
szHistory, NULL, ppidlHistory, NULL);
|
|
psfDesktop->Release();
|
|
}
|
|
}
|
|
else
|
|
hres = E_FAIL;
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// This function is called from hist/hsfolder.cpp
|
|
//
|
|
HRESULT CUrlHistory::GetUserName(LPTSTR pszUserName, DWORD cchUserName)
|
|
{
|
|
s_Init();
|
|
|
|
if (cchUserName < s_cchUserPrefix)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
CopyMemory(pszUserName, s_szUserPrefix, (s_cchUserPrefix-1) * sizeof(TCHAR));
|
|
pszUserName[s_cchUserPrefix-1] = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
#if defined (ux10) && defined(UNIX)
|
|
void CUrlHistory::s_SetDaysToKeep(DWORD dwDays)
|
|
{
|
|
HKEY hk;
|
|
DWORD dwDisp;
|
|
|
|
DWORD Error = RegCreateKeyEx(
|
|
HKEY_CURRENT_USER,
|
|
REGSTR_PATH_URLHISTORY,
|
|
0, NULL, 0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hk,
|
|
&dwDisp);
|
|
|
|
if(ERROR_SUCCESS != Error)
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
Error = RegSetValueEx(
|
|
hk,
|
|
c_szRegValDaysToKeep,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE) &dwDays,
|
|
sizeof(dwDays));
|
|
|
|
ASSERT(ERROR_SUCCESS == Error);
|
|
|
|
RegCloseKey(hk);
|
|
|
|
return;
|
|
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// This function is called from hist/hsfolder.cpp
|
|
//
|
|
DWORD CUrlHistory::s_GetDaysToKeep(void)
|
|
{
|
|
HKEY hk;
|
|
DWORD cbDays = SIZEOF(DWORD);
|
|
DWORD dwDays = DEFAULT_DAYS_TO_KEEP;
|
|
DWORD dwType;
|
|
|
|
|
|
DWORD Error = RegOpenKeyEx(
|
|
HKEY_CURRENT_USER,
|
|
REGSTR_PATH_URLHISTORY,
|
|
0,
|
|
KEY_READ,
|
|
&hk);
|
|
|
|
|
|
if(Error)
|
|
{
|
|
Error = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_URLHISTORY,
|
|
0,
|
|
KEY_READ,
|
|
&hk);
|
|
}
|
|
|
|
|
|
if(!Error)
|
|
{
|
|
Error = RegQueryValueEx(
|
|
hk,
|
|
c_szRegValDaysToKeep,
|
|
0,
|
|
&dwType,
|
|
(LPBYTE) &dwDays,
|
|
&cbDays);
|
|
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
return dwDays;
|
|
}
|
|
|
|
IUrlHistoryPriv* g_puhUrlHistory = NULL;
|
|
|
|
void CUrlHistory_CleanUp()
|
|
{
|
|
// Release will clean up the global
|
|
ENTERCRITICAL;
|
|
if (g_puhUrlHistory)
|
|
g_puhUrlHistory->Release();
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
STDAPI CUrlHistory_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
*ppunk = NULL;
|
|
|
|
// !!! Take NOTE: CUrlHistory *MUST* be thread safe!
|
|
|
|
// aggregation checking is handled in class factory
|
|
ENTERCRITICAL;
|
|
|
|
if (!g_puhUrlHistory)
|
|
{
|
|
CUrlHistory *pcuh = new CUrlHistory;
|
|
if (pcuh)
|
|
{
|
|
g_puhUrlHistory = SAFECAST(pcuh, IUrlHistoryPriv *);
|
|
// The memory tracking code thinks this is a leak
|
|
}
|
|
}
|
|
|
|
if (g_puhUrlHistory)
|
|
{
|
|
*ppunk = SAFECAST(g_puhUrlHistory, IUnknown*);
|
|
g_puhUrlHistory->AddRef();
|
|
hr = S_OK;
|
|
}
|
|
|
|
LEAVECRITICAL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Public members of CUrlHistory
|
|
//
|
|
|
|
CUrlHistory::CUrlHistory(void) : _cRef(1)
|
|
{
|
|
//
|
|
// Update s_dwDaysToKeep for each call
|
|
//
|
|
s_dwDaysToKeep = s_GetDaysToKeep();
|
|
|
|
#if defined(ux10) && defined(UNIX)
|
|
//Work around for mmap limitation in hp-ux10
|
|
if (s_dwDaysToKeep > MAX_HISTORY_DAYS)
|
|
{
|
|
s_dwDaysToKeep = MAX_HISTORY_DAYS;
|
|
s_SetDaysToKeep(s_dwDaysToKeep);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
if (g_dwPrototype & 0x00000020) {
|
|
s_CleanupHistory();
|
|
}
|
|
#endif
|
|
|
|
DllAddRef();
|
|
}
|
|
|
|
CUrlHistory::~CUrlHistory(void)
|
|
{
|
|
DllRelease();
|
|
}
|
|
|
|
HRESULT LoadHistoryShellFolder(IUnknown *punk, IHistSFPrivate **ppsfpHistory)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppsfpHistory = NULL;
|
|
if (punk)
|
|
{
|
|
hr = punk->QueryInterface(IID_IHistSFPrivate, (void **)ppsfpHistory);
|
|
}
|
|
else
|
|
{
|
|
LPITEMIDLIST pidlHistory;
|
|
|
|
hr = SHGetHistoryPIDL(&pidlHistory);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHBindToObject(NULL, IID_IHistSFPrivate, pidlHistory, (void **)ppsfpHistory);
|
|
ILFree(pidlHistory);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// ClearHistory on a per user basis. moved from inetcpl to facilitate changes in
|
|
// implementation.
|
|
HRESULT CUrlHistory::ClearHistory()
|
|
{
|
|
HRESULT hr;
|
|
IEnumSTATURL *penum;
|
|
IHistSFPrivate *psfpHistory = NULL;
|
|
|
|
hr = THR(EnumUrls(&penum));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
penum->SetFilter(NULL, STATURL_QUERYFLAG_NOTITLE);
|
|
|
|
ULONG cFetched;
|
|
STATURL rsu[1] = {{sizeof(STATURL), NULL, NULL}};
|
|
while (SUCCEEDED(penum->Next(1, rsu, &cFetched)) && cFetched)
|
|
{
|
|
ASSERT(rsu[0].pwcsUrl);
|
|
|
|
hr = THR(DeleteUrl(rsu[0].pwcsUrl, URLFLAG_DONT_DELETE_SUBSCRIBED));
|
|
|
|
OleFree(rsu[0].pwcsUrl);
|
|
rsu[0].pwcsUrl = NULL;
|
|
|
|
ASSERT(!rsu[0].pwcsTitle);
|
|
}
|
|
penum->Release();
|
|
}
|
|
hr = LoadHistoryShellFolder(NULL, &psfpHistory);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfpHistory->ClearHistory();
|
|
psfpHistory->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
extern void _FileTimeDeltaDays(FILETIME *pftBase, FILETIME *pftNew, int Days);
|
|
|
|
HRESULT CUrlHistory::s_CleanupHistory(void)
|
|
{
|
|
TraceMsg(DM_URLCLEANUP, "CUH::s_CleanupHistory called");
|
|
|
|
HRESULT hr;
|
|
DWORD tCurrent = GetTickCount();
|
|
|
|
if (!g_tCleanupHistory || (tCurrent > g_tCleanupHistory + CLEANUP_HISTORY_INTERVAL)) {
|
|
g_tCleanupHistory = tCurrent;
|
|
} else {
|
|
#ifdef DEBUG
|
|
if (!(g_dwPrototype & 0x00000020))
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
SYSTEMTIME st;
|
|
FILETIME ftNow;
|
|
FILETIME ftOldest;
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st, &ftNow);
|
|
_FileTimeDeltaDays(&ftNow, &ftOldest, -((int)s_GetDaysToKeep()));
|
|
|
|
IEnumSTATURL * penum = NULL;
|
|
if (SUCCEEDED(s_EnumUrls(&penum)))
|
|
{
|
|
STATURL rsu[1] = {{sizeof(STATURL), NULL, NULL}};
|
|
ULONG cFetched = 0;
|
|
|
|
penum->SetFilter(NULL, STATURL_QUERYFLAG_NOTITLE);
|
|
|
|
while (S_OK == penum->Next(1, rsu, &cFetched))
|
|
{
|
|
ASSERT(cFetched);
|
|
ASSERT(rsu[0].pwcsUrl);
|
|
ASSERT(rsu[0].pwcsTitle==NULL);
|
|
|
|
#ifdef DEBUG
|
|
TCHAR szUrl[MAX_URL_STRING];
|
|
SHUnicodeToTChar(rsu[0].pwcsUrl, szUrl, ARRAYSIZE(szUrl));
|
|
#endif
|
|
// check to see if expires is not special && ftLastUpdated is earlier
|
|
// than we need
|
|
if (CompareFileTime(&(rsu[0].ftLastUpdated), &ftOldest) < 0 &&
|
|
(rsu[0].ftExpires.dwLowDateTime != DW_FOREVERLOW ||
|
|
rsu[0].ftExpires.dwHighDateTime != DW_FOREVERHIGH))
|
|
{
|
|
hr = THR(s_DeleteUrl(rsu[0].pwcsUrl, 0));
|
|
#ifdef DEBUG
|
|
TraceMsg(DM_URLCLEANUP, "CUH::s_Cleanup deleting %s", szUrl);
|
|
#endif
|
|
} else {
|
|
#ifdef DEBUG
|
|
TraceMsg(DM_URLCLEANUP, "CUH::s_Cleanup keeping %s", szUrl);
|
|
#endif
|
|
}
|
|
|
|
CoTaskMemFree(rsu[0].pwcsUrl);
|
|
rsu[0].pwcsUrl = NULL;
|
|
cFetched = 0;
|
|
|
|
ASSERT(!rsu[0].pwcsTitle);
|
|
}
|
|
|
|
penum->Release();
|
|
}
|
|
else
|
|
ASSERT(FALSE);
|
|
|
|
TraceMsg(DM_URLCLEANUP, "CUH::s_CleanupHistory (expensive!) just called");
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUrlHistory::CleanupHistory()
|
|
{
|
|
return CUrlHistory::s_CleanupHistory();
|
|
}
|
|
|
|
|
|
TCHAR CUrlHistory::s_szUserPrefix[INTERNET_MAX_USER_NAME_LENGTH + 1] = TEXT("");
|
|
DWORD CUrlHistory::s_cchUserPrefix = 0;
|
|
DWORD CUrlHistory::s_dwDaysToKeep = 0;
|
|
|
|
|
|
void CUrlHistory::s_Init(void)
|
|
{
|
|
// Cache the user name only once per process
|
|
if (!s_cchUserPrefix)
|
|
{
|
|
ENTERCRITICAL;
|
|
// Maybe it changed since entering the crit sec.
|
|
// This really happened to me (BryanSt)
|
|
// We do it twice for perf reasons.
|
|
if (!s_cchUserPrefix)
|
|
{
|
|
ASSERT(s_szUserPrefix[0] == '\0');
|
|
s_cchUserPrefix = ARRAYSIZE(s_szUserPrefix);
|
|
|
|
// Get the current user or set to default
|
|
::GetUserName(s_szUserPrefix, &s_cchUserPrefix);
|
|
|
|
StrCatBuff(s_szUserPrefix, TEXT("@"), ARRAYSIZE(s_szUserPrefix));
|
|
s_cchUserPrefix = lstrlen(s_szUserPrefix);
|
|
}
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
HRESULT CUrlHistory::QueryInterface(REFIID riid, PVOID *ppvObj)
|
|
{
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
|
|
*ppvObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
|
IsEqualIID(riid, IID_IUrlHistoryStg2) ||
|
|
IsEqualIID(riid, IID_IUrlHistoryPriv) ||
|
|
IsEqualIID(riid, IID_IUrlHistoryStg))
|
|
{
|
|
AddRef();
|
|
*ppvObj = (LPVOID) SAFECAST(this, IUrlHistoryPriv *);
|
|
hr = S_OK;
|
|
|
|
}
|
|
else if (IsEqualIID(riid, CLSID_CUrlHistory))
|
|
{
|
|
AddRef();
|
|
*ppvObj = (LPVOID) this;
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
ULONG CUrlHistory::AddRef(void)
|
|
{
|
|
_cRef++;
|
|
|
|
return _cRef;
|
|
}
|
|
|
|
ULONG CUrlHistory::Release(void)
|
|
{
|
|
ASSERT(_cRef > 0);
|
|
|
|
_cRef--;
|
|
|
|
if (!_cRef)
|
|
{
|
|
//time to go bye bye
|
|
ENTERCRITICAL;
|
|
g_puhUrlHistory = NULL;
|
|
LEAVECRITICAL;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return _cRef;
|
|
}
|
|
|
|
//
|
|
// Converts a normal URL to a URL with the correct cache prefix.
|
|
// it also finds a fragment (local Anchor) if it exists in the URL.
|
|
//
|
|
// if the URL is invalid, then the returned lplpszPrefixedUrl is just
|
|
// the prefix. this is used primarily for doing enumeration.
|
|
//
|
|
void CUrlHistory::s_ConvertToPrefixedUrlW(
|
|
IN LPCWSTR pszUrl,
|
|
OUT LPTSTR pszPrefixedUrl,
|
|
IN DWORD cchPrefixedUrl,
|
|
OUT LPCTSTR *ppszFragment
|
|
)
|
|
{
|
|
//
|
|
// Make it sure that s_cchUserPrefix is initialized.
|
|
//
|
|
s_Init();
|
|
|
|
// Prefix + UserPrefix + '@'
|
|
|
|
ASSERT(pszPrefixedUrl && ppszFragment);
|
|
|
|
// clear the out params
|
|
pszPrefixedUrl[0] = L'\0';
|
|
*ppszFragment = NULL;
|
|
|
|
|
|
// if there is no URL, send back a default case
|
|
// this is just for EnumObjects
|
|
if (!pszUrl || !*pszUrl)
|
|
{
|
|
wnsprintf(pszPrefixedUrl, cchPrefixedUrl, L"%s%s", c_szHistoryPrefix, s_szUserPrefix);
|
|
}
|
|
else
|
|
{
|
|
int slen;
|
|
int nScheme;
|
|
LPWSTR pszFragment;
|
|
|
|
wnsprintf(pszPrefixedUrl, cchPrefixedUrl, L"%s%s", c_szHistoryPrefix, s_szUserPrefix);
|
|
slen = lstrlen(pszPrefixedUrl);
|
|
StrCpyN(pszPrefixedUrl + slen, pszUrl, MAX_URL_STRING-slen);
|
|
|
|
// Only strip the anchor fragment if it's not JAVASCRIPT: or VBSCRIPT:, because a # could not an
|
|
// anchor but a string to be evaluated by a script engine like #00ff00 for an RGB color.
|
|
nScheme = GetUrlSchemeW(pszPrefixedUrl);
|
|
if (nScheme == URL_SCHEME_JAVASCRIPT || nScheme == URL_SCHEME_VBSCRIPT)
|
|
{
|
|
pszFragment = NULL;
|
|
}
|
|
else
|
|
{
|
|
// locate local anchor fragment if possible
|
|
pszFragment = StrChr(pszPrefixedUrl + slen, L'#'); // a-naghej Added " + slen" to fix WinSe bug# 13822 & 13926
|
|
}
|
|
|
|
if(pszFragment)
|
|
{
|
|
// kill the '#' so that lpszPrefixedUrl is isolated
|
|
*pszFragment = L'\0';
|
|
*ppszFragment = pszFragment+1;
|
|
}
|
|
|
|
// check for trailing slash and eliminate
|
|
LPWSTR pszT = CharPrev(pszPrefixedUrl, pszPrefixedUrl + lstrlen(pszPrefixedUrl));
|
|
if (pszT[0] == L'/') {
|
|
TraceMsg(DM_HISTNLS, "CUH::s_Convert removing the trailing slash of %s", pszPrefixedUrl);
|
|
ASSERT(lstrlen(pszT)==1);
|
|
pszT[0] = L'\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Basically a wrapper function for RetreiveUrlCacheEntryInfo
|
|
// meant to be called with the prefixed Url.
|
|
// it handles allocating the buffer and reallocating if necessary.
|
|
//
|
|
void CUrlHistory::s_RetrievePrefixedUrlInfo(
|
|
LPCTSTR pszUrl, CEI_PREALLOC* pbuf)
|
|
{
|
|
TraceMsg(DM_UHRETRIEVE, "CURLHistory::s_RetrievePrefixUrlInfo called (%s)", pszUrl);
|
|
|
|
s_Init();
|
|
|
|
DWORD cbCEI = SIZEOF(pbuf->ab);
|
|
pbuf->pcei = &pbuf->cei;
|
|
|
|
BOOL fSuccess = GetUrlCacheEntryInfoBinary(pszUrl, pbuf->pcei, &cbCEI);
|
|
|
|
if (!fSuccess) {
|
|
pbuf->pcei = NULL;
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
|
TraceMsg(DM_TRACE, "CUH::s_RetrievePUI not enough buffer. Allocate! (%d)", cbCEI);
|
|
pbuf->pcei = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, cbCEI);
|
|
if (pbuf->pcei) {
|
|
fSuccess = GetUrlCacheEntryInfoBinary(pszUrl, pbuf->pcei, &cbCEI);
|
|
if (!fSuccess) {
|
|
TraceMsg(DM_HISTMISS, "CUH::s_Retrieve (%s) failed %x (on second attempt)",
|
|
pszUrl, GetLastError());
|
|
LocalFree(pbuf->pcei);
|
|
pbuf->pcei = NULL;
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
} else {
|
|
TraceMsg(DM_HISTMISS, "CUH::s_Retrieve (%s) failed %x (on first attempt)",
|
|
pszUrl, GetLastError());
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the total of a double-null terminated string (including the
|
|
// terminating null).
|
|
//
|
|
UINT lstrzlen(LPCTSTR pszz)
|
|
{
|
|
for (LPCTSTR psz=pszz; *psz; psz += lstrlen(psz) + 1) ;
|
|
return (unsigned int)(psz+1-pszz);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
this creates a buffer that holds a HISTDATA, and everything the HISTDATAs offsets
|
|
point to. it only sets those offsets that are passed in.
|
|
|
|
Arguments:
|
|
|
|
lpszTitle Title to place in the buffer
|
|
|
|
lpBase this is the base of the offsets in aFrags
|
|
|
|
aFrags an array of offsets to the fragments to place in the buffer
|
|
|
|
cFrags number of fragments in aFrags
|
|
|
|
lpszNewFrag this is an additional fragment to add in the new buffer
|
|
|
|
pcbHeader this is a pointer to the final size of the buffer returned
|
|
|
|
NOTE: any of the arguments except pcbHeader may be NULL.
|
|
if lpBase is NULL, then aFrags must also be NULL. this is CALLERs responsibility!
|
|
if a parameter is NULL, then it just isnt added to the buffer.
|
|
|
|
Return Value:
|
|
|
|
POINTER
|
|
Success - a valid pointer to a buffer that must be freed.
|
|
|
|
Failure - NULL. this only fails with ERROR_NOT_ENOUGH_MEMORY
|
|
|
|
NOTE: Caller must free the returned pointer. *pcbHeader only set upon successful return.
|
|
|
|
--*/
|
|
|
|
HISTDATA* CUrlHistory::s_GenerateHeaderInfo(
|
|
IN LPCTSTR pszTitle,
|
|
IN HISTDATA* phdPrev,
|
|
IN LPCTSTR pszFragment,
|
|
OUT LPDWORD pcbHeader
|
|
)
|
|
{
|
|
DWORD cbHeader = 0;
|
|
UINT cbHistExtra = 0;
|
|
HISTEXTRA* phextPrev;
|
|
|
|
// Get the size for title
|
|
UINT cchTitle = 0;
|
|
if (pszTitle[0]) {
|
|
cchTitle = lstrlen(pszTitle) + 1;
|
|
cbHistExtra += DW_ALIGNED(SIZEOF(HISTEXTRA) + (cchTitle * sizeof(TCHAR)));
|
|
|
|
if (phdPrev && (phextPrev = phdPrev->_FindExtraForSave(PID_INTSITE_TITLE))!=NULL) {
|
|
phextPrev->vtExtra = VT_EMPTY;
|
|
}
|
|
}
|
|
|
|
// Get the size of fragments
|
|
UINT cchFragsPrev = 0;
|
|
UINT cchFragment = 0;
|
|
if (pszFragment) {
|
|
cchFragment = lstrlen(pszFragment) + 2; // Double NULL terminated
|
|
if (phdPrev && (phextPrev=phdPrev->_FindExtraForSave(PID_INTSITE_FRAGMENT))!=NULL) {
|
|
cchFragsPrev = lstrzlen((LPCTSTR)phextPrev->abExtra) - 1; // lstrzlen includes both terminating nulls
|
|
// -1 since cchFragment already accounts
|
|
// for double terminating NULLs.
|
|
ASSERT(cchFragsPrev != (UINT)-1);
|
|
phextPrev->vtExtra = VT_EMPTY;
|
|
}
|
|
cbHistExtra += DW_ALIGNED(SIZEOF(HISTEXTRA) + (cchFragsPrev + cchFragment) * sizeof(TCHAR));
|
|
}
|
|
|
|
// Get the size of other extra
|
|
if (phdPrev) {
|
|
cbHistExtra += phdPrev->GetTotalExtraSize();
|
|
}
|
|
|
|
// Allocate it
|
|
CHistoryData* phdNew = CHistoryData::s_AllocateHeaderInfo(
|
|
cbHistExtra, phdPrev,
|
|
&cbHeader);
|
|
|
|
if (phdNew) {
|
|
HISTEXTRA* phext = phdNew->_GetExtra();
|
|
|
|
ASSERT( phext );
|
|
|
|
// Append title
|
|
if (pszTitle[0]) {
|
|
phext->cbExtra = DW_ALIGNED((cchTitle * sizeof(TCHAR)) + SIZEOF(HISTEXTRA));
|
|
phext->idExtra = PID_INTSITE_TITLE;
|
|
phext->vtExtra = VT_LPTSTR;
|
|
StrCpyN((LPTSTR)phext->abExtra, pszTitle, cchTitle);
|
|
phext = phext->GetNextFastForSave();
|
|
}
|
|
|
|
// Append fragment
|
|
if (pszFragment) {
|
|
// Copy pszFragment to the top.
|
|
StrCpyN((LPTSTR)phext->abExtra, pszFragment, cchFragment);
|
|
// Double NULL terminate. Note cchFragment = strlen + 2
|
|
*(((LPTSTR)phext->abExtra) + cchFragment - 1) = TEXT('\0');
|
|
|
|
// Copy existing fragments if any
|
|
if (cchFragsPrev) {
|
|
ASSERT(phdPrev);
|
|
phextPrev = phdPrev->_FindExtraForSave(PID_INTSITE_FRAGMENT);
|
|
ASSERT(phextPrev);
|
|
if (phextPrev) {
|
|
ASSERT(IS_DW_ALIGNED(phextPrev->cbExtra));
|
|
memcpy(phext->abExtra + ((cchFragment - 1) * sizeof(TCHAR)), phextPrev->abExtra,
|
|
(cchFragsPrev + 1) * sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
ASSERT(lstrzlen((LPCTSTR)phext->abExtra) == cchFragsPrev + cchFragment);
|
|
phext->cbExtra += DW_ALIGNED(SIZEOF(HISTEXTRA) + (cchFragsPrev + cchFragment) * sizeof(TCHAR));
|
|
phext->idExtra = PID_INTSITE_FRAGMENT;
|
|
phext->vtExtra = VT_NULL; // HACK (means internal)
|
|
phext = phext->GetNextFastForSave();
|
|
}
|
|
|
|
// Migrate extra data from previous one
|
|
if (phdPrev) {
|
|
phext = phdPrev->CopyExtra(phext);
|
|
}
|
|
|
|
ASSERT( phext->cbExtra == 0); // terminator
|
|
ASSERT( (LPBYTE)phdNew+cbHeader == (LPBYTE)phext+SIZEOF(DWORD) );
|
|
ASSERT( cbHistExtra == phdNew->GetTotalExtraSize() );
|
|
}
|
|
|
|
*pcbHeader = cbHeader;
|
|
|
|
TraceMsg(DM_HISTGENERATE, "CUH::s_GenerateHeader allocated %d bytes (%d extra)",
|
|
cbHeader, cbHistExtra);
|
|
|
|
return phdNew;
|
|
}
|
|
|
|
// FEATURE: Move this to UTIL.CPP
|
|
LPWSTR AllocOleStrFromTChar(LPCTSTR psz)
|
|
{
|
|
DWORD cch = lstrlen(psz) + 1;
|
|
LPWSTR pwsz = (LPWSTR)CoTaskMemAlloc(cch * SIZEOF(WCHAR));
|
|
if (pwsz) {
|
|
SHTCharToUnicode(psz, pwsz, cch);
|
|
}
|
|
return pwsz;
|
|
}
|
|
|
|
HRESULT CUrlHistory::s_GenerateSTATURL(
|
|
IN PCTSTR pszPrefixedURL,
|
|
IN LPINTERNET_CACHE_ENTRY_INFO lpCEI,
|
|
IN DWORD dwFlags,
|
|
OUT LPSTATURL lpSTATURL)
|
|
{
|
|
ASSERT(lpCEI);
|
|
ASSERT(lpSTATURL);
|
|
|
|
if (!lpCEI || !lpSTATURL)
|
|
return E_INVALIDARG;
|
|
|
|
HRESULT hr = S_OK;
|
|
LPHISTDATA phd = CHistoryData::s_GetHistoryData(lpCEI);
|
|
// I've seen lpCEI->lpszSourceUrlName be NULL with help "ms-its:" URLs.
|
|
LPCTSTR pszUrl = lpCEI->lpszSourceUrlName ? lpCEI->lpszSourceUrlName : pszPrefixedURL;
|
|
if (pszUrl && *pszUrl)
|
|
{
|
|
pszUrl += s_cchUserPrefix + CCHHISTORYPREFIX;
|
|
}
|
|
|
|
ZeroMemory(lpSTATURL, SIZEOF(STATURL));
|
|
|
|
lpSTATURL->ftLastUpdated = lpCEI->LastModifiedTime;
|
|
lpSTATURL->ftExpires = lpCEI->ExpireTime;
|
|
lpSTATURL->ftLastVisited = lpCEI->LastSyncTime;
|
|
|
|
if(dwFlags & STATURL_QUERYFLAG_ISCACHED)
|
|
{
|
|
if (pszUrl)
|
|
{
|
|
if (s_IsCached(pszUrl))
|
|
lpSTATURL->dwFlags |= STATURLFLAG_ISCACHED;
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
if (dwFlags & STATURL_QUERYFLAG_TOPLEVEL)
|
|
{
|
|
if (phd) {
|
|
if (phd->dwFlags & PIDISF_HISTORY)
|
|
{
|
|
lpSTATURL->dwFlags |= STATURLFLAG_ISTOPLEVEL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(dwFlags & STATFLAG_NONAME))
|
|
{
|
|
if (!(dwFlags & STATURL_QUERYFLAG_NOURL))
|
|
{
|
|
if (pszUrl)
|
|
{
|
|
// set the Url
|
|
lpSTATURL->pwcsUrl = AllocOleStrFromTChar(pszUrl); // This will RIP if NULL is passed.
|
|
if (lpSTATURL->pwcsUrl == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
if (!(dwFlags & STATURL_QUERYFLAG_NOTITLE))
|
|
{
|
|
// is there a title to set?
|
|
if (phd)
|
|
{
|
|
const HISTEXTRA* phextTitle = phd->_FindExtra(PID_INTSITE_TITLE);
|
|
|
|
if (phextTitle && phextTitle->vtExtra == VT_LPTSTR) {
|
|
lpSTATURL->pwcsTitle = AllocOleStrFromTChar((LPCTSTR)phextTitle->abExtra);
|
|
if (lpSTATURL->pwcsTitle == NULL) {
|
|
if (lpSTATURL->pwcsUrl)
|
|
CoTaskMemFree(lpSTATURL->pwcsUrl);
|
|
lpSTATURL->pwcsUrl = NULL;
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(SUCCEEDED(hr) || (lpSTATURL->pwcsUrl==NULL && lpSTATURL->pwcsTitle==NULL));
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Places the specified URL into the history.
|
|
|
|
If it does not exist, then it is created. If it does exist it is overwritten.
|
|
|
|
Arguments:
|
|
|
|
pwszUrl - The URL in question.
|
|
|
|
pwszTitle - pointer to the friendly title that should be associated
|
|
with this URL. If NULL, no title will be added.
|
|
|
|
dwFlags - Sets options for storage type and durability
|
|
Not implemented yet
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
Success - S_OK
|
|
|
|
Failure - E_ hresult
|
|
|
|
--*/
|
|
|
|
|
|
HRESULT CUrlHistory::AddUrl(
|
|
IN LPCWSTR pwszUrl, // Full URL to be added
|
|
IN LPCWSTR pwszTitle,
|
|
IN DWORD dwFlags // Storage options
|
|
)
|
|
{
|
|
BOOL fWriteHistory = TRUE;
|
|
|
|
if (ADDURL_ADDTOCACHE == dwFlags)
|
|
{
|
|
fWriteHistory = FALSE;
|
|
}
|
|
|
|
return AddUrlAndNotify(pwszUrl, pwszTitle, dwFlags, fWriteHistory, NULL, NULL);
|
|
}
|
|
|
|
|
|
BOOL CUrlHistory::s_CommitUrlCacheEntry(LPCTSTR pszPrefixedUrl,
|
|
LPINTERNET_CACHE_ENTRY_INFO pcei)
|
|
{
|
|
if (s_dwDaysToKeep==0) {
|
|
s_dwDaysToKeep = s_GetDaysToKeep();
|
|
}
|
|
|
|
//
|
|
// prepare the expire time
|
|
//
|
|
SYSTEMTIME st;
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st, &pcei->LastModifiedTime);
|
|
|
|
//
|
|
// Assume the normal expiration date ... we add 6 days to expiration date
|
|
// to make sure when we show the oldest week, we'll still have data for days
|
|
// that are past the s_dwDaysToKeep
|
|
//
|
|
LONGLONG llExpireHorizon = SECS_PER_DAY * (s_dwDaysToKeep + 6);
|
|
llExpireHorizon *= FILETIME_SEC;
|
|
pcei->ExpireTime.dwLowDateTime = pcei->LastModifiedTime.dwLowDateTime + (DWORD) (llExpireHorizon % 0xFFFFFFFF);
|
|
pcei->ExpireTime.dwHighDateTime = pcei->LastModifiedTime.dwHighDateTime + (DWORD) (llExpireHorizon / 0xFFFFFFFF);
|
|
|
|
//
|
|
// Check if it's subscribed
|
|
//
|
|
CHistoryData* phd = CHistoryData::s_GetHistoryData(pcei);
|
|
if (phd && phd->_FindExtra(PID_INTSITE_SUBSCRIPTION)) {
|
|
//
|
|
// It's subscribed. Keep it forever (until unsubscribed).
|
|
//
|
|
TraceMsg(DM_URLCLEANUP, "CUH::s_CommitUrlCacheEntry found subscription key %s", pszPrefixedUrl);
|
|
pcei->ExpireTime.dwLowDateTime = DW_FOREVERLOW;
|
|
pcei->ExpireTime.dwHighDateTime = DW_FOREVERHIGH;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
LPCTSTR pszTitle = TEXT("(no extra data)");
|
|
if (phd) {
|
|
const HISTEXTRA* phext = phd->_FindExtra(PID_INTSITE_TITLE);
|
|
if (phext && phext->vtExtra==VT_LPTSTR) {
|
|
pszTitle = (LPCTSTR)phext->abExtra;
|
|
} else {
|
|
pszTitle = TEXT("(no title property)");
|
|
}
|
|
|
|
TraceMsg(DM_HISTCOMMIT, "CURL::s_C calling Commit for %s with %s",
|
|
pszPrefixedUrl, pszTitle);
|
|
}
|
|
#endif
|
|
|
|
return CommitUrlCacheEntryBinary(pszPrefixedUrl,
|
|
pcei->ExpireTime,
|
|
pcei->LastModifiedTime,
|
|
pcei->CacheEntryType | URLHISTORY_CACHE_ENTRY,
|
|
(LPBYTE)pcei->lpHeaderInfo,
|
|
pcei->dwHeaderInfoSize);
|
|
}
|
|
|
|
void CUrlHistory::_WriteToHistory(LPCTSTR pszPrefixedUrl, FILETIME& ftExpires, IOleCommandTarget *poctNotify, IUnknown *punkSFHistory)
|
|
{
|
|
IHistSFPrivate *psfpHistory;
|
|
HRESULT hr = LoadHistoryShellFolder(punkSFHistory, &psfpHistory);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidlNotify = NULL;
|
|
//
|
|
// prepare the local mod time
|
|
//
|
|
SYSTEMTIME st;
|
|
GetLocalTime (&st);
|
|
|
|
FILETIME ftLocModified; // new history written in "User Perceived Time"
|
|
SystemTimeToFileTime(&st, &ftLocModified);
|
|
hr = psfpHistory->WriteHistory(pszPrefixedUrl,
|
|
ftExpires,
|
|
ftLocModified,
|
|
poctNotify ? &pidlNotify : NULL);
|
|
if (pidlNotify)
|
|
{
|
|
VARIANTARG var;
|
|
InitVariantFromIDList(&var, pidlNotify);
|
|
|
|
poctNotify->Exec(&CGID_Explorer, SBCMDID_SELECTHISTPIDL, OLECMDEXECOPT_PROMPTUSER, &var, NULL);
|
|
|
|
ILFree(pidlNotify);
|
|
VariantClear(&var);
|
|
}
|
|
psfpHistory->Release();
|
|
}
|
|
// if we made it to here, we win!
|
|
}
|
|
|
|
void CUrlHistory::s_UpdateIcon(Intshcut* pintshcut, DWORD dwFlags)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
int niIcon = 0;
|
|
UINT uFlags;
|
|
|
|
// mask off the other stuff so we get consistent results.
|
|
dwFlags &= PIDISF_RECENTLYCHANGED;
|
|
|
|
// Get the old icon location
|
|
pintshcut->GetIconLocationFromFlags(0, szPath, SIZECHARS(szPath),
|
|
&niIcon, &uFlags, dwFlags);
|
|
|
|
// property.
|
|
// int icachedImage = SHLookupIconIndex(PathFindFileName(szPath), niIcon, uFlags);
|
|
int icachedImage = Shell_GetCachedImageIndex(szPath, niIcon, uFlags);
|
|
|
|
TraceMsg(DM_HISTSPLAT, "CUH::s_UpdateIcon splat flag is changed for %s (%d)",
|
|
szPath, icachedImage);
|
|
|
|
SHUpdateImage(szPath, niIcon, uFlags, icachedImage );
|
|
}
|
|
|
|
HRESULT CUrlHistory::AddUrlAndNotify(
|
|
IN LPCWSTR pwszUrl, // Full URL to be added
|
|
IN LPCWSTR pwszTitle,
|
|
IN DWORD dwFlags, // Storage options
|
|
IN BOOL fWriteHistory, // Write History ShellFolder
|
|
IN IOleCommandTarget *poctNotify,
|
|
IN IUnknown *punkSFHistory)
|
|
{
|
|
return AddUrlAndNotifyCP(pwszUrl, pwszTitle, dwFlags, fWriteHistory,
|
|
poctNotify, punkSFHistory, NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT CUrlHistory::AddUrlAndNotifyCP(
|
|
IN LPCWSTR pwszUrl, // Full URL to be added
|
|
IN LPCWSTR pwszTitle,
|
|
IN DWORD dwFlags, // Storage options
|
|
IN BOOL fWriteHistory, // Write History ShellFolder
|
|
IN IOleCommandTarget *poctNotify,
|
|
IN IUnknown *punkSFHistory,
|
|
UINT* pcodepage)
|
|
{
|
|
if (pcodepage) {
|
|
*pcodepage = CP_ACP; // this is default.
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
LPCWSTR pwszTitleToStore = pwszTitle;
|
|
|
|
// check to make sure we got an URL
|
|
if (!pwszUrl || !pwszUrl[0])
|
|
{
|
|
TraceMsg( TF_WARNING, "CUrlHistory::AddUrlAndNotifyCP() - pwszUrl is NULL or Empty!" );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (pwszTitleToStore && 0 == StrCmpIW(pwszTitleToStore, pwszUrl))
|
|
{
|
|
// Suppress redundant title data
|
|
pwszTitleToStore = NULL;
|
|
}
|
|
|
|
CEI_PREALLOC buf;
|
|
INTERNET_CACHE_ENTRY_INFO cei = { 0 };
|
|
|
|
// Wininet URL cache only supports 8-bit ANSI, so we need to encode any characters
|
|
// which can't be converted by the system code page, in order to allow Unicode
|
|
// filenames in History. The URLs will remain in the encoded form through most of
|
|
// the History code paths, with only the display and navigation code needing to be
|
|
// aware of the UTF8
|
|
|
|
LPCTSTR pszUrlSource = pwszUrl; // points to the URL that we decide to use
|
|
|
|
TCHAR szEncodedUrl[MAX_URL_STRING];
|
|
BOOL bUsedDefaultChar;
|
|
|
|
// Find out if any of the chars will get scrambled. We can use our szEncodedUrl
|
|
// buffer to store he multibyte result because we don't actually want it
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, pwszUrl, -1,
|
|
(LPSTR) szEncodedUrl, sizeof(szEncodedUrl), NULL, &bUsedDefaultChar);
|
|
|
|
StrCpyN(szEncodedUrl, pwszUrl, ARRAYSIZE(szEncodedUrl));
|
|
SHCleanupUrlForDisplay(szEncodedUrl);
|
|
pszUrlSource = szEncodedUrl;
|
|
|
|
if (bUsedDefaultChar)
|
|
{
|
|
// one or more chars couldn't be converted, so we store the UTF8 escaped string
|
|
ConvertToUtf8Escaped(szEncodedUrl, ARRAYSIZE(szEncodedUrl));
|
|
}
|
|
|
|
s_ConvertToPrefixedUrlW(pszUrlSource, buf.szPrefixedUrl, ARRAYSIZE(buf.szPrefixedUrl), &buf.pszFragment);
|
|
s_RetrievePrefixedUrlInfo(buf.szPrefixedUrl, &buf);
|
|
|
|
LPHISTDATA phdPrev = NULL;
|
|
|
|
TCHAR szTitle[MAX_PATH];
|
|
szTitle[0] = '\0';
|
|
|
|
LPINTERNET_CACHE_ENTRY_INFO pceiUrl = NULL;
|
|
|
|
//
|
|
// if there is already an entry for this Url, then we will reuse some of the
|
|
// settings. retrieve the relevant info if possible.
|
|
//
|
|
if (buf.pcei)
|
|
{
|
|
// The existing one cannot be copied since the size may vary.
|
|
// That is, the s_RetrievePrefixedUrlInfo above may allocate a larger buffer than expected.
|
|
pceiUrl = buf.pcei;
|
|
|
|
phdPrev = CHistoryData::s_GetHistoryData( pceiUrl );
|
|
if (pwszTitle==NULL && phdPrev) {
|
|
phdPrev->_GetTitle(szTitle, ARRAYSIZE(szTitle));
|
|
}
|
|
|
|
if (pcodepage && phdPrev) {
|
|
//
|
|
// NOTES: This is the best place get the codepage stored
|
|
// in this URL history.
|
|
//
|
|
const HISTEXTRA* phextCP =phdPrev->_FindExtra(PID_INTSITE_CODEPAGE);
|
|
if (phextCP && phextCP->vtExtra == VT_UI4) {
|
|
*pcodepage = *(DWORD*)phextCP->abExtra;
|
|
TraceMsg(DM_TRACE, "CUH::AddAndNotify this URL has CP=%d",
|
|
*pcodepage);
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
pceiUrl = &cei;
|
|
|
|
cei.CacheEntryType = NORMAL_CACHE_ENTRY;
|
|
ASSERT(cei.dwHeaderInfoSize == 0);
|
|
}
|
|
|
|
ASSERT( pceiUrl );
|
|
|
|
if ( ! pceiUrl )
|
|
{
|
|
TraceMsg( TF_ERROR, "CUrlHistory::AddUrlAndNotifyCP() - pceiUrl is NULL!" );
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// search for a fragment if necessary
|
|
//
|
|
if (buf.pszFragment && phdPrev)
|
|
{
|
|
if (phdPrev->_HasFragment(buf.pszFragment)) {
|
|
buf.pszFragment = NULL;
|
|
}
|
|
}
|
|
|
|
// Override the title if specified.
|
|
if (pwszTitleToStore) {
|
|
// GetDisplayableTitle puts szTitle[0] = '\0' if it's
|
|
// not displayable with shell codepage
|
|
StrCpyN(szTitle, pwszTitleToStore, ARRAYSIZE(szTitle));
|
|
}
|
|
|
|
CHistoryData* phdNew = s_GenerateHeaderInfo(
|
|
szTitle, phdPrev, buf.pszFragment, &pceiUrl->dwHeaderInfoSize);
|
|
|
|
if (phdNew)
|
|
{
|
|
pceiUrl->lpHeaderInfo = (LPTSTR)phdNew;
|
|
|
|
//
|
|
// [alanau] Background: See IE5 Bug #110378 and related.
|
|
//
|
|
// For secure URLs (https:) that respond with a cache control header (pragma: no-cache or
|
|
// cache-control: no-cache or no-store), we presume that these can contain sensitive data that
|
|
// the site did not want retained on the client machine. Therefore, we do not write a history entry
|
|
// for such sites.
|
|
//
|
|
// Previously, a check was made in CDocObjectHost::CDOHBindStatusCallback::OnObjectAvailable(), where
|
|
// the binding was queried for INTERNET_REQFLAG_CACHE_WRITE_DISABLED. This flag would be set not only
|
|
// for https: URLs but for http: URLs that contained cache-control: no-store. With Native Frames, however,
|
|
// OnObjectAvailable() is no longer called (as Trident performs the bind), so SHDOCVW doesn't have access
|
|
// to the PIB any longer. However, all we are really interested in is whether or not the URL is in cache,
|
|
// and we have a straightforward way to check that.
|
|
//
|
|
// Note that IE 5.5 has a deliberate change of behavior to write http: (non-secure) URLs that
|
|
// contain cache-control: no-store to history.
|
|
|
|
// If caller specified to write history, and the scheme is a history-enabled scheme,
|
|
//
|
|
if (fWriteHistory && !UrlIsNoHistoryW(pwszUrl))
|
|
{
|
|
// If it's https://, we have more work to do.
|
|
//
|
|
if (URL_SCHEME_HTTPS == GetUrlScheme(pwszUrl))
|
|
{
|
|
// Check the cache. If https: and in cache, add to history.
|
|
//
|
|
if (UrlIsInCache(pwszUrl))
|
|
{
|
|
phdNew->dwFlags |= PIDISF_HISTORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not https:, so turn on history flag.
|
|
//
|
|
phdNew->dwFlags |= PIDISF_HISTORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
phdNew->dwFlags &= ~PIDISF_HISTORY; // clear the flag
|
|
}
|
|
|
|
BOOL fUpdateIcon = FALSE;
|
|
|
|
if (phdNew->dwFlags & PIDISF_RECENTLYCHANGED) {
|
|
fUpdateIcon = TRUE;
|
|
phdNew->dwFlags &= ~PIDISF_RECENTLYCHANGED;
|
|
}
|
|
|
|
if ( s_CommitUrlCacheEntry( buf.szPrefixedUrl, pceiUrl ) )
|
|
{
|
|
if (fUpdateIcon) {
|
|
TraceMsg(DM_HISTSPLAT, "CUH::AddAndNotify remove splat!");
|
|
|
|
// APPCOMPAT: This is a temporary hack to make splat update
|
|
// work as bad as previously.
|
|
Intshcut* pintshcut = new Intshcut();
|
|
if (pintshcut) {
|
|
pintshcut->SetURL(pwszUrl ,0);
|
|
s_UpdateIcon(pintshcut, PIDISF_RECENTLYCHANGED);
|
|
pintshcut->Release();
|
|
}
|
|
}
|
|
|
|
//
|
|
// When we have successfully updated the global history and
|
|
// we have updated something in the HISTDATA, update the
|
|
// date-based history as well.
|
|
//
|
|
//
|
|
// Cache IShellFolder for the history folder if we don't have
|
|
// it yet.
|
|
//
|
|
// Use the previously set PIDISF_HISTORY flag to decide whether or not to write history here too.
|
|
if (phdNew->dwFlags & PIDISF_HISTORY)
|
|
{
|
|
_WriteToHistory( buf.szPrefixedUrl, pceiUrl->ExpireTime, poctNotify, punkSFHistory );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError ());
|
|
}
|
|
|
|
LocalFree(phdNew);
|
|
phdNew = NULL;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (g_dwPrototype & 0x00000020) {
|
|
TCHAR szUrl[MAX_URL_STRING];
|
|
SHUnicodeToTChar(pwszUrl, szUrl, ARRAYSIZE(szUrl));
|
|
PROPVARIANT var = { 0 };
|
|
HRESULT hrT = GetProperty(szUrl, PID_INTSITE_SUBSCRIPTION, &var);
|
|
if (SUCCEEDED(hrT)) {
|
|
TraceMsg(DM_TRACE, "CUH::AddAndNotify got property vt=%d lVal=%x",
|
|
var.vt, var.lVal);
|
|
PropVariantClear(&var);
|
|
} else {
|
|
TraceMsg(DM_TRACE, "CUH::AddAndNotify failed to get property (%x)", hrT);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CUrlHistory::QueryUrl(
|
|
IN LPCWSTR pwszUrl,
|
|
IN DWORD dwFlags,
|
|
OUT LPSTATURL lpSTATURL
|
|
)
|
|
{
|
|
if (!pwszUrl || !pwszUrl[0])
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (lpSTATURL)
|
|
{
|
|
lpSTATURL->pwcsUrl = NULL;
|
|
lpSTATURL->pwcsTitle = NULL;
|
|
}
|
|
|
|
LPCTSTR pszFragment;
|
|
TCHAR szPrefixedUrl[MAX_URL_STRING];
|
|
|
|
s_ConvertToPrefixedUrlW(pwszUrl, szPrefixedUrl, ARRAYSIZE(szPrefixedUrl), &pszFragment);
|
|
return s_QueryUrlCommon(szPrefixedUrl, pszFragment, dwFlags, lpSTATURL);
|
|
}
|
|
|
|
HRESULT CUrlHistory::s_QueryUrlCommon(
|
|
IN LPCTSTR lpszPrefixedUrl,
|
|
LPCTSTR lpszFragment,
|
|
IN DWORD dwFlags,
|
|
OUT LPSTATURL lpSTATURL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks to see if Url is a valid History item
|
|
|
|
Arguments:
|
|
|
|
pwszUrl - The URL in question.
|
|
|
|
dwFlags - Flags on the query
|
|
|
|
lpSTATURL - points to a STATURL storage structure
|
|
If this is NULL, then a S_OK means the URL was found.
|
|
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
Success - S_OK, Item found and STATURL filled
|
|
|
|
Failure - valid E_ code
|
|
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) indicates the URL is not available
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
LPHISTDATA phd = NULL;
|
|
CEI_PREALLOC buf;
|
|
|
|
//
|
|
// if there is no data required, and there are no fragments
|
|
// we dont need to get a copy of the CEI
|
|
//
|
|
if(!lpSTATURL && !lpszFragment)
|
|
{
|
|
if(s_IsCached(lpszPrefixedUrl))
|
|
hr = S_OK;
|
|
else
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
|
|
goto quit;
|
|
}
|
|
|
|
s_RetrievePrefixedUrlInfo(lpszPrefixedUrl, &buf);
|
|
if (buf.pcei)
|
|
{
|
|
DEBUG_CODE(DWORD cbNHI = buf.pcei->dwHeaderInfoSize;)
|
|
phd = CHistoryData::s_GetHistoryData(buf.pcei);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError ());
|
|
goto quit;
|
|
}
|
|
|
|
|
|
//
|
|
// Need to check for local anchor fragments
|
|
//
|
|
if (lpszFragment)
|
|
{
|
|
if (phd && phd->_HasFragment(lpszFragment))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
} else {
|
|
hr = S_OK;
|
|
}
|
|
|
|
// check to see if we should fill the STATURL
|
|
if (S_OK == hr && lpSTATURL) {
|
|
hr = s_GenerateSTATURL(lpszPrefixedUrl, buf.pcei, dwFlags, lpSTATURL);
|
|
}
|
|
|
|
quit:
|
|
|
|
if (S_OK != hr && lpSTATURL)
|
|
{
|
|
if (lpSTATURL->pwcsUrl)
|
|
{
|
|
LocalFree(lpSTATURL->pwcsUrl);
|
|
lpSTATURL->pwcsUrl = NULL;
|
|
}
|
|
|
|
if (lpSTATURL->pwcsTitle)
|
|
{
|
|
LocalFree(lpSTATURL->pwcsTitle);
|
|
lpSTATURL->pwcsTitle = NULL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CUrlHistory::QueryUrlA(LPCSTR pszUrl, DWORD dwFlags, LPSTATURL lpSTATURL)
|
|
{
|
|
TCHAR szPrefixedUrl[MAX_URL_STRING];
|
|
LPCTSTR lpszFragment = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!pszUrl || !pszUrl[0]) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (lpSTATURL)
|
|
{
|
|
lpSTATURL->pwcsUrl = NULL;
|
|
lpSTATURL->pwcsTitle = NULL;
|
|
}
|
|
|
|
TCHAR szUrl[MAX_URL_STRING];
|
|
|
|
SHAnsiToUnicode(pszUrl, szUrl, ARRAYSIZE(szUrl));
|
|
CUrlHistory::s_ConvertToPrefixedUrlW(szUrl, szPrefixedUrl, ARRAYSIZE(szPrefixedUrl), &lpszFragment);
|
|
|
|
return CUrlHistory::s_QueryUrlCommon(szPrefixedUrl, lpszFragment, dwFlags, lpSTATURL);
|
|
}
|
|
|
|
HRESULT CUrlHistory::s_DeleteUrl(LPCWSTR pwszUrl, DWORD dwFlags)
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
TCHAR szPrefixedUrl[MAX_URL_STRING];
|
|
LPCTSTR lpszFragment;
|
|
BOOL fDoDelete = TRUE;
|
|
|
|
if (!pwszUrl || !pwszUrl[0]) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
s_ConvertToPrefixedUrlW(pwszUrl, szPrefixedUrl, ARRAYSIZE(szPrefixedUrl), &lpszFragment);
|
|
|
|
// don't delete it if its not a subscription
|
|
if (dwFlags & URLFLAG_DONT_DELETE_SUBSCRIBED) {
|
|
CEI_PREALLOC buf;
|
|
// query to find out if its a subscription
|
|
s_RetrievePrefixedUrlInfo(szPrefixedUrl, &buf);
|
|
if (buf.pcei &&
|
|
// Hack alert (chrisfra) avoid deleting subscriptions, etc!
|
|
((buf.pcei)->ExpireTime.dwLowDateTime == DW_FOREVERLOW) &&
|
|
((buf.pcei)->ExpireTime.dwHighDateTime == DW_FOREVERHIGH))
|
|
{
|
|
fDoDelete = FALSE;
|
|
// re-write it as a non-history item and just a subscription
|
|
CHistoryData *phdPrev = CHistoryData::s_GetHistoryData(buf.pcei);
|
|
if (phdPrev) // offset into pcei structure
|
|
{
|
|
phdPrev->dwFlags &= ~PIDISF_HISTORY;
|
|
s_CommitUrlCacheEntry(szPrefixedUrl, buf.pcei);
|
|
}
|
|
else {
|
|
// I'd rather return ERROR_OUT_OF_PAPER...
|
|
Error = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fDoDelete) {
|
|
if(!::DeleteUrlCacheEntry(szPrefixedUrl))
|
|
Error = GetLastError();
|
|
}
|
|
|
|
return HRESULT_FROM_WIN32(Error);
|
|
}
|
|
|
|
HRESULT CUrlHistory::DeleteUrl(LPCWSTR pwszUrl, DWORD dwFlags)
|
|
{
|
|
return s_DeleteUrl(pwszUrl, dwFlags);
|
|
}
|
|
|
|
HRESULT CUrlHistory::BindToObject (LPCWSTR pwszUrl, REFIID riid, void **ppvOut)
|
|
{
|
|
*ppvOut = NULL;
|
|
return E_NOTIMPL;
|
|
#if 0
|
|
HRESULT hr;
|
|
|
|
if(!ppvOut || !pwszUrl || !*pwszUrl)
|
|
return E_INVALIDARG;
|
|
|
|
*ppvOut = NULL;
|
|
|
|
CUrlHObj *pcuho = new CUrlHObj (this);
|
|
if (!pcuho)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = pcuho->QueryInterface(riid, ppvOut);
|
|
pcuho->Release();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pcuho->Init(pwszUrl);
|
|
if (FAILED(hr))
|
|
{
|
|
pcuho->Release();
|
|
*ppvOut = NULL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
#endif
|
|
}
|
|
|
|
HRESULT CUrlHistory::s_EnumUrls(IEnumSTATURL **ppEnum)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
*ppEnum = NULL;
|
|
CEnumSTATURL *penum = new CEnumSTATURL();
|
|
if (penum)
|
|
{
|
|
*ppEnum = (IEnumSTATURL *)penum;
|
|
hres = S_OK;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CUrlHistory::EnumUrls(IEnumSTATURL **ppEnum)
|
|
{
|
|
return s_EnumUrls(ppEnum);
|
|
}
|
|
|
|
HRESULT CUrlHistory::GetProperty(LPCTSTR pszURL, PROPID pid, PROPVARIANT* pvarOut)
|
|
{
|
|
HRESULT hres = E_FAIL; // assume error
|
|
PropVariantInit(pvarOut);
|
|
|
|
CEI_PREALLOC buf;
|
|
CUrlHistory::s_ConvertToPrefixedUrlW(pszURL, buf.szPrefixedUrl, ARRAYSIZE(buf.szPrefixedUrl), &buf.pszFragment);
|
|
CUrlHistory::s_RetrievePrefixedUrlInfo(buf.szPrefixedUrl, &buf);
|
|
if (buf.pcei) {
|
|
CHistoryData* phdPrev = CHistoryData::s_GetHistoryData(buf.pcei);
|
|
if (phdPrev) {
|
|
const HISTEXTRA* phextPrev;
|
|
|
|
switch(pid) {
|
|
case PID_INTSITE_FLAGS:
|
|
pvarOut->vt = VT_UI4;
|
|
pvarOut->lVal = phdPrev->dwFlags;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case PID_INTSITE_LASTVISIT:
|
|
pvarOut->vt = VT_FILETIME;
|
|
pvarOut->filetime = buf.pcei->LastAccessTime;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case PID_INTSITE_LASTMOD:
|
|
pvarOut->vt = VT_FILETIME;
|
|
pvarOut->filetime = buf.pcei->LastModifiedTime;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case PID_INTSITE_WATCH:
|
|
pvarOut->vt = VT_UI4;
|
|
pvarOut->lVal = phdPrev->dwWatch;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case PID_INTSITE_VISITCOUNT:
|
|
pvarOut->vt = VT_UI4;
|
|
pvarOut->lVal = buf.pcei->dwHitRate;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
default:
|
|
phextPrev = phdPrev->_FindExtra(pid);
|
|
LPCWSTR pwsz;
|
|
|
|
if (phextPrev) {
|
|
WCHAR wszBuf[MAX_URL_STRING];
|
|
|
|
switch(phextPrev->vtExtra) {
|
|
case VT_UI4:
|
|
case VT_I4:
|
|
pvarOut->vt = phextPrev->vtExtra;
|
|
pvarOut->lVal = *(DWORD*)phextPrev->abExtra;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case VT_LPSTR:
|
|
AnsiToUnicode((LPCSTR)phextPrev->abExtra, wszBuf, ARRAYSIZE(wszBuf));
|
|
pwsz = wszBuf;
|
|
goto Return_LPWSTR;
|
|
|
|
case VT_LPWSTR:
|
|
pwsz = (LPWSTR)phextPrev->abExtra;
|
|
Return_LPWSTR:
|
|
int cch = lstrlenW(pwsz)+1;
|
|
pvarOut->pwszVal = (LPWSTR)CoTaskMemAlloc(cch * SIZEOF(WCHAR));
|
|
if (pvarOut->pwszVal) {
|
|
StrCpyNW(pvarOut->pwszVal, pwsz, cch);
|
|
pvarOut->vt = VT_LPWSTR;
|
|
hres = S_OK;
|
|
} else {
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// IEnumSTATURL methods
|
|
//
|
|
CEnumSTATURL::~CEnumSTATURL()
|
|
{
|
|
if(m_lpCEI)
|
|
{
|
|
LocalFree(m_lpCEI);
|
|
m_lpCEI = NULL;
|
|
}
|
|
|
|
if(m_hEnum)
|
|
FindCloseUrlCache(m_hEnum);
|
|
|
|
return;
|
|
}
|
|
|
|
HRESULT CEnumSTATURL::QueryInterface(REFIID riid, PVOID *ppvObj)
|
|
{
|
|
if (!ppvObj)
|
|
return E_INVALIDARG;
|
|
|
|
*ppvObj = NULL;
|
|
|
|
if (IsEqualIID(IID_IUnknown, riid))
|
|
{
|
|
*ppvObj = (IUnknown *) this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else if (IsEqualIID(IID_IEnumSTATURL, riid))
|
|
{
|
|
*ppvObj = (IEnumSTATURL *) this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
ULONG CEnumSTATURL::AddRef(void)
|
|
{
|
|
_cRef++;
|
|
|
|
return _cRef;
|
|
}
|
|
|
|
|
|
ULONG CEnumSTATURL::Release(void)
|
|
{
|
|
_cRef--;
|
|
|
|
if (!_cRef)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return _cRef;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CEnumSTATURL::RetrieveFirstUrlInfo()
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ASSERT(!m_lpCEI);
|
|
|
|
m_cbCEI = DEFAULT_CEI_BUFFER_SIZE;
|
|
m_lpCEI = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, DEFAULT_CEI_BUFFER_SIZE);
|
|
if (!m_lpCEI)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
m_hEnum = FindFirstUrlCacheEntryBinary(_szPrefixedUrl,
|
|
m_lpCEI,
|
|
&m_cbCEI);
|
|
|
|
if (!m_hEnum)
|
|
{
|
|
DWORD Error = GetLastError ();
|
|
|
|
LocalFree(m_lpCEI);
|
|
m_lpCEI = NULL;
|
|
|
|
if (Error == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
m_lpCEI = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, m_cbCEI);
|
|
if (!m_lpCEI)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ERROR_NO_MORE_ITEMS == Error)
|
|
hr = S_FALSE;
|
|
else
|
|
hr = HRESULT_FROM_WIN32(Error);
|
|
break;
|
|
}
|
|
}
|
|
else break;
|
|
}
|
|
|
|
quit:
|
|
|
|
m_cbCEI = (DWORD)max(m_cbCEI, DEFAULT_CEI_BUFFER_SIZE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// This function should not becaused if the previous call failed
|
|
// and ::Reset() was never called.
|
|
HRESULT CEnumSTATURL::RetrieveNextUrlInfo()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL ok;
|
|
|
|
ASSERT(m_hEnum);
|
|
|
|
while (TRUE)
|
|
{
|
|
|
|
ok = FindNextUrlCacheEntryBinary(m_hEnum,
|
|
m_lpCEI,
|
|
&m_cbCEI);
|
|
|
|
if (!ok)
|
|
{
|
|
DWORD Error = GetLastError ();
|
|
|
|
if (m_lpCEI)
|
|
{
|
|
LocalFree(m_lpCEI);
|
|
m_lpCEI = NULL;
|
|
}
|
|
|
|
if (Error == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
m_lpCEI = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, m_cbCEI);
|
|
if (!m_lpCEI)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ERROR_NO_MORE_ITEMS == Error)
|
|
hr = S_FALSE;
|
|
else
|
|
hr = HRESULT_FROM_WIN32(Error);
|
|
break;
|
|
}
|
|
}
|
|
else break;
|
|
}
|
|
|
|
m_cbCEI = (DWORD)max(m_cbCEI, DEFAULT_CEI_BUFFER_SIZE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT CEnumSTATURL::Next(ULONG celt, LPSTATURL rgelt, ULONG * pceltFetched)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches through the History looking for URLs that match the search pattern,
|
|
and copies the STATURL into the buffer.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL found = FALSE;
|
|
LPHISTDATA phd = NULL;
|
|
|
|
if(pceltFetched)
|
|
*pceltFetched = 0;
|
|
|
|
if(!celt)
|
|
goto quit;
|
|
|
|
if (!m_hEnum)
|
|
{
|
|
//must handle new enumerator
|
|
CUrlHistory::s_ConvertToPrefixedUrlW(m_poszFilter, _szPrefixedUrl, ARRAYSIZE(_szPrefixedUrl), &m_lpszFragment);
|
|
|
|
//loop until we get our first handle or bag out
|
|
hr = RetrieveFirstUrlInfo();
|
|
if (S_OK != hr || !m_lpCEI)
|
|
goto quit;
|
|
|
|
m_cchPrefixedUrl = lstrlen(_szPrefixedUrl);
|
|
|
|
while(StrCmpN(_szPrefixedUrl, m_lpCEI->lpszSourceUrlName, m_cchPrefixedUrl))
|
|
{
|
|
hr = RetrieveNextUrlInfo();
|
|
if(S_OK != hr || !m_lpCEI)
|
|
goto quit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
hr = RetrieveNextUrlInfo();
|
|
if (S_OK != hr || !m_lpCEI)
|
|
goto quit;
|
|
|
|
} while(StrCmpN(_szPrefixedUrl, m_lpCEI->lpszSourceUrlName, m_cchPrefixedUrl));
|
|
}
|
|
|
|
hr = CUrlHistory::s_GenerateSTATURL(NULL, m_lpCEI, m_dwFilter, rgelt);
|
|
|
|
if(SUCCEEDED(hr) && pceltFetched)
|
|
(*pceltFetched)++;
|
|
|
|
|
|
|
|
quit:
|
|
if (pceltFetched) {
|
|
ASSERT((0 == *pceltFetched && (S_FALSE == hr || FAILED(hr))) ||
|
|
(*pceltFetched && S_OK == hr));
|
|
}
|
|
|
|
return hr;
|
|
|
|
|
|
}
|
|
|
|
HISTEXTRA* CHistoryData::CopyExtra(HISTEXTRA* phextCur) const
|
|
{
|
|
const HISTEXTRA* phext;
|
|
for (phext = _GetExtra();
|
|
phext && !phext->IsTerminator();
|
|
phext = phext->GetNextFast())
|
|
{
|
|
if (phext->vtExtra != VT_EMPTY) {
|
|
TraceMsg(DM_HISTEXTRA, "CHD::CopyExtra copying vt=%d id=%d %d bytes",
|
|
phext->vtExtra, phext->idExtra, phext->cbExtra);
|
|
memcpy(phextCur, phext, phext->cbExtra);
|
|
phextCur = phextCur->GetNextFastForSave();
|
|
} else {
|
|
TraceMsg(DM_HISTEXTRA, "CHD::CopyExtra skipping vt=%d id=%d %d bytes",
|
|
phext->vtExtra, phext->idExtra, phext->cbExtra);
|
|
}
|
|
}
|
|
|
|
return phextCur;
|
|
}
|
|
|
|
CHistoryData* CHistoryData::s_AllocateHeaderInfo(UINT cbExtra, const HISTDATA* phdPrev, ULONG* pcbTotal)
|
|
{
|
|
DWORD cbTotal = SIZEOF(HISTDATA) + SIZEOF(DWORD) + cbExtra;
|
|
|
|
LPHISTDATA phdNew = (LPHISTDATA)LocalAlloc(LPTR, cbTotal);
|
|
if (phdNew) {
|
|
if (phdPrev) {
|
|
*phdNew = *phdPrev; // Copy all the field
|
|
}
|
|
phdNew->cbSize = SIZEOF(HISTDATA);
|
|
phdNew->cbVer = HISTDATA_VER;
|
|
*pcbTotal = cbTotal;
|
|
}
|
|
|
|
return phdNew;
|
|
}
|
|
|
|
//
|
|
// Returns the total size of extra data (exclude VT_EMPTY)
|
|
//
|
|
UINT CHistoryData::GetTotalExtraSize() const
|
|
{
|
|
const HISTEXTRA* phext;
|
|
UINT cbTotal = 0;
|
|
for (phext = _GetExtra();
|
|
phext && !phext->IsTerminator();
|
|
phext = phext->GetNextFast())
|
|
{
|
|
if (phext->vtExtra != VT_EMPTY) {
|
|
cbTotal += phext->cbExtra;
|
|
}
|
|
}
|
|
|
|
return cbTotal;
|
|
}
|
|
|
|
HRESULT CEnumSTATURL::Skip(ULONG celt)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CEnumSTATURL::Reset(void)
|
|
{
|
|
if(m_hEnum)
|
|
{
|
|
FindCloseUrlCache(m_hEnum);
|
|
m_hEnum = NULL;
|
|
}
|
|
|
|
if(m_poszFilter)
|
|
{
|
|
LocalFree(m_poszFilter);
|
|
m_poszFilter = NULL;
|
|
}
|
|
|
|
if(m_lpCEI)
|
|
{
|
|
LocalFree(m_lpCEI);
|
|
m_lpCEI = NULL;
|
|
}
|
|
|
|
m_dwFilter = 0;
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CEnumSTATURL::Clone(IEnumSTATURL ** ppenum)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// IEnumSTATURL methods
|
|
|
|
HRESULT CEnumSTATURL::SetFilter(LPCWSTR poszFilter, DWORD dwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if(poszFilter)
|
|
{
|
|
m_poszFilter = StrDupW(poszFilter);
|
|
if (!m_poszFilter)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
}
|
|
|
|
m_dwFilter = dwFlags;
|
|
|
|
quit:
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
const HISTEXTRA * CHistoryData::_FindExtra(UINT idExtra) const
|
|
{
|
|
for (const HISTEXTRA* phext = _GetExtra();
|
|
phext && !phext->IsTerminator();
|
|
phext = phext->GetNextFastForSave())
|
|
{
|
|
if (phext->idExtra == idExtra) {
|
|
return phext;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CHistoryData* CHistoryData::s_GetHistoryData(LPINTERNET_CACHE_ENTRY_INFO lpCEI)
|
|
{
|
|
CHistoryData* phd = (CHistoryData*)lpCEI->lpHeaderInfo;
|
|
if (phd && phd->_IsOldHistory()) {
|
|
TraceMsg(DM_TRACE, "CHistoryData::GetHistoryData found old header. Ignore");
|
|
phd = NULL;
|
|
}
|
|
|
|
if (phd && phd->cbVer != HISTDATA_VER) {
|
|
TraceMsg(DM_TRACE, "CHistoryData::GetHistoryData found old header (%d). Ignore",
|
|
phd->cbVer);
|
|
phd = NULL;
|
|
}
|
|
|
|
return phd;
|
|
}
|
|
|
|
BOOL CHistoryData::_HasFragment(LPCTSTR pszFragment) const
|
|
{
|
|
BOOL fHas = FALSE;
|
|
const HISTEXTRA* phext = _FindExtra(PID_INTSITE_FRAGMENT);
|
|
|
|
if (phext) {
|
|
for (LPCTSTR psz=(LPCTSTR)(phext->abExtra); *psz ; psz += lstrlen(psz)+1) {
|
|
if (StrCmp(psz, pszFragment)==0) {
|
|
fHas = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fHas;
|
|
}
|
|
|
|
void CHistoryData::_GetTitle(LPTSTR szTitle, UINT cchMax) const
|
|
{
|
|
szTitle[0] = '\0';
|
|
const HISTEXTRA* phext = _FindExtra(PID_INTSITE_TITLE);
|
|
if (phext && phext->vtExtra == VT_LPSTR) {
|
|
StrCpyN(szTitle, (LPCTSTR)phext->abExtra, cchMax);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef USE_NEW_HISTORYDATA
|
|
#include "urlprop2.cpp"
|
|
#endif // USE_NEW_HISTORYDATA
|
|
|