windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/cmp/asp/cookies.cpp
2020-09-26 16:20:57 +08:00

1520 lines
37 KiB
C++

/*===================================================================
Microsoft Denali
Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: Request, Response objects
File: cookies.cpp
Owner: DGottner
This file contains the code for the implementation of the
Request.Cookies and Response.Cookies collections.
===================================================================*/
#include "denpre.h"
#pragma hdrstop
#include "objbase.h"
#include "cookies.h"
#include "memchk.h"
#pragma warning (disable: 4355) // ignore: "'this' used in base member init
/*------------------------------------------------------------------
* C C o o k i e S u p p o r t E r r
*/
/*===================================================================
CCookieSupportErr::CCookieSupportErr
constructor
===================================================================*/
CCookieSupportErr::CCookieSupportErr(CCookie *pCookie)
{
m_pCookie = pCookie;
}
/*===================================================================
CCookieSupportErr::QueryInterface
CCookieSupportErr::AddRef
CCookieSupportErr::Release
Delegating IUnknown members for CCookieSupportErr object.
===================================================================*/
STDMETHODIMP CCookieSupportErr::QueryInterface(const IID &idInterface, void **ppvObj)
{
return m_pCookie->QueryInterface(idInterface, ppvObj);
}
STDMETHODIMP_(ULONG) CCookieSupportErr::AddRef()
{
return m_pCookie->AddRef();
}
STDMETHODIMP_(ULONG) CCookieSupportErr::Release()
{
return m_pCookie->Release();
}
/*===================================================================
CCookieSupportErr::InterfaceSupportsErrorInfo
Report back to OA about which interfaces we support that return
error information
===================================================================*/
STDMETHODIMP CCookieSupportErr::InterfaceSupportsErrorInfo(const GUID &idInterface)
{
if (idInterface == IID_IDispatch || idInterface == IID_IWriteCookie || idInterface == IID_IReadCookie)
return S_OK;
return S_FALSE;
}
/*------------------------------------------------------------------
* C W r i t e C o o k i e
*/
/*===================================================================
CWriteCookie::CWriteCookie
constructor
===================================================================*/
CWriteCookie::CWriteCookie(CCookie *pCookie)
{
m_pCookie = pCookie;
CDispatch::Init(IID_IWriteCookie);
}
/*===================================================================
CWriteCookie::QueryInterface
CWriteCookie::AddRef
CWriteCookie::Release
Delegating IUnknown members for CWriteCookie object.
===================================================================*/
STDMETHODIMP CWriteCookie::QueryInterface(const IID &idInterface, void **ppvObj)
{
// Bug 85953 Trap IDispatch before it gets to the core object
if (idInterface == IID_IUnknown ||
idInterface == IID_IWriteCookie ||
idInterface == IID_IDispatch)
{
*ppvObj = this;
static_cast<IUnknown *>(*ppvObj)->AddRef();
return S_OK;
}
else
return m_pCookie->QueryInterface(idInterface, ppvObj);
}
STDMETHODIMP_(ULONG) CWriteCookie::AddRef()
{
return m_pCookie->AddRef();
}
STDMETHODIMP_(ULONG) CWriteCookie::Release()
{
return m_pCookie->Release();
}
/*===================================================================
CWriteCookie::put_Item
Set the primary value for a cookie.
===================================================================*/
STDMETHODIMP CWriteCookie::put_Item(VARIANT varKey, BSTR bstrValue)
{
char *szKey; // ascii value of the key
CWCharToMBCS convValue;
CWCharToMBCS convKey;
// Bug 122589: Don't crash when "bstrValue" is NULL
if (bstrValue == NULL)
return E_FAIL;
// Initialize things
//
VARIANT *pvarKey = &varKey;
HRESULT hrReturn = S_OK;
// BUG 937: VBScript passes VT_VARIANT|VT_BYREF when passing obect
// produced by IEnumVariant
//
// Use VariantResolveDispatch which will:
//
// * Copy BYREF variants for us using VariantCopyInd
// * handle E_OUTOFMEMORY for us
// * get the default value from an IDispatch, which seems
// like an appropriate conversion.
//
VARIANT varKeyCopy;
VariantInit(&varKeyCopy);
if (V_VT(pvarKey) != VT_BSTR)
{
if (FAILED(VariantResolveDispatch(&varKeyCopy, &varKey, IID_IRequestDictionary, IDE_REQUEST)))
goto LExit;
pvarKey = &varKeyCopy;
}
switch (V_VT(pvarKey))
{
case VT_BSTR:
break;
case VT_ERROR:
if (V_ERROR(pvarKey) == DISP_E_PARAMNOTFOUND)
{
if (m_pCookie->m_szValue == NULL) // current value is a dictionary
{
CCookiePair *pNukePair = static_cast<CCookiePair *>(m_pCookie->m_mpszValues.Head());
while (pNukePair != NULL)
{
CCookiePair *pNext = static_cast<CCookiePair *>(pNukePair->m_pNext);
delete pNukePair;
pNukePair = pNext;
}
m_pCookie->m_mpszValues.ReInit();
}
else // no dictionary value
if (m_pCookie->m_fDuplicate)
free(m_pCookie->m_szValue);
if (FAILED(hrReturn = convValue.Init(bstrValue,m_pCookie->m_lCodePage))) {
goto LExit;
}
m_pCookie->m_szValue = NULL;
m_pCookie->AddValue(convValue.GetString(), TRUE);
m_pCookie->m_fDirty = TRUE;
goto LExit;
}
// Other error, FALL THROUGH to wrong type case
default:
ExceptionId(IID_IWriteCookie, IDE_COOKIE, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
// don't allow empty keys in the cookie
//
if (V_BSTR(pvarKey)) {
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey),m_pCookie->m_lCodePage))) {
goto LExit;
}
else {
szKey = convKey.GetString();
}
}
else {
szKey = "";
}
if (*szKey == '\0')
{
ExceptionId(IID_IWriteCookie, IDE_COOKIE, IDE_COOKIE_EMPTY_DICT);
hrReturn = E_FAIL;
goto LExit;
}
// we're changing a dictionary value, so first trash the primary value
//
if (m_pCookie->m_fDuplicate)
free(m_pCookie->m_szValue);
if (FAILED(hrReturn = convValue.Init(bstrValue,m_pCookie->m_lCodePage))) {
goto LExit;
}
m_pCookie->m_szValue = NULL;
m_pCookie->AddKeyAndValue(szKey, convValue.GetString(), TRUE);
m_pCookie->m_fDirty = TRUE;
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CWriteCookie::put_Expires
Set the expires attribute for a cookie.
===================================================================*/
STDMETHODIMP CWriteCookie::put_Expires(DATE dtExpires)
{
if (FAILED(VariantDateToCTime(dtExpires, &m_pCookie->m_tExpires)))
{
ExceptionId(IID_IWriteCookie, IDE_COOKIE, IDE_COOKIE_BAD_EXPIRATION);
return E_FAIL;
}
m_pCookie->m_fDirty = TRUE;
return S_OK;
}
/*===================================================================
CWriteCookie::put_Domain
Set the domain attribute for a cookie.
===================================================================*/
STDMETHODIMP CWriteCookie::put_Domain(BSTR bstrDomain)
{
CWCharToMBCS convDomain;
HRESULT hr = S_OK;
if (FAILED(hr = convDomain.Init(bstrDomain,m_pCookie->m_lCodePage)));
else {
if (m_pCookie->m_szDomain)
free(m_pCookie->m_szDomain);
m_pCookie->m_szDomain = convDomain.GetString(TRUE);
m_pCookie->m_fDirty = TRUE;
}
return hr;
}
/*===================================================================
CWriteCookie::put_Path
Set the path attribute for a cookie.
===================================================================*/
STDMETHODIMP CWriteCookie::put_Path(BSTR bstrPath)
{
HRESULT hr = S_OK;
CWCharToMBCS convPath;
if (FAILED(hr = convPath.Init(bstrPath,m_pCookie->m_lCodePage)));
else {
if (m_pCookie->m_szPath)
free(m_pCookie->m_szPath);
m_pCookie->m_szPath = convPath.GetString(TRUE);
if (m_pCookie->m_szPath == NULL)
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
m_pCookie->m_fDirty = TRUE;
return hr;
}
/*===================================================================
CWriteCookie::put_Secure
Set the secure attribute for a cookie.
===================================================================*/
STDMETHODIMP CWriteCookie::put_Secure(VARIANT_BOOL fSecure)
{
m_pCookie->m_fSecure = fSecure;
m_pCookie->m_fDirty = TRUE;
return S_OK;
}
/*===================================================================
CWriteCookie::get_HasKeys
Return True if the cookie contains keys, False if it is a simple
value
===================================================================*/
STDMETHODIMP CWriteCookie::get_HasKeys(VARIANT_BOOL *pfHasKeys)
{
*pfHasKeys = ( m_pCookie->m_mpszValues.Count() > 0 ? VARIANT_TRUE : VARIANT_FALSE);
return S_OK;
}
/*===================================================================
CWriteCookie::get__NewEnum
Return an enumerator object.
ReadCookie and WriteCookie use the same iterator object.
To reduce useless redundancy, deletage to IReadCookie.
The IReadCookie enumerator will likely be used much more
frequently than the IWriteCookie iterator, so we pay the
overhead of delegation in this function.
===================================================================*/
STDMETHODIMP CWriteCookie::get__NewEnum(IUnknown **ppEnumReturn)
{
IReadCookie *pReadCookie;
if (FAILED(QueryInterface(IID_IReadCookie, reinterpret_cast<void **>(&pReadCookie))))
{
Assert (FALSE); // expect success!
return E_FAIL;
}
HRESULT hrNewEnum = pReadCookie->get__NewEnum(ppEnumReturn);
pReadCookie->Release();
return hrNewEnum;
}
/*------------------------------------------------------------------
* C R e a d C o o k i e
*/
/*===================================================================
CReadCookie::CReadCookie
constructor
===================================================================*/
CReadCookie::CReadCookie(CCookie *pCookie)
{
m_pCookie = pCookie;
CDispatch::Init(IID_IReadCookie);
}
/*===================================================================
CReadCookie::QueryInterface
CReadCookie::AddRef
CReadCookie::Release
Delegating IUnknown members for CReadCookie object.
===================================================================*/
STDMETHODIMP CReadCookie::QueryInterface(const IID &idInterface, void **ppvObj)
{
// Bug 85953 Trap IDispatch before it gets to the core object
if (idInterface == IID_IUnknown ||
idInterface == IID_IReadCookie ||
idInterface == IID_IDispatch)
{
*ppvObj = this;
static_cast<IUnknown *>(*ppvObj)->AddRef();
return S_OK;
}
else
return m_pCookie->QueryInterface(idInterface, ppvObj);
}
STDMETHODIMP_(ULONG) CReadCookie::AddRef()
{
return m_pCookie->AddRef();
}
STDMETHODIMP_(ULONG) CReadCookie::Release()
{
return m_pCookie->Release();
}
/*===================================================================
CReadCookie::get_Item
Retrieve a value in the cookie dictionary.
===================================================================*/
STDMETHODIMP CReadCookie::get_Item(VARIANT varKey, VARIANT *pvarReturn)
{
char *szKey; // ascii version of the key
CCookiePair *pPair = NULL; // name and value of cookie in the dictionary
CWCharToMBCS convKey;
STACK_BUFFER( tempCookie, 128 );
// Initialize things
//
VariantInit(pvarReturn);
VARIANT *pvarKey = &varKey;
HRESULT hrReturn = S_OK;
// BUG 937: VBScript passes VT_VARIANT|VT_BYREF when passing obect
// produced by IEnumVariant
//
// Use VariantResolveDispatch which will:
//
// * Copy BYREF variants for us using VariantCopyInd
// * handle E_OUTOFMEMORY for us
// * get the default value from an IDispatch, which seems
// like an appropriate conversion.
//
VARIANT varKeyCopy;
VariantInit(&varKeyCopy);
DWORD vt = V_VT(pvarKey);
if ((V_VT(pvarKey) != VT_BSTR) && (vt != VT_I2) && (vt != VT_I4))
{
if (FAILED(VariantResolveDispatch(&varKeyCopy, &varKey, IID_IRequestDictionary, IDE_REQUEST)))
goto LExit;
pvarKey = &varKeyCopy;
}
vt = V_VT(pvarKey);
switch (vt)
{
// Bug 95201 support all numberic sub-types
case VT_I1: case VT_I2: case VT_I8:
case VT_UI1: case VT_UI2: case VT_UI4: case VT_UI8:
case VT_R4: case VT_R8:
// Coerce all integral types to VT_I4
if (FAILED(hrReturn = VariantChangeType(pvarKey, pvarKey, 0, VT_I4)))
goto LExit;
// fallthru to VT_I4
case VT_I4:
case VT_BSTR:
break;
case VT_ERROR:
if (V_ERROR(pvarKey) == DISP_E_PARAMNOTFOUND)
{
V_VT(pvarReturn) = VT_BSTR;
// simple value, URLEncoding NOT a good idea in this case
if (m_pCookie->m_szValue)
{
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(m_pCookie->m_szValue, 0, &bstrT,m_pCookie->m_lCodePage)))
{
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_OOM);
hrReturn = E_FAIL;
goto LExit;
}
V_BSTR(pvarReturn) = bstrT;
}
// dictionary value, must URLEncode to prevent '&', '=' from being misinterpreted
else
{
int cbHTTPCookie = m_pCookie->GetHTTPCookieSize();
if (cbHTTPCookie > REQUEST_ALLOC_MAX)
{
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_STACK_OVERFLOW);
hrReturn = E_FAIL;
goto LExit;
}
if (tempCookie.Resize(cbHTTPCookie) == FALSE) {
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
char *szHTTPCookie = static_cast<char *>(tempCookie.QueryPtr());
m_pCookie->GetHTTPCookie(szHTTPCookie);
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(szHTTPCookie, 0, &bstrT,m_pCookie->m_lCodePage)))
{
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_OOM);
hrReturn = E_FAIL;
goto LExit;
}
V_BSTR(pvarReturn) = bstrT;
}
goto LExit;
}
default:
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
if (vt == VT_BSTR)
{
// convert the key to ANSI
if (V_BSTR(pvarKey)) {
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey),m_pCookie->m_lCodePage))) {
goto LExit;
}
else {
szKey = convKey.GetString();
}
}
else {
szKey = "";
}
// Look up the key in the Cookie.
pPair = static_cast<CCookiePair *>(m_pCookie->m_mpszValues.FindElem(szKey, strlen(szKey)));
}
else
{
// Look up item by index
int iCount;
iCount = V_I4(pvarKey);
if ((iCount < 1) ||
(m_pCookie->m_mpszValues.Count() == 0) ||
(iCount > (int) m_pCookie->m_mpszValues.Count() ))
{
hrReturn = E_FAIL;
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pPair = static_cast<CCookiePair *>(m_pCookie->m_mpszValues.Head());
while((iCount > 1) && (pPair != NULL))
{
pPair = static_cast<CCookiePair *>(pPair->m_pNext);
iCount--;
}
}
if (pPair)
{
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(pPair->m_szValue, 0, &bstrT,m_pCookie->m_lCodePage)))
{
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_OOM);
hrReturn = E_FAIL;
goto LExit;
}
V_VT(pvarReturn) = VT_BSTR;
V_BSTR(pvarReturn) = bstrT;
}
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CReadCookie::get_HasKeys
Return True if the cookie contains keys, False if it is a simple
value
===================================================================*/
STDMETHODIMP CReadCookie::get_HasKeys(VARIANT_BOOL *pfHasKeys)
{
*pfHasKeys = (m_pCookie->m_mpszValues.Count() > 0 ? VARIANT_TRUE : VARIANT_FALSE);
return S_OK;
}
/*===================================================================
CReadCookie::get__NewEnum
Return an enumerator object.
===================================================================*/
STDMETHODIMP CReadCookie::get__NewEnum(IUnknown **ppEnumReturn)
{
*ppEnumReturn = NULL;
CCookieIterator *pIterator = new CCookieIterator(m_pCookie);
if (pIterator == NULL)
{
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_OOM);
return E_OUTOFMEMORY;
}
*ppEnumReturn = pIterator;
return S_OK;
}
/*===================================================================
CReadCookie::get_Count
Parameters:
pcValues - count is stored in *pcValues. Set to 0 if this
cookie is not multi-valued.
===================================================================*/
STDMETHODIMP CReadCookie::get_Count(int *pcValues)
{
*pcValues = m_pCookie->m_mpszValues.Count();
return S_OK;
}
/*===================================================================
CReadCookie::get_Key
Function called from DispInvoke to get keys from a multi-valued
Cookie collection.
Parameters:
vKey VARIANT [in], which parameter to get the key of
pvarReturn VARIANT *, [out] value of the requested parameter
Returns:
S_OK on success, E_FAIL on failure.
===================================================================*/
STDMETHODIMP CReadCookie::get_Key(VARIANT varKey, VARIANT *pvarReturn)
{
char *szKey; // ascii version of the key
CCookiePair *pPair = NULL; // name and value of cookie in the dictionary
CWCharToMBCS convKey;
STACK_BUFFER( tempCookie, 128);
// Initialize things
//
VariantInit(pvarReturn);
VARIANT *pvarKey = &varKey;
HRESULT hrReturn = S_OK;
// BUG 937: VBScript passes VT_VARIANT|VT_BYREF when passing obect
// produced by IEnumVariant
//
// Use VariantResolveDispatch which will:
//
// * Copy BYREF variants for us using VariantCopyInd
// * handle E_OUTOFMEMORY for us
// * get the default value from an IDispatch, which seems
// like an appropriate conversion.
//
VARIANT varKeyCopy;
VariantInit(&varKeyCopy);
DWORD vt = V_VT(pvarKey);
if ((V_VT(pvarKey) != VT_BSTR) && (vt != VT_I2) && (vt != VT_I4))
{
if (FAILED(VariantResolveDispatch(&varKeyCopy, &varKey, IID_IRequestDictionary, IDE_REQUEST)))
goto LExit;
pvarKey = &varKeyCopy;
}
vt = V_VT(pvarKey);
switch (vt)
{
// Bug 95201 support all numberic sub-types
case VT_I1: case VT_I2: case VT_I8:
case VT_UI1: case VT_UI2: case VT_UI4: case VT_UI8:
case VT_R4: case VT_R8:
// Coerce all integral types to VT_I4
if (FAILED(hrReturn = VariantChangeType(pvarKey, pvarKey, 0, VT_I4)))
goto LExit;
// fallthru to VT_I4
case VT_I4:
case VT_BSTR:
break;
case VT_ERROR:
if (V_ERROR(pvarKey) == DISP_E_PARAMNOTFOUND)
{
V_VT(pvarReturn) = VT_BSTR;
// simple value, URLEncoding NOT a good idea in this case
if (m_pCookie->m_szValue)
{
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(m_pCookie->m_szValue, 0, &bstrT,m_pCookie->m_lCodePage)))
{
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_OOM);
hrReturn = E_FAIL;
goto LExit;
}
V_BSTR(pvarReturn) = bstrT;
}
// dictionary value, must URLEncode to prevent '&', '=' from being misinterpreted
else
{
int cbHTTPCookie = m_pCookie->GetHTTPCookieSize();
if (cbHTTPCookie > REQUEST_ALLOC_MAX)
{
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_STACK_OVERFLOW);
hrReturn = E_FAIL;
goto LExit;
}
if (tempCookie.Resize(cbHTTPCookie) == FALSE) {
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
char *szHTTPCookie = static_cast<char *>(tempCookie.QueryPtr());
m_pCookie->GetHTTPCookie(szHTTPCookie);
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(szHTTPCookie, 0, &bstrT, m_pCookie->m_lCodePage)))
{
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_OOM);
hrReturn = E_FAIL;
goto LExit;
}
V_BSTR(pvarReturn) = bstrT;
}
goto LExit;
}
default:
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
if (vt == VT_BSTR)
{
// convert the key to ANSI
if (V_BSTR(pvarKey)) {
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey),m_pCookie->m_lCodePage))) {
goto LExit;
}
else {
szKey = convKey.GetString();
}
}
else {
szKey = "";
}
// Look up the key in the Cookie.
pPair = static_cast<CCookiePair *>(m_pCookie->m_mpszValues.FindElem(szKey, strlen(szKey)));
}
else
{
// Look up item by index
int iCount;
iCount = V_I4(pvarKey);
if ((iCount < 1) ||
(m_pCookie->m_mpszValues.Count() == 0) ||
(iCount > (int) m_pCookie->m_mpszValues.Count() ))
{
hrReturn = E_FAIL;
ExceptionId(IID_IReadCookie, IDE_COOKIE, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pPair = static_cast<CCookiePair *>(m_pCookie->m_mpszValues.Head());
while((iCount > 1) && (pPair != NULL))
{
pPair = static_cast<CCookiePair *>(pPair->m_pNext);
iCount--;
}
}
if (pPair)
{
// Create a BSTR containing the key for this variant
BSTR bstrT;
SysAllocStringFromSz((CHAR *)pPair->m_pKey, 0, &bstrT, m_pCookie->m_lCodePage);
if (!bstrT)
return E_OUTOFMEMORY;
V_VT(pvarReturn) = VT_BSTR;
V_BSTR(pvarReturn) = bstrT;
}
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*------------------------------------------------------------------
* C C o o k i e
*/
/*===================================================================
CCookie::CCookie
constructor
===================================================================*/
CCookie::CCookie(CIsapiReqInfo *pIReq, UINT lCodePage, IUnknown *pUnkOuter, PFNDESTROYED pfnDestroy)
: m_WriteCookieInterface(this),
m_ReadCookieInterface(this),
m_CookieSupportErrorInfo(this)
{
m_szValue = NULL;
m_tExpires = -1;
m_szDomain = NULL;
m_szPath = NULL;
m_fSecure = FALSE;
m_fDirty = FALSE;
m_fDuplicate = FALSE;
m_pfnDestroy = pfnDestroy;
m_pIReq = pIReq;
m_lCodePage = lCodePage;
m_cRefs = 1;
}
/*===================================================================
CCookie::~CCookie
Destructor
===================================================================*/
CCookie::~CCookie()
{
CCookiePair *pNukePair = static_cast<CCookiePair *>(m_mpszValues.Head());
while (pNukePair != NULL)
{
CCookiePair *pNext = static_cast<CCookiePair *>(pNukePair->m_pNext);
delete pNukePair;
pNukePair = pNext;
}
m_mpszValues.UnInit();
if (m_fDuplicate)
free(m_szValue);
if (m_szDomain) free(m_szDomain);
if (m_szPath) free(m_szPath);
}
/*===================================================================
CCookie::Init
initialize the cookie. This initializes the cookie's value hashing
table
===================================================================*/
HRESULT CCookie::Init()
{
HRESULT hr = S_OK;
TCHAR pathInfo[MAX_PATH];
#if UNICODE
CWCharToMBCS convStr;
#endif
if (FAILED(hr = m_mpszValues.Init(7)));
// it would be nice if we could use the application path from the metabase,
// but because of case sensitivity issues, we can't. The safest bet is
// to use the request's path info up to the length of the application's
// pathinfo.
else if (FAILED(hr=FindApplicationPath(m_pIReq, pathInfo, sizeof(pathInfo))));
#if UNICODE
else if (FAILED(hr = convStr.Init(m_pIReq->QueryPszPathInfo(), m_lCodePage, _tcslen(pathInfo))));
else {
m_szPath = convStr.GetString(TRUE);
}
#else
else {
int cchPathInfo = _tcslen(pathInfo);
if (!(m_szPath = (char *)malloc(cchPathInfo+1))) {
hr = E_OUTOFMEMORY;
}
else {
memcpy(m_szPath, pathInfo, cchPathInfo+1);
}
}
#endif
return hr;
}
/*===================================================================
CCookie::QueryInterface
CCookie::AddRef
CCookie::Release
IUnknown members for CCookie object.
Note on CCookie::QueryInterface: The Query for IDispatch is
ambiguous because it can either refer to IReadCookie or
IWriteCookie. To resolve this, we resolve requests for IDispatch
to IReadCookie. The rationale for this is that the code in
request.cpp calls QueryInterface for a generic IDispatch pointer
(because the collection is heterogenous) The Response.Cookies
collection is homogeneous and so only calls QueryInterface for
IWriteCookie.
===================================================================*/
STDMETHODIMP CCookie::QueryInterface(const IID &idInterface, void **ppvObj)
{
if (idInterface == IID_IUnknown)
*ppvObj = this;
else if (idInterface == IID_IReadCookie || idInterface == IID_IDispatch)
*ppvObj = &m_ReadCookieInterface;
else if (idInterface == IID_IWriteCookie)
*ppvObj = &m_WriteCookieInterface;
else if (idInterface == IID_ISupportErrorInfo)
*ppvObj = &m_CookieSupportErrorInfo;
else
*ppvObj = NULL;
if (*ppvObj != NULL)
{
static_cast<IUnknown *>(*ppvObj)->AddRef();
return S_OK;
}
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP_(ULONG) CCookie::AddRef()
{
return ++m_cRefs;
}
STDMETHODIMP_(ULONG) CCookie::Release(void)
{
if (--m_cRefs != 0)
return m_cRefs;
if (m_pfnDestroy != NULL)
(*m_pfnDestroy)();
delete this;
return 0;
}
/*===================================================================
CCookie::AddValue
Set the cookie's primary value. One you set the primary value,
you can't reset it.
===================================================================*/
HRESULT CCookie::AddValue(char *szValue, BOOL fDuplicate)
{
if (m_szValue != NULL) // cookie already is marked as single-valued
return E_FAIL;
if (m_mpszValues.Count() != 0) // cookie already has a value
return E_FAIL;
if (fDuplicate)
{
char *szNew = (char *)malloc(strlen(szValue) + 1);
if (szNew == NULL)
return E_OUTOFMEMORY;
m_szValue = strcpy(szNew, szValue);
}
else
m_szValue = szValue;
m_fDuplicate = fDuplicate;
return S_OK;
}
/*===================================================================
CCookie::AddKeyAndValue
Add a key and value pair to the Cookie's dictionary. It fails
if the cookie has a primary value already set. It will overwrite
the value if the key already exists.
===================================================================*/
HRESULT CCookie::AddKeyAndValue(char *szKey, char *szValue, BOOL fDuplicate)
{
if (m_szValue != NULL)
return E_FAIL;
delete static_cast<CCookiePair *>(m_mpszValues.DeleteElem(szKey, strlen(szKey)));
CCookiePair *pCookiePair = new CCookiePair;
if (pCookiePair == NULL)
return E_OUTOFMEMORY;
if (FAILED(pCookiePair->Init(szKey, szValue, fDuplicate)))
return E_FAIL;
m_mpszValues.AddElem(pCookiePair);
return S_OK;
}
/*===================================================================
CCookie::GetHTTPCookieSize
Return the number of bytes required for the expansion of the HTTP_COOKIE variable
===================================================================*/
size_t CCookie::GetHTTPCookieSize()
{
if (m_szValue)
return URLEncodeLen(m_szValue);
else
{
int cbValue = 1;
CCookiePair *pPair = static_cast<CCookiePair *>(m_mpszValues.Head());
while (pPair)
{
// Add size of the URL Encoded key, a character for the '=', and a
// character for the '&' or the NUL terminator. URLEncodeLen
// returns the size + 1, so the two calls to URLEncodeLen() add the
// two characters we need.
//
cbValue += URLEncodeLen(reinterpret_cast<char *>(pPair->m_pKey)) + URLEncodeLen(pPair->m_szValue);
pPair = static_cast<CCookiePair *>(pPair->m_pNext);
}
return cbValue;
}
}
/*===================================================================
CCookie::GetHTTPCookie
Return the URL Encoded value a single cookie
Parameters:
szBuffer - pointer to the destination buffer to store the
URL encoded value
Returns:
Returns a pointer to the terminating NUL character.
===================================================================*/
char *CCookie::GetHTTPCookie(char *szBuffer)
{
if (m_szValue)
return URLEncode(szBuffer, m_szValue);
else
{
char *szDest = szBuffer;
*szDest = '\0';
CCookiePair *pPair = static_cast<CCookiePair *>(m_mpszValues.Head());
while (pPair)
{
// Write <name>=<value> string
szDest = URLEncode(szDest, reinterpret_cast<char *>(pPair->m_pKey));
*szDest++ = '=';
szDest = URLEncode(szDest, pPair->m_szValue);
// Advance
pPair = static_cast<CCookiePair *>(pPair->m_pNext);
// Append '&' if there's another one following
if (pPair)
*szDest++ = '&';
}
Assert (*szDest == '\0'); // make sure we are nul-terminated
return szDest;
}
}
/*===================================================================
CCookie::GetCookieHeaderSize
Return the number of bytes required to allocate for the "Set-Cookie" header.
Parameters:
szName - the name of the cookie (the size of the name is added to the value)
Returns:
Returns 0 if *this does not contain a cookie value.
===================================================================*/
size_t CCookie::GetCookieHeaderSize(const char *szName)
{
int cbCookie = sizeof "Set-Cookie: "; // initialize and add NUL terminator now
// Add size of the URL Encoded name, a character for the '=', and the size
// of the URL Encoded cookie value. URLEncodeLen, and GetHttpCookieSize
// compensate for the NUL terminator, so we actually SUBTRACT 1. (-2 for
// these two function calls, +1 for the '=' sign
//
cbCookie += URLEncodeLen(szName) + GetHTTPCookieSize() - 1;
if (m_tExpires != -1)
cbCookie += (sizeof "; expires=") + DATE_STRING_SIZE - 1;
// BUG 250 - DBCS External
// ASP does not URLEncode the domain and path attributes, which was noticed
// during localizaiton.
//
// NOTE: URLEncodeLen and sizeof both add a space for the nul terminator,
// so we subtract 2 to compensate.
//
if (m_szDomain)
cbCookie += (sizeof "; domain=") + DBCSEncodeLen(m_szDomain) - 2;
cbCookie += (sizeof "; path=") + DBCSEncodeLen(m_szPath) - 2;
if (m_fSecure)
cbCookie += (sizeof "; secure") - 1;
return cbCookie;
}
/*===================================================================
CCookie::GetCookieHeader
Construct the appropriate "Set-Cookie" header for a cookie.
Parameters:
szName - the name of the cookie (the size of the name is added to the value)
Returns:
Returns 0 if *this does not contain a cookie value.
===================================================================*/
char *CCookie::GetCookieHeader(const char *szName, char *szBuffer)
{
// write out the cookie name and value
//
char *szDest = strcpyExA(szBuffer, "Set-Cookie: ");
szDest = URLEncode(szDest, szName);
szDest = strcpyExA(szDest, "=");
szDest = GetHTTPCookie(szDest);
if (m_tExpires != -1) {
char szExpires[DATE_STRING_SIZE];
CTimeToStringGMT(&m_tExpires, szExpires, TRUE);
szDest = strcpyExA(szDest, "; expires=");
szDest = strcpyExA(szDest, szExpires);
}
if (m_szDomain) {
szDest = strcpyExA(szDest, "; domain=");
szDest = DBCSEncode(szDest, m_szDomain);
}
szDest = strcpyExA(szDest, "; path=");
szDest = DBCSEncode(szDest, m_szPath);
if (m_fSecure)
szDest = strcpyExA(szDest, "; secure");
return szDest;
}
/*------------------------------------------------------------------
* C C o o k i e P a i r
*/
/*===================================================================
CCookiePair::CCookiePair
constructor
===================================================================*/
CCookiePair::CCookiePair()
{
m_fDuplicate = FALSE;
m_szValue = NULL;
}
/*===================================================================
CCookiePair::Init
Initialize the cookie pair with a key and a value. Optionally,
it will copy the strings as well.
===================================================================*/
HRESULT CCookiePair::Init(const char *szKey, const char *szValue, BOOL fDuplicate)
{
m_fDuplicate = fDuplicate;
if (fDuplicate)
{
char *szNewKey = (char *)malloc(strlen(szKey) + 1);
if (szNewKey == NULL)
return E_OUTOFMEMORY;
char *szNewValue = (char *)malloc(strlen(szValue) + 1);
if (szNewValue == NULL)
{
free(szNewKey);
return E_OUTOFMEMORY;
}
if (FAILED(CLinkElem::Init(strcpy(szNewKey, szKey), strlen(szKey))))
{
free(szNewKey);
free(szNewValue);
return E_FAIL;
}
m_szValue = strcpy(szNewValue, szValue);
}
else
{
if (FAILED(CLinkElem::Init(const_cast<char *>(szKey), strlen(szKey))))
return E_FAIL;
m_szValue = const_cast<char *>(szValue);
}
return S_OK;
}
/*===================================================================
CCookiePair::~CCookiePair
destructor
===================================================================*/
CCookiePair::~CCookiePair()
{
if (m_fDuplicate)
{
if (m_pKey) free(m_pKey);
if (m_szValue) free(m_szValue);
}
}
/*------------------------------------------------------------------
* C C o o k i e I t e r a t o r
*/
/*===================================================================
CCookieIterator::CCookieIterator
Constructor
===================================================================*/
CCookieIterator::CCookieIterator(CCookie *pCookie)
{
m_pCookie = pCookie;
m_pCurrent = static_cast<CCookiePair *>(m_pCookie->m_mpszValues.Head());
m_cRefs = 1;
m_pCookie->AddRef();
}
/*===================================================================
CCookieIterator::CCookieIterator
Destructor
===================================================================*/
CCookieIterator::~CCookieIterator()
{
m_pCookie->Release();
}
/*===================================================================
CCookieIterator::QueryInterface
CCookieIterator::AddRef
CCookieIterator::Release
IUnknown members for CServVarsIterator object.
===================================================================*/
STDMETHODIMP CCookieIterator::QueryInterface(REFIID iid, void **ppvObj)
{
if (iid == IID_IUnknown || iid == IID_IEnumVARIANT)
{
AddRef();
*ppvObj = this;
return S_OK;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CCookieIterator::AddRef()
{
return ++m_cRefs;
}
STDMETHODIMP_(ULONG) CCookieIterator::Release()
{
if (--m_cRefs > 0)
return m_cRefs;
delete this;
return 0;
}
/*===================================================================
CCookieIterator::Clone
Clone this iterator (standard method)
===================================================================*/
STDMETHODIMP CCookieIterator::Clone(IEnumVARIANT **ppEnumReturn)
{
CCookieIterator *pNewIterator = new CCookieIterator(m_pCookie);
if (pNewIterator == NULL)
return E_OUTOFMEMORY;
// new iterator should point to same location as this.
pNewIterator->m_pCurrent = m_pCurrent;
*ppEnumReturn = pNewIterator;
return S_OK;
}
/*===================================================================
CCookieIterator::Next
Get next value (standard method)
To rehash standard OLE semantics:
We get the next "cElements" from the collection and store them
in "rgVariant" which holds at least "cElements" items. On
return "*pcElementsFetched" contains the actual number of elements
stored. Returns S_FALSE if less than "cElements" were stored, S_OK
otherwise.
===================================================================*/
STDMETHODIMP CCookieIterator::Next(unsigned long cElementsRequested, VARIANT *rgVariant, unsigned long *pcElementsFetched)
{
// give a valid pointer value to 'pcElementsFetched'
//
unsigned long cElementsFetched;
if (pcElementsFetched == NULL)
pcElementsFetched = &cElementsFetched;
// Loop through the collection until either we reach the end or
// cElements becomes zero
//
unsigned long cElements = cElementsRequested;
*pcElementsFetched = 0;
while (cElements > 0 && m_pCurrent != NULL)
{
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(reinterpret_cast<char *>(m_pCurrent->m_pKey), 0, &bstrT, m_pCookie->m_lCodePage)))
return E_OUTOFMEMORY;
V_VT(rgVariant) = VT_BSTR;
V_BSTR(rgVariant) = bstrT;
++rgVariant;
--cElements;
++*pcElementsFetched;
m_pCurrent = static_cast<CCookiePair *>(m_pCurrent->m_pNext);
}
// initialize the remaining variants
//
while (cElements-- > 0)
VariantInit(rgVariant++);
return (*pcElementsFetched == cElementsRequested)? S_OK : S_FALSE;
}
/*===================================================================
CCookieIterator::Skip
Skip items (standard method)
To rehash standard OLE semantics:
We skip over the next "cElements" from the collection.
Returns S_FALSE if less than "cElements" were skipped, S_OK
otherwise.
===================================================================*/
STDMETHODIMP CCookieIterator::Skip(unsigned long cElements)
{
/* Loop through the collection until either we reach the end or
* cElements becomes zero
*/
while (cElements > 0 && m_pCurrent != NULL)
{
--cElements;
m_pCurrent = static_cast<CCookiePair *>(m_pCurrent->m_pNext);
}
return (cElements == 0)? S_OK : S_FALSE;
}
/*===================================================================
CCookieIterator::Reset
Reset the iterator (standard method)
===================================================================*/
STDMETHODIMP CCookieIterator::Reset()
{
m_pCurrent = static_cast<CCookiePair *>(m_pCookie->m_mpszValues.Head());
return S_OK;
}