windows-nt/Source/XPSP1/NT/net/tapi/skywalker/inc/objectwithsite.h
2020-09-26 16:20:57 +08:00

1032 lines
20 KiB
C++

#ifndef _TAPI_OBJECT_WITH_SITE_H_
#define _TAPI_OBJECT_WITH_SITE_H_
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
ObjectWithSite.h
Abstract:
The implementation of IObjectWithSite interface that allows
for per-page persistent data to be stored in registry or as
a cookie.
--*/
#include <Mshtml.h>
#include <Wininet.h>
//
// this url is used to construct the URL for cookies -- a security measure
// so a script applet cannot drop a cookie with the same name and data
// and fool us into thinking it is our cookie
//
static const TCHAR gszHardCodedURL[] = _T("http://www.microsoft.com/");
//
// the expiration date is needed to make the cookie persistent
//
static const TCHAR gszCookieData[] =
_T("6; expires = Sat, 12-Sep-2099 00:00:00 GMT");
//
// dummy suffix to be appended to the url string
//
static const TCHAR gszURLSuffix[] =
_T("/url");
class CObjectWithSite : public IObjectWithSite
{
public:
//
// current validation level. used to determine whether the page is safe,
// unsafe, or whether information from the user is needed
//
enum EnValidation { VALIDATED_SAFE, VALIDATED_SAFE_PERMANENT, VALIDATED_UNSAFE, UNVALIDATED };
public:
//
// store type
//
enum EnMechanism { COOKIES, REGISTRY };
CObjectWithSite(TCHAR const *pszStorageName)
:m_pszURL(NULL),
m_dwSecurityZone(URLZONE_UNTRUSTED),
m_pUnkSite(NULL),
m_pszStorageName(NULL)
{
SetStorageName(pszStorageName);
}
~CObjectWithSite()
{
if (m_pszURL)
{
delete m_pszURL;
m_pszURL = NULL;
}
if (m_pUnkSite)
{
m_pUnkSite->Release();
m_pUnkSite = NULL;
}
if (m_pszStorageName)
{
delete m_pszStorageName;
m_pszStorageName = NULL;
}
}
////////////////////////////
//
// IObjectWithSite methods
STDMETHOD(SetSite)(IUnknown *pUnkSite)
{
if ((NULL != pUnkSite) && IsBadCodePtr((FARPROC)pUnkSite))
{
return E_POINTER;
}
s_ObjectWithSiteCritSection.Lock();
//
// we are moving away from a page. this is the new page, as far as
// validation logic is concerned, so invalidate the current page
//
if (NULL == pUnkSite)
{
Validate(UNVALIDATED);
}
//
// Get URL and zone information for this site
//
//
// Note: we could delay this until we are actually asked for
// zone or URL, but this should not be a performance bottlneck
// in our case, so do this now to keep the code simple.
StoreURLAndZone(pUnkSite);
//
// replace the current site pointer with the new one
//
if (m_pUnkSite)
{
m_pUnkSite->Release();
}
m_pUnkSite = pUnkSite;
if (m_pUnkSite)
{
m_pUnkSite->AddRef();
}
s_ObjectWithSiteCritSection.Unlock();
return S_OK;
}
STDMETHOD(GetSite)(REFIID riid, void **ppSite)
{
HRESULT hr = E_POINTER;
if (!IsBadWritePtr(ppSite, sizeof(void*)))
{
s_ObjectWithSiteCritSection.Lock();
*ppSite = NULL;
if (m_pUnkSite)
{
hr = m_pUnkSite->QueryInterface(riid, ppSite);
}
else
{
hr = E_FAIL;
}
s_ObjectWithSiteCritSection.Unlock();
}
return hr;
}
//
// has this page been validated?
//
EnValidation GetValidation()
{
//
// if the page has not been validated, see if it is marked as safe
//
s_ObjectWithSiteCritSection.Lock();
if (UNVALIDATED == s_enValidation)
{
if (IsPageSafe())
{
s_enValidation = VALIDATED_SAFE;
}
}
EnValidation enValidation = s_enValidation;
s_ObjectWithSiteCritSection.Unlock();
return enValidation;
}
//
// validate page as safe, unsafe, or reset validation
//
EnValidation Validate(EnValidation enNewValidation)
{
s_ObjectWithSiteCritSection.Lock();
//
// keep the validation before the change
//
EnValidation enOldValidation = s_enValidation;
//
// safe permanent is a special case:
//
if (VALIDATED_SAFE_PERMANENT == enNewValidation)
{
//
// set persistent safety flag and
// validate page as safe
//
MarkPageAsSafe();
enNewValidation = VALIDATED_SAFE;
}
//
// change our validation level for this page
//
s_enValidation = enNewValidation;
s_ObjectWithSiteCritSection.Unlock();
return enOldValidation;
}
BOOL IsIntranet()
{
//
// if anything other that intranet assume internet -- a less secure zone
//
s_ObjectWithSiteCritSection.Lock();
BOOL bIntranet = ( m_dwSecurityZone == URLZONE_INTRANET );
s_ObjectWithSiteCritSection.Unlock();
return bIntranet;
}
////////////////////
//
// HaveSite()
//
// return true if we have a site pointer
//
BOOL HaveSite()
{
s_ObjectWithSiteCritSection.Lock();
BOOL bHaveSite = FALSE;
if (NULL != m_pUnkSite)
{
bHaveSite = TRUE;
}
s_ObjectWithSiteCritSection.Unlock();
return bHaveSite;
}
private:
////////////////////////////
//
// store the current url in the "safe" list
//
//
// not thread safe, called from inside a lock
//
HRESULT MarkPageAsSafe(EnMechanism enMechanism = COOKIES)
{
//
// if storage is invalid, the object has not been properly initialized
//
if (IsBadStringPtr(m_pszStorageName, -1))
{
return E_UNEXPECTED;
}
//
// is we don't have the url, can't do what we are asked
//
if (NULL == m_pszURL)
{
return S_FALSE;
}
//
// if url is garbage, we have a problem
//
if ( IsBadStringPtr(m_pszURL, -1) )
{
return E_FAIL;
}
HRESULT hr = E_FAIL;
switch (enMechanism)
{
case REGISTRY:
hr = MarkPageSafeInRegistry(m_pszStorageName);
break;
case COOKIES:
hr = MarkPageSafeCookie(m_pszStorageName);
break;
default:
break;
}
return hr;
}
//
// Returns TRUE if the current page is in the safe list
//
//
// not thread safe, called from inside a lock
//
BOOL IsPageSafe( EnMechanism enMechanism = COOKIES )
{
//
// if we cannot get safety marking for whatever reason,
// return false
//
_ASSERTE(NULL != m_pszStorageName);
if ( IsBadStringPtr(m_pszURL, -1) ||
IsBadStringPtr(m_pszStorageName, -1))
{
return FALSE;
}
BOOL bSafe = FALSE;
switch (enMechanism)
{
case REGISTRY:
bSafe = IsPageSafeRegistry(m_pszStorageName);
break;
case COOKIES:
bSafe = IsPageSafeCookie(m_pszStorageName);
break;
default:
break;
}
return bSafe;
}
private:
//
// this method is only called from the constructor. not thread safe.
//
HRESULT SetStorageName(TCHAR const *pszStorageName)
{
//
// calling this method invalidates the old storage name
// so deallocate it before doing anything else
//
if (NULL != m_pszStorageName)
{
delete m_pszStorageName;
m_pszStorageName = NULL;
}
//
// argument must be valid
//
if (IsBadStringPtr(pszStorageName, -1))
{
return E_POINTER;
}
//
// allocate buffer for the new storage name
//
size_t nSize = _tcsclen(pszStorageName) + 1;
m_pszStorageName = new TCHAR[nSize];
if (NULL == m_pszStorageName)
{
return E_OUTOFMEMORY;
}
_tcscpy(m_pszStorageName, pszStorageName);
return S_OK;
}
//
// cache the url string and security zone id
// not thread safe must be called from inside a lock
//
HRESULT StoreURLAndZone(IUnknown *pUnkSite)
{
//
// reset zone and deallocate URL, if it exists
//
m_dwSecurityZone = URLZONE_UNTRUSTED;
if (m_pszURL)
{
delete m_pszURL;
m_pszURL = NULL;
}
if (pUnkSite == NULL)
{
return S_OK;
}
//
// use pUnkSite to get to IHTMLDocument2, which will give us the URL
//
//
// these interfaces need to be released on exit.
// smart pointers will do exactly what we need
//
HRESULT hr = E_FAIL;
CComPtr<IOleClientSite> pSite;
if (FAILED(hr = pUnkSite->QueryInterface(IID_IOleClientSite, (LPVOID *) &pSite)))
{
return hr;
}
CComPtr<IOleContainer> pOleCtr;
if (FAILED(hr = pSite->GetContainer(&pOleCtr)))
{
return hr;
}
CComPtr<IHTMLDocument2> pDoc;
if (FAILED(hr = pOleCtr->QueryInterface(IID_IHTMLDocument2, (LPVOID *) &pDoc)))
{
return hr;
}
//
// get and keep the url
//
BSTR bstrURL;
if (FAILED(hr = pDoc->get_URL(&bstrURL)))
{
return hr;
}
UINT nURLLength = SysStringLen(bstrURL) + 1;
_ASSERTE(NULL == m_pszURL);
m_pszURL = new TCHAR[nURLLength];
if (NULL == m_pszURL)
{
SysFreeString(bstrURL);
return E_OUTOFMEMORY;
}
#ifdef _UNICODE
_tcscpy(m_pszURL, bstrURL);
#else
int r = WideCharToMultiByte(
CP_ACP,
0,
bstrURL,
nURLLength,
m_pszURL,
nURLLength,
NULL,
NULL );
if (0 == r)
{
SysFreeString(bstrURL);
delete m_pszURL;
m_pszURL = NULL;
return E_FAIL;
}
#endif
//
// whatever follows '#' and '?' is "extra info" and is not considered
// to be a part of the actual URL by Internet(Set/Get)Coookie. Extra
// Info has no value for us -- so throw it out
//
TCHAR *psDelimiter = _tcsstr(m_pszURL, _T("#"));
if (NULL != psDelimiter)
{
*psDelimiter = _T('\0');
}
psDelimiter = _tcsstr(m_pszURL, _T("?"));
if (NULL != psDelimiter)
{
*psDelimiter = _T('\0');
}
//
// at this point we cached the URL
// now attempt to get the security zone. if we fail getting zone
// information still keep the url.
//
//
// Get security zone
//
CComPtr<IInternetSecurityManager> pSecMgr;
hr = CoCreateInstance(CLSID_InternetSecurityManager,
NULL,
CLSCTX_INPROC_SERVER,
IID_IInternetSecurityManager,
(LPVOID *) &pSecMgr);
if (pSecMgr == NULL)
{
SysFreeString(bstrURL);
return hr;
}
hr = pSecMgr->MapUrlToZone(bstrURL, &m_dwSecurityZone, 0);
//
// if failed, reset url to untrusted, just in case
//
if ( FAILED(hr) )
{
m_dwSecurityZone = URLZONE_UNTRUSTED;
}
SysFreeString(bstrURL);
//
// we should have at least the URL at this point
//
return S_OK;
}
//
// drop a cookie for this page as an indicator that this page is safe
//
HRESULT MarkPageSafeCookie(TCHAR const *pszCookieName)
{
TCHAR *pszURL = NULL;
//
// generate the url for the cookie
// remember to delete the returned string
//
GenerateURLString(&pszURL);
if (NULL == pszURL)
return E_OUTOFMEMORY;
BOOL bReturn = InternetSetCookie(pszURL, pszCookieName, gszCookieData);
delete pszURL;
return (bReturn)?S_OK:E_FAIL;
}
//
// presence of a cookie for this page is an indicator that it's safe
// returns TRUE if the cookie exists. FALSE otherwise
//
BOOL IsPageSafeCookie(TCHAR const *pszCookieName)
{
//
// m_pszURL was checked by the calling function and the object
// is protected. m_pszURL should never be null here.
//
_ASSERTE(m_pszURL);
//
// same goes for pszCookieName
//
_ASSERTE(pszCookieName);
BOOL bReturn = FALSE;
BOOL bFinalReturn = FALSE;
TCHAR *pszURL = NULL;
// remember to delete the returned string
GenerateURLString(&pszURL);
if (NULL == pszURL)
{
return FALSE;
}
//
// see how much data the cookie contains
//
DWORD dwCookieDataSize = 0;
//
// assuming the return code is TRUE if the method succeeds in getting
// get the buffer size. the current documentation is not 100% clear
//
bReturn = InternetGetCookie(pszURL, pszCookieName, NULL, &dwCookieDataSize);
//
// dwCookieDataSize has the length of cookie data
//
if ( bReturn && dwCookieDataSize )
{
//
// allocate the buffer for cookie data
//
TCHAR *pCookieDataBuffer = new TCHAR[dwCookieDataSize];
if (NULL != pCookieDataBuffer)
{
//
// all cookies for this page are returned in cookie data,
// the name argument is ignored
//
bReturn = InternetGetCookie(pszURL,
pszCookieName,
pCookieDataBuffer,
&dwCookieDataSize);
//
// is succeeded, parse cookie data buffer to see if the
// cookie we are looking for is there
//
if ( bReturn && ( NULL != _tcsstr(pCookieDataBuffer, pszCookieName) ) )
{
bFinalReturn = TRUE;
}
delete pCookieDataBuffer;
pCookieDataBuffer = NULL;
}
}
delete pszURL;
pszURL = NULL;
return bFinalReturn;
}
//
// add a registry entry for this page as an indicator that the page is safe
// returns TRUE if the registry entry exists
//
HRESULT MarkPageSafeInRegistry(TCHAR const *szRegistryKeyName)
{
_ASSERTE(m_pszURL);
//
// open the registry key. create if not there
//
DWORD dwDisposition = 0;
HKEY hKey = 0;
LONG rc = RegCreateKeyEx(HKEY_CURRENT_USER,
szRegistryKeyName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwDisposition);
if ( rc == ERROR_SUCCESS )
{
DWORD dwData = 0;
//
// add the current URL to the registry
//
rc = RegSetValueEx(hKey,
m_pszURL,
0,
REG_DWORD,
(BYTE*)&dwData,
sizeof(DWORD));
}
if (hKey)
{
RegCloseKey(hKey);
}
hKey = NULL;
if (rc == ERROR_SUCCESS )
{
return S_OK;
}
else
{
return E_FAIL;
}
}
//
// presence of a registry entry for this page indicates that the
// page is safe
//
BOOL IsPageSafeRegistry(TCHAR const *szRegistryKeyName)
{
DWORD dwDisposition = 0;
HKEY hKey = 0;
//
// the default is not safe
//
if (NULL == m_pszURL)
{
return FALSE;
}
//
// open the registry key where the page information is kept.
// create if not there
//
LONG rc = RegCreateKeyEx(HKEY_CURRENT_USER,
szRegistryKeyName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_CREATE_SUB_KEY | KEY_READ,
NULL,
&hKey,
&dwDisposition);
if ( rc == ERROR_SUCCESS )
{
DWORD dwDataType = 0;
DWORD dwDataSize = 0;
//
// read the setting for the current page.
// Note: we don't need the actual data, we just
// want to see if the value exists
//
rc = RegQueryValueEx(hKey,
m_pszURL,
0,
&dwDataType,
NULL,
&dwDataSize
);
}
if (hKey)
{
RegCloseKey(hKey);
}
hKey = NULL;
return (rc == ERROR_SUCCESS);
}
//
// build the URL string based on the hardcoded URL and
// the actual URL for this page.
// we are hoping that the striing will be unique (per page) and no
// mischevious scripting app can drop a cookie corresponding to
// this URL
//
// Note: if the implementation of of Internet(Set/Get)Cookie changes
// to have stricter validation for the URL string, this technique will
// not work
//
void GenerateURLString(TCHAR **ppszURL)
{
//
// the precondition is that m_pszURL exists
//
_ASSERT(NULL != m_pszURL);
*ppszURL = NULL;
//
// alias the char pointer pointer to by *pszURL.
// so it is easier to refer to.
//
TCHAR* &pszURL = *ppszURL;
//
// allocate memory for concatenated string
//
pszURL = new TCHAR[_tcslen(gszHardCodedURL) +
_tcslen(m_pszURL) +
_tcslen(gszURLSuffix) + 1];
// concatenate
if (pszURL)
{
*pszURL = _T('\0');
_tcscat(pszURL, gszHardCodedURL);
_tcscat(pszURL, m_pszURL);
_tcscat(pszURL, gszURLSuffix);
}
}
private:
//
// cached URL string
//
TCHAR *m_pszURL;
//
// cached security zone
//
DWORD m_dwSecurityZone;
//
// site for IObjectWithSite
//
IUnknown *m_pUnkSite;
//
// thread safety
//
static CComAutoCriticalSection s_ObjectWithSiteCritSection;
//
// the status of the current page
//
static EnValidation s_enValidation;
//
// name of the persistent cookie or registry key
//
TCHAR *m_pszStorageName;
};
#endif // _TAPI_OBJECT_WITH_SITE_H_