Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5975 lines
172 KiB

/*===================================================================
Microsoft Denali
Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: request object
File: request.cpp
Owner: CGrant, DGottner
This file contains the code for the implementation of the Request object.
===================================================================*/
#include "denpre.h"
#pragma hdrstop
#include "objbase.h"
#include "request.h"
#include "cookies.h"
#include "clcert.h"
#include "memchk.h"
#pragma warning (disable: 4355) // ignore: "'this' used in base member init
static char HexToChar(LPSTR);
static char DecodeFromURL(char **pszSource, char *szStop, char *szDest, UINT uCodePage, BOOL fIgnoreCase = FALSE);
struct {
int varLen;
char *szVarName;
} g_sUNICODEVars [] = {
{3, "URL"},
{9, "PATH_INFO"},
{9, "AUTH_USER"},
{9, "CACHE_URL"},
{10,"LOGON_USER"},
{11,"REMOTE_USER"},
{11,"SCRIPT_NAME"},
{11,"APP_POOL_ID"},
{12,"APPL_MD_PATH"},
{15,"PATH_TRANSLATED"},
{17,"SCRIPT_TRANSLATED"},
{18,"APPL_PHYSICAL_PATH"},
{20,"UNMAPPED_REMOTE_USER"},
{-1,""}
};
/*------------------------------------------------------------------
* C R e q u e s t H i t
*/
/*===================================================================
CRequestHit::CRequestHit
Constructor
Parameters:
None
===================================================================*/
CRequestHit::CRequestHit()
{
m_fInited = FALSE;
m_fDuplicate = FALSE;
m_pQueryData = m_pFormData = NULL;
m_pCookieData = NULL;
m_pClCertData = NULL;
}
/*===================================================================
CRequestHit::~CRequestHit
Destructor
===================================================================*/
CRequestHit::~CRequestHit()
{
if (m_pQueryData != NULL)
m_pQueryData->Release();
if (m_pFormData != NULL)
m_pFormData->Release();
if (m_pCookieData != NULL)
m_pCookieData->Release();
if (m_pClCertData != NULL)
m_pClCertData->Release();
if (m_fDuplicate)
delete m_pKey;
}
/*===================================================================
CRequestHit::Init
Constructor
Parameters:
szName - pointer to string containing name
fDuplicate - TRUE if we should dup the string
Returns:
E_OUTOFMEMORY, E_FAIL, or S_OK
===================================================================*/
HRESULT CRequestHit::Init(char *szName, BOOL fDuplicate)
{
if (m_fInited)
return E_FAIL;
m_fDuplicate = fDuplicate;
if (fDuplicate)
{
char *szNewKey = new char [strlen(szName) + 1];
if (szNewKey == NULL)
return E_OUTOFMEMORY;
if (FAILED(CLinkElem::Init(strcpy(szNewKey, szName), strlen(szName))))
return E_FAIL;
}
else
if (FAILED(CLinkElem::Init(szName, strlen(szName))))
return E_FAIL;
m_fInited = TRUE;
return S_OK;
}
/*===================================================================
CRequestHit::AddValue
Parameters:
source - type of the value (QueryString or Body)
szValue - the value as a null-terminated string.
lCodePage - the CodePage used when retrieve the data
Returns:
Nothing.
===================================================================*/
HRESULT CRequestHit::AddValue
(
CollectionType Source,
char *szValue,
CIsapiReqInfo *pIReq,
UINT lCodePage
)
{
HRESULT hResult;
CStringList **ppValues = NULL;
switch (Source)
{
case QUERYSTRING:
ppValues = &m_pQueryData;
break;
case FORM:
ppValues = &m_pFormData;
break;
case COOKIE:
if (m_pCookieData == NULL)
{
m_pCookieData = new CCookie(pIReq, lCodePage);
if (m_pCookieData == NULL)
return E_OUTOFMEMORY;
if (FAILED(hResult = m_pCookieData->Init()))
return hResult;
}
return m_pCookieData->AddValue(szValue);
case CLCERT:
if (m_pClCertData == NULL)
{
m_pClCertData = new CClCert;
if (m_pClCertData == NULL)
return E_OUTOFMEMORY;
if (FAILED(hResult = m_pClCertData->Init()))
return hResult;
}
return m_pClCertData->AddValue(szValue);
default:
return E_FAIL;
}
if (*ppValues == NULL)
{
*ppValues = new CStringList;
if (*ppValues == NULL)
return E_OUTOFMEMORY;
}
if (FAILED(hResult = (*ppValues)->AddValue(szValue, FALSE, lCodePage)))
return hResult;
return S_OK;
}
HRESULT CRequestHit::AddCertValue(VARENUM ve, LPBYTE pValue, UINT cLen )
{
HRESULT hResult;
if (m_pClCertData == NULL)
{
m_pClCertData = new CClCert;
if (m_pClCertData == NULL)
return E_OUTOFMEMORY;
if (FAILED(hResult = m_pClCertData->Init()))
return hResult;
}
return m_pClCertData->AddValue( (LPSTR)pValue, ve, cLen );
}
/*===================================================================
CRequestHit::AddKeyAndValue
Add a value based on keys for collections that support them. Currently,
only cookies support them.
Parameters:
source - type of the value (must be Cookie)
szKey - the key
szValue - the value as a null-terminated string.
Returns:
Returns E_OUTOFMEMORY if memory cannot be allocated,
E_FAIL if source collection does not support keys,
===================================================================*/
HRESULT CRequestHit::AddKeyAndValue
(
CollectionType Source,
char *szKey,
char *szValue,
CIsapiReqInfo *pIReq,
UINT lCodePage
)
{
HRESULT hResult;
switch ( Source )
{
case COOKIE:
if (m_pCookieData == NULL)
{
m_pCookieData = new CCookie( pIReq , lCodePage);
if (m_pCookieData == NULL)
return E_OUTOFMEMORY;
if (FAILED(hResult = m_pCookieData->Init()))
return hResult;
}
return m_pCookieData->AddKeyAndValue(szKey, szValue);
default:
return E_FAIL;
}
}
/*------------------------------------------------------------------
* C R e q u e s t H i t s A r r a y
*/
/*===================================================================
CRequestHitsArray::CRequestHitsArray
Constructor
Parameters:
Returns:
===================================================================*/
CRequestHitsArray::CRequestHitsArray()
: m_dwCount(0), m_dwHitMax(0), m_rgRequestHit(NULL)
{
}
/*===================================================================
CRequestHitsArray::~CRequestHitsArray
Destructor
Parameters:
Returns:
===================================================================*/
CRequestHitsArray::~CRequestHitsArray()
{
if (m_rgRequestHit)
delete [] m_rgRequestHit;
}
/*===================================================================
CRequestHitsArray::AddRequestHit
Add an element to the array
Parameters:
pHit element to add
Returns:
===================================================================*/
BOOL CRequestHitsArray::AddRequestHit
(
CRequestHit *pHit
)
{
Assert(pHit);
if (m_dwCount == m_dwHitMax)
{
DWORD dwNewSize = m_dwHitMax + NUM_REQUEST_HITS;
CRequestHit **ppNewArray = new CRequestHit *[dwNewSize];
if (ppNewArray == NULL)
return FALSE;
ZeroMemory(ppNewArray, sizeof(CRequestHit *) * dwNewSize);
if (m_dwCount)
{
Assert(m_rgRequestHit);
// Copy pointers from old array
memcpy
(
ppNewArray,
m_rgRequestHit,
m_dwCount * sizeof(CRequestHit *)
);
// free old array
delete [] m_rgRequestHit;
}
else
{
Assert(m_rgRequestHit == NULL);
}
m_rgRequestHit = ppNewArray;
m_dwHitMax = dwNewSize;
}
m_rgRequestHit[m_dwCount++] = pHit;
return TRUE;
}
/*------------------------------------------------------------------
* C Q u e r y S t r i n g
*/
/*===================================================================
CQueryString::CQueryString
Constructor
Parameters:
pRequest Pointer to main Request object
pUnkOuter LPUNKNOWN of a controlling unknown.
Returns:
Nothing.
Note:
This object is NOT ref counted since it is created & destroyed
automatically by C++.
===================================================================*/
CQueryString::CQueryString(CRequest *pRequest, IUnknown *pUnkOuter)
: m_ISupportErrImp(this, pUnkOuter, IID_IRequestDictionary)
{
m_punkOuter = pUnkOuter;
if (pRequest)
pRequest->AddRef();
m_pRequest = pRequest;
CDispatch::Init(IID_IRequestDictionary);
}
/*===================================================================
CQueryString::~CQueryString
Destructor
Parameters:
None
Returns:
Nothing.
===================================================================*/
CQueryString::~CQueryString()
{
if (m_pRequest)
m_pRequest->Release();
}
/*===================================================================
HRESULT CQueryString::Init
Parameters:
None
Returns:
E_OUTOFMEMORY if allocation fails.
===================================================================*/
HRESULT CQueryString::Init()
{
return CRequestHitsArray::Init();
}
/*===================================================================
HRESULT CQueryString::ReInit
Parameters:
None
Returns:
S_OK
===================================================================*/
HRESULT CQueryString::ReInit()
{
return CRequestHitsArray::ReInit();
}
/*===================================================================
CQueryString::QueryInterface
CQueryString::AddRef
CQueryString::Release
IUnknown members for CQueryString object.
===================================================================*/
STDMETHODIMP CQueryString::QueryInterface(REFIID iid, void **ppvObj)
{
*ppvObj = NULL;
if (iid == IID_IUnknown || iid == IID_IRequestDictionary || iid == IID_IDispatch)
*ppvObj = this;
else if (iid == IID_ISupportErrorInfo)
*ppvObj = &m_ISupportErrImp;
if (*ppvObj != NULL)
{
static_cast<IUnknown *>(*ppvObj)->AddRef();
return S_OK;
}
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP_(ULONG) CQueryString::AddRef(void)
{
return m_punkOuter->AddRef();
}
STDMETHODIMP_(ULONG) CQueryString::Release(void)
{
return m_punkOuter->Release();
}
/*===================================================================
CQueryString::get_Item
Function called from DispInvoke to get values from the QueryString collection.
Parameters:
vKey VARIANT [in], which parameter to get the value of - Empty means whole collection
pvarReturn VARIANT *, [out] value of the requested parameter
Returns:
S_OK on success, E_FAIL on failure.
===================================================================*/
HRESULT CQueryString::get_Item(VARIANT varKey, VARIANT *pvarReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey;
CWCharToMBCS convKey;
CRequestHit *pRequestHit; // pointer to request bucket
IDispatch *pSListReturn; // value of the key
// 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 ((vt != 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)
{
// Look up QueryString using the "ServerVariables" collection -
// The LoadVariables() function trashes QueryPszQueryString() in the CIsapiReqInfo
//
DWORD dwQStrSize;
STACK_BUFFER( queryStrBuff, 256 );
if (!SERVER_GET(m_pRequest->GetIReq(), "QUERY_STRING", &queryStrBuff, &dwQStrSize)) {
if (GetLastError() == ERROR_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
}
else {
hrReturn = E_FAIL;
}
goto LExit;
}
char *szQueryString = (char *)queryStrBuff.QueryPtr();
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(szQueryString, 0, &bstrT,m_pRequest->GetCodePage())))
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
V_VT(pvarReturn) = VT_BSTR;
V_BSTR(pvarReturn) = bstrT;
goto LExit;
}
// Other error, FALL THROUGH to wrong type case
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
if (m_pRequest->m_pData->m_fLoadQuery)
{
if (FAILED(m_pRequest->LoadVariables(QUERYSTRING, m_pRequest->GetIReq()->QueryPszQueryString(), m_pRequest->GetCodePage())))
{
hrReturn = E_FAIL;
goto LExit;
}
m_pRequest->m_pData->m_fLoadQuery = FALSE;
}
if (vt == VT_BSTR)
{
// convert BSTR version to ANSI version of the key using current Session.CodePage
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey), m_pRequest->GetCodePage()))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = convKey.GetString();
}
pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
}
else
{
// Look up item by index
int iCount;
iCount = V_I4(pvarKey);
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_dwCount)) || ((iCount > 0) && (int) m_dwCount == 0))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pRequestHit = m_rgRequestHit[iCount - 1];
}
if (pRequestHit)
{
CStringList *pValues = pRequestHit->m_pQueryData;
if (pValues == NULL)
goto LNotFound;
if (FAILED(pValues->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&pSListReturn))))
Assert (FALSE);
V_VT(pvarReturn) = VT_DISPATCH;
V_DISPATCH(pvarReturn) = pSListReturn;
goto LExit;
}
LNotFound: // Return "Empty"
if (FAILED(m_pRequest->m_pData->GetEmptyStringList(&pSListReturn)))
hrReturn = E_FAIL;
V_VT(pvarReturn) = VT_DISPATCH;
V_DISPATCH(pvarReturn) = pSListReturn;
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CQueryString::get_Key
Function called from DispInvoke to get keys from the QueryString 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.
===================================================================*/
HRESULT CQueryString::get_Key(VARIANT varKey, VARIANT *pVar)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey; // ascii version of the key
CWCharToMBCS convKey;
CRequestHit *pRequestHit; // pointer to request bucket
IDispatch *pSListReturn; // value of the key
// Initialize things
//
VariantInit(pVar);
VARIANT *pvarKey = &varKey;
V_VT(pVar) = VT_BSTR;
V_BSTR(pVar) = NULL;
HRESULT hrReturn = S_OK;
// 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 ((vt != 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;
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
if (m_pRequest->m_pData->m_fLoadQuery)
{
if (FAILED(m_pRequest->LoadVariables(QUERYSTRING, m_pRequest->GetIReq()->QueryPszQueryString(), m_pRequest->GetCodePage())))
{
hrReturn = E_FAIL;
goto LExit;
}
m_pRequest->m_pData->m_fLoadQuery = FALSE;
}
if (vt == VT_BSTR)
{
// convert BSTR version to ANSI version of the key using current Session.CodePage
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey),m_pRequest->GetCodePage()))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = convKey.GetString();
}
pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
}
else
{
int iCount;
iCount = V_I4(pvarKey);
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_dwCount)) || ((iCount > 0) && ((int) m_dwCount == 0)))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pRequestHit = m_rgRequestHit[iCount - 1];
}
if (pRequestHit)
{
// Create a BSTR containing the key for this variant
BSTR bstrT = NULL;
SysAllocStringFromSz((char *)pRequestHit->m_pKey,0,&bstrT,m_pRequest->GetCodePage());
if (!bstrT)
return E_OUTOFMEMORY;
V_BSTR(pVar) = bstrT;
}
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CQueryString::get_Count
Parameters:
pcValues - count is stored in *pcValues
===================================================================*/
STDMETHODIMP CQueryString::get_Count(int *pcValues)
{
HRESULT hrReturn = S_OK;
if (m_pRequest->m_pData->m_fLoadQuery)
{
if (FAILED(hrReturn = m_pRequest->LoadVariables(QUERYSTRING, m_pRequest->GetIReq()->QueryPszQueryString(), m_pRequest->GetCodePage())))
{
goto LExit;
}
m_pRequest->m_pData->m_fLoadQuery = FALSE;
}
*pcValues = m_dwCount;
LExit:
return hrReturn;
}
/*===================================================================
CQueryString::get__NewEnum
Return a new enumerator
===================================================================*/
HRESULT CQueryString::get__NewEnum(IUnknown **ppEnumReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
return m_pRequest->GetRequestEnumerator(QUERYSTRING, ppEnumReturn);
}
/*------------------------------------------------------------------
* C F o r m I n p u t s
*/
/*===================================================================
CFormInputs::CFormInputs
Constructor
Parameters:
pRequest Pointer to main Request object
pUnkOuter LPUNKNOWN of a controlling unknown.
Returns:
Nothing.
Note:
This object is NOT ref counted since it is created & destroyed
automatically by C++.
===================================================================*/
CFormInputs::CFormInputs(CRequest *pRequest, IUnknown *pUnkOuter)
: m_ISupportErrImp(this, pUnkOuter, IID_IRequestDictionary)
{
m_punkOuter = pUnkOuter;
if (pRequest)
pRequest->AddRef();
m_pRequest = pRequest;
CDispatch::Init(IID_IRequestDictionary);
}
/*===================================================================
CFormInputs::CFormInputs
Destructor
Parameters:
None
Returns:
Nothing.
===================================================================*/
CFormInputs::~CFormInputs()
{
if (m_pRequest)
m_pRequest->Release();
}
/*===================================================================
HRESULT CFormInputs::Init
Parameters:
None
Returns:
E_OUTOFMEMORY if allocation fails.
===================================================================*/
HRESULT CFormInputs::Init()
{
return CRequestHitsArray::Init();
}
/*===================================================================
HRESULT CFormInputs::ReInit
Parameters:
None
Returns:
S_OK
===================================================================*/
HRESULT CFormInputs::ReInit()
{
return CRequestHitsArray::ReInit();
}
/*===================================================================
CFormInputs::QueryInterface
CFormInputs::AddRef
CFormInputs::Release
IUnknown members for CFormInputs object.
===================================================================*/
STDMETHODIMP CFormInputs::QueryInterface(REFIID iid, void **ppvObj)
{
*ppvObj = NULL;
if (iid == IID_IUnknown || iid == IID_IRequestDictionary || iid == IID_IDispatch)
*ppvObj = this;
else if (iid == IID_ISupportErrorInfo)
*ppvObj = &m_ISupportErrImp;
if (*ppvObj != NULL)
{
static_cast<IUnknown *>(*ppvObj)->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CFormInputs::AddRef(void)
{
return m_punkOuter->AddRef();
}
STDMETHODIMP_(ULONG) CFormInputs::Release(void)
{
return m_punkOuter->Release();
}
/*===================================================================
CFormInputs::get_Item
Function called from DispInvoke to get values from the QueryString collection.
Parameters:
vKey VARIANT [in], which parameter to get the value of - Empty means whole collection
pvarReturn VARIANT *, [out] value of the requested parameter
Returns:
S_OK on success, S_OK if key not found, E_FAIL on failure.
===================================================================*/
HRESULT CFormInputs::get_Item(VARIANT varKey, VARIANT *pvarReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey; // ascii version of the key
CWCharToMBCS convKey;
IDispatch *pSListReturn; // value of the key
CRequestHit *pRequestHit; // pointer to request bucket
BOOL fDataAvail = FALSE; // true if data seen from client
// Initialize things
//
VariantInit(pvarReturn);
VARIANT *pvarKey = &varKey;
HRESULT hrReturn = S_OK;
// If BinaryRead has been called, the Form collection is no longer available
if (m_pRequest->m_pData->m_FormDataStatus != AVAILABLE &&
m_pRequest->m_pData->m_FormDataStatus != FORMCOLLECTIONONLY)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_REQUEST_FORMCOLLECTION_NA);
hrReturn = E_FAIL;
}
if (m_pRequest->m_pData->m_FormDataStatus == AVAILABLE)
m_pRequest->m_pData->m_FormDataStatus = FORMCOLLECTIONONLY;
// 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 ((vt != 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);
if (m_pRequest->m_pData->m_fLoadForm)
{
if (FAILED(hrReturn = m_pRequest->CopyClientData()))
goto LExit;
if (FAILED(hrReturn = m_pRequest->LoadVariables(FORM, m_pRequest->m_pData->m_szFormData, m_pRequest->GetCodePage())))
goto LExit;
// BUG:895 (JHITTLE) added to check for null result set
// this fixes the out of memory error when the form
// data is NULL
//
fDataAvail = (m_pRequest->m_pData->m_szFormData != NULL);
m_pRequest->m_pData->m_fLoadForm = FALSE;
}
else
fDataAvail = (m_pRequest->m_pData->m_szFormData != NULL);
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)
{
if (fDataAvail)
{
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(m_pRequest->m_pData->m_szFormClone, 0, &bstrT,m_pRequest->GetCodePage())))
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
}
V_VT(pvarReturn) = VT_BSTR;
V_BSTR(pvarReturn) = bstrT;
}
// If there was no data available, status & return value are already set
goto LExit;
}
// Other error, FALL THROUGH to wrong type case
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
pRequestHit = NULL;
if (! fDataAvail) // Quick check before we do an expensive lookup
goto LNotFound;
if (vt == VT_BSTR)
{
// convert BSTR version to ANSI version of the key using current Session.CodePage
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey), m_pRequest->GetCodePage()))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = convKey.GetString();
}
pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
}
else
{
// Look up item by index
int iCount;
iCount = V_I4(pvarKey);
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_dwCount)) || ((iCount > 0) && ((int) m_dwCount == 0)))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pRequestHit = m_rgRequestHit[iCount - 1];
}
if (pRequestHit)
{
CStringList *pValues = pRequestHit->m_pFormData;
if (pValues == NULL)
goto LNotFound;
if (FAILED(pValues->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&pSListReturn))))
Assert (FALSE);
V_VT(pvarReturn) = VT_DISPATCH;
V_DISPATCH(pvarReturn) = pSListReturn;
goto LExit;
}
LNotFound: // Return "Empty"
if(vt != VT_BSTR)
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
if (FAILED(m_pRequest->m_pData->GetEmptyStringList(&pSListReturn)))
hrReturn = E_FAIL;
V_VT(pvarReturn) = VT_DISPATCH;
V_DISPATCH(pvarReturn) = pSListReturn;
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CFormInputs::get_Count
Parameters:
pcValues - count is stored in *pcValues
===================================================================*/
STDMETHODIMP CFormInputs::get_Count(int *pcValues)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
HRESULT hrReturn = S_OK;
// If BinaryRead has been called, the Form collection is no longer available
if (m_pRequest->m_pData->m_FormDataStatus != AVAILABLE &&
m_pRequest->m_pData->m_FormDataStatus != FORMCOLLECTIONONLY)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_REQUEST_FORMCOLLECTION_NA);
hrReturn = E_FAIL;
}
if (m_pRequest->m_pData->m_FormDataStatus == AVAILABLE)
m_pRequest->m_pData->m_FormDataStatus = FORMCOLLECTIONONLY;
if (m_pRequest->m_pData->m_fLoadForm)
{
if (FAILED(hrReturn = m_pRequest->CopyClientData()))
goto LExit;
if (FAILED(hrReturn = m_pRequest->LoadVariables(FORM, m_pRequest->m_pData->m_szFormData, m_pRequest->GetCodePage())))
goto LExit;
m_pRequest->m_pData->m_fLoadForm = FALSE;
}
*pcValues = m_dwCount;
LExit:
return hrReturn;
}
/*===================================================================
CFormInputs::get_Key
Function called from DispInvoke to get keys from the form inputs 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.
===================================================================*/
HRESULT CFormInputs::get_Key(VARIANT varKey, VARIANT *pVar)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey; // ascii version of the key
CWCharToMBCS convKey;
CRequestHit *pRequestHit; // pointer to request bucket
IDispatch *pSListReturn; // value of the key
// Initialize things
//
VariantInit(pVar);
VARIANT *pvarKey = &varKey;
V_VT(pVar) = VT_BSTR;
V_BSTR(pVar) = NULL;
HRESULT hrReturn = S_OK;
// 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 ((vt != 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;
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
if (m_pRequest->m_pData->m_fLoadForm)
{
if (FAILED(hrReturn = m_pRequest->CopyClientData()))
goto LExit;
if (FAILED(hrReturn = m_pRequest->LoadVariables(FORM, m_pRequest->m_pData->m_szFormData, m_pRequest->GetCodePage())))
{
goto LExit;
}
m_pRequest->m_pData->m_fLoadForm = FALSE;
}
if (vt == VT_BSTR)
{
// convert BSTR version to ANSI version of the key using current Session.CodePage
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey), m_pRequest->GetCodePage()))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = convKey.GetString();
}
pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
}
else
{
int iCount;
iCount = V_I4(pvarKey);
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_dwCount)) || ((iCount > 0) && ((int) m_dwCount == 0)))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pRequestHit = m_rgRequestHit[iCount - 1];
}
if (pRequestHit)
{
// Create a BSTR containing the key for this variant
BSTR bstrT = NULL;
SysAllocStringFromSz((char *)pRequestHit->m_pKey,0,&bstrT,m_pRequest->GetCodePage());
if (!bstrT)
return E_OUTOFMEMORY;
V_BSTR(pVar) = bstrT;
}
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CFormInputs::get__NewEnum
Return a new enumerator
===================================================================*/
HRESULT CFormInputs::get__NewEnum(IUnknown **ppEnumReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
return m_pRequest->GetRequestEnumerator(FORM, ppEnumReturn);
}
/*------------------------------------------------------------------
* C C o o k i e s
*/
/*===================================================================
CCookies::CCookies
Constructor
Parameters:
pRequest Pointer to main Request object
pUnkOuter LPUNKNOWN of a controlling unknown.
Returns:
Nothing.
Note:
This object is NOT ref counted since it is created & destroyed
automatically by C++.
===================================================================*/
CCookies::CCookies(CRequest *pRequest, IUnknown *pUnkOuter)
: m_ISupportErrImp(this, pUnkOuter, IID_IRequestDictionary)
{
m_punkOuter = pUnkOuter;
if (pRequest)
pRequest->AddRef();
m_pRequest = pRequest;
m_pEmptyCookie = NULL;
CDispatch::Init(IID_IRequestDictionary);
}
/*===================================================================
CCookies::CCookies
Destructor
Note:
This object is NOT ref counted since it is created & destroyed
automatically by C++.
===================================================================*/
CCookies::~CCookies()
{
if (m_pRequest)
m_pRequest->Release();
if (m_pEmptyCookie)
m_pEmptyCookie->Release();
}
/*===================================================================
CCookies::Init
Initializer
Parameters:
None
Returns:
Nothing.
===================================================================*/
HRESULT CCookies::Init()
{
return CRequestHitsArray::Init();
}
/*===================================================================
HRESULT CCookies::ReInit
Parameters:
None
Returns:
S_OK
===================================================================*/
HRESULT CCookies::ReInit()
{
return CRequestHitsArray::ReInit();
}
/*===================================================================
CCookies::QueryInterface
CCookies::AddRef
CCookies::Release
IUnknown members for CQueryString object.
===================================================================*/
STDMETHODIMP CCookies::QueryInterface(REFIID iid, void **ppvObj)
{
*ppvObj = NULL;
if (iid == IID_IUnknown || iid == IID_IRequestDictionary || iid == IID_IDispatch)
*ppvObj = this;
else if (iid == IID_ISupportErrorInfo)
*ppvObj = &m_ISupportErrImp;
if (*ppvObj != NULL)
{
static_cast<IUnknown *>(*ppvObj)->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CCookies::AddRef(void)
{
return m_punkOuter->AddRef();
}
STDMETHODIMP_(ULONG) CCookies::Release(void)
{
return m_punkOuter->Release();
}
/*===================================================================
CCookies::get_Item
Function called from DispInvoke to get values from the Cookies collection.
Parameters:
vKey VARIANT [in], which parameter to get the value of - Empty means whole collection
pvarReturn VARIANT *, [out] value of the requested parameter
Returns:
S_OK on success, E_FAIL on failure.
===================================================================*/
HRESULT CCookies::get_Item(VARIANT varKey, VARIANT *pvarReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey; // ascii version of the key
CRequestHit *pRequestHit; // pointer to request bucket
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 ((vt != 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);
if (m_pRequest->m_pData->m_fLoadCookies)
{
char *szCookie = m_pRequest->GetIReq()->QueryPszCookie();
if (FAILED(hrReturn = m_pRequest->LoadVariables(COOKIE, szCookie, m_pRequest->GetCodePage())))
goto LExit;
m_pRequest->m_pData->m_fLoadCookies = FALSE;
}
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)
{
// Dynamically construct value of HTTP_COOKIE.
//
// Step 1: figure out how much space we need
//
int cbHTTPCookie = 1; // At the least we will need space for '\0'
for (pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->Head());
pRequestHit != NULL;
pRequestHit = static_cast<CRequestHit *>(pRequestHit->m_pNext))
{
CCookie *pCookie = pRequestHit->m_pCookieData;
if (pCookie)
cbHTTPCookie += pCookie->GetHTTPCookieSize() + pRequestHit->m_cbKey + 1;
}
// Allocate space for the HTTP_COOKIE value
//
if (cbHTTPCookie > REQUEST_ALLOC_MAX)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_STACK_OVERFLOW);
hrReturn = E_FAIL;
goto LExit;
}
if (tempCookie.Resize(cbHTTPCookie) == FALSE) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
char *szHTTPCookie = static_cast<char *>(tempCookie.QueryPtr());
// Step 2: create the value of HTTP_COOKIE
//
char *szDest = szHTTPCookie;
for (pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->Head());
pRequestHit != NULL;
pRequestHit = static_cast<CRequestHit *>(pRequestHit->m_pNext))
{
CCookie *pCookie = pRequestHit->m_pCookieData;
if (pCookie)
{
strcpy(szDest, reinterpret_cast<char *>(pRequestHit->m_pKey));
szDest = strchr(szDest, '\0');
*szDest++ = '=';
szDest = pCookie->GetHTTPCookie(szDest);
if (pRequestHit->m_pNext)
*szDest++ = ';';
}
}
*szDest = '\0';
// Now we have the value, so return it.
//
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(szHTTPCookie, 0, &bstrT, m_pRequest->GetCodePage())))
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
V_VT(pvarReturn) = VT_BSTR;
V_BSTR(pvarReturn) = bstrT;
goto LExit;
}
// Other error, FALL THROUGH to wrong type case
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
V_VT(pvarReturn) = VT_DISPATCH;
V_DISPATCH(pvarReturn) = NULL;
if (vt == VT_BSTR)
{
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey),m_pRequest->GetCodePage()))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = convKey.GetString();
}
pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
}
else
{
// Look up item by index
int iCount;
iCount = V_I4(pvarKey);
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_dwCount)) || ((iCount > 0) && ((int) m_dwCount == 0)))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pRequestHit = m_rgRequestHit[iCount - 1];
}
if (pRequestHit)
{
CCookie *pDictionary = pRequestHit->m_pCookieData;
if (pDictionary == NULL)
goto LNotFound;
if (FAILED(pDictionary->QueryInterface(IID_IReadCookie, reinterpret_cast<void **>(&V_DISPATCH(pvarReturn)))))
Assert (FALSE);
goto LExit;
}
LNotFound: // Return Empty Cookie
if (!m_pEmptyCookie)
{
// create on demand
if ((m_pEmptyCookie = new CCookie(m_pRequest->GetIReq(), m_pRequest->GetCodePage())) != NULL)
hrReturn = m_pEmptyCookie->Init();
else
hrReturn = E_OUTOFMEMORY;
}
if (m_pEmptyCookie)
hrReturn = m_pEmptyCookie->QueryInterface(IID_IReadCookie, reinterpret_cast<void **>(&V_DISPATCH(pvarReturn)));
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CCookies::get_Key
Function called from DispInvoke to get keys from the 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.
===================================================================*/
HRESULT CCookies::get_Key(VARIANT varKey, VARIANT *pVar)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey; // ascii version of the key
CWCharToMBCS convKey;
CRequestHit *pRequestHit; // pointer to request bucket
IDispatch *pSListReturn; // value of the key
// Initialize things
//
VariantInit(pVar);
VARIANT *pvarKey = &varKey;
V_VT(pVar) = VT_BSTR;
V_BSTR(pVar) = NULL;
HRESULT hrReturn = S_OK;
// 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 ((vt != 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;
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
if (m_pRequest->m_pData->m_fLoadCookies)
{
char *szCookie = m_pRequest->GetIReq()->QueryPszCookie();
if (FAILED(hrReturn = m_pRequest->LoadVariables(COOKIE, szCookie, m_pRequest->GetCodePage())))
goto LExit;
m_pRequest->m_pData->m_fLoadCookies = FALSE;
}
if (vt == VT_BSTR)
{
// convert BSTR version to ANSI version of the key using current Session.CodePage
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey), m_pRequest->GetCodePage()))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = convKey.GetString();
}
pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
}
else
{
int iCount;
iCount = V_I4(pvarKey);
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_dwCount)) || ((iCount > 0) && ((int) m_dwCount == 0)))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pRequestHit = m_rgRequestHit[iCount - 1];
}
if (pRequestHit)
{
// Create a BSTR containing the key for this variant
BSTR bstrT = NULL;
SysAllocStringFromSz((char *)pRequestHit->m_pKey,0,&bstrT,m_pRequest->GetCodePage());
if (!bstrT)
return E_OUTOFMEMORY;
V_BSTR(pVar) = bstrT;
}
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CCookies::get_Count
Parameters:
pcValues - count is stored in *pcValues
===================================================================*/
STDMETHODIMP CCookies::get_Count(int *pcValues)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
HRESULT hrReturn = S_OK;
if (m_pRequest->m_pData->m_fLoadCookies)
{
char *szCookie = m_pRequest->GetIReq()->QueryPszCookie();
if (FAILED(hrReturn = m_pRequest->LoadVariables(COOKIE, szCookie, m_pRequest->GetCodePage())))
goto LExit;
m_pRequest->m_pData->m_fLoadCookies = FALSE;
}
*pcValues = m_dwCount;
LExit:
return hrReturn;
}
/*===================================================================
CCookies::get__NewEnum
Return a new enumerator
===================================================================*/
HRESULT CCookies::get__NewEnum(IUnknown **ppEnumReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
return m_pRequest->GetRequestEnumerator(COOKIE, ppEnumReturn);
}
/*------------------------------------------------------------------
* C C l C e r t s
*/
/*===================================================================
CClCerts::CClCerts
Constructor
Parameters:
pRequest Pointer to main Request object
pUnkOuter LPUNKNOWN of a controlling unknown.
Returns:
Nothing.
Note:
This object is NOT ref counted since it is created & destroyed
automatically by C++.
===================================================================*/
CClCerts::CClCerts(CRequest *pRequest, IUnknown *pUnkOuter)
: m_ISupportErrImp(this, pUnkOuter, IID_IRequestDictionary)
{
m_punkOuter = pUnkOuter;
if (pRequest)
pRequest->AddRef();
m_pRequest = pRequest;
m_pEmptyClCert = NULL;
CDispatch::Init(IID_IRequestDictionary);
}
/*===================================================================
CClCerts::ClCerts
Destructor
Note:
This object is NOT ref counted since it is created & destroyed
automatically by C++.
===================================================================*/
CClCerts::~CClCerts()
{
if (m_pRequest)
m_pRequest->Release();
if (m_pEmptyClCert)
m_pEmptyClCert->Release();
}
/*===================================================================
CClCerts::Init
Initializer
Parameters:
None
Returns:
Nothing.
===================================================================*/
HRESULT CClCerts::Init()
{
return CRequestHitsArray::Init();
}
/*===================================================================
CClCerts::ReInit
Parameters:
None
Returns:
S_OK
===================================================================*/
HRESULT CClCerts::ReInit()
{
return CRequestHitsArray::ReInit();
}
/*===================================================================
CClCerts::QueryInterface
CClCerts::AddRef
CClCerts::Release
IUnknown members for CQueryString object.
===================================================================*/
STDMETHODIMP CClCerts::QueryInterface(REFIID riid, void **ppv)
{
*ppv = NULL;
if (riid == IID_IUnknown || riid == IID_IRequestDictionary || riid == IID_IDispatch)
*ppv = this;
else if (riid == IID_ISupportErrorInfo)
*ppv = &m_ISupportErrImp;
if (*ppv != NULL)
{
static_cast<IUnknown *>(*ppv)->AddRef();
return S_OK;
}
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP_(ULONG) CClCerts::AddRef(void)
{
return m_punkOuter->AddRef();
}
STDMETHODIMP_(ULONG) CClCerts::Release(void)
{
return m_punkOuter->Release();
}
/*===================================================================
CClCerts::get_Item
Function called from DispInvoke to get values from the ClCerts collection.
Parameters:
vKey VARIANT [in], which parameter to get the value of - Empty means whole collection
pvarReturn VARIANT *, [out] value of the requested parameter
Returns:
S_OK on success, S_FALSE if key not found, E_FAIL on failure.
===================================================================*/
HRESULT CClCerts::get_Item(VARIANT varKey, VARIANT *pvarReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey; // ascii version of the key
CRequestHit *pRequestHit; // pointer to request bucket
CWCharToMBCS convKey;
// 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 ((vt != 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);
if (m_pRequest->m_pData->m_fLoadClCerts)
{
if (FAILED(hrReturn = m_pRequest->LoadVariables(CLCERT, reinterpret_cast<char *>(m_pRequest->GetIReq()), m_pRequest->GetCodePage())))
goto LExit;
m_pRequest->m_pData->m_fLoadClCerts = FALSE;
}
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)
{
// Dynamically construct value of CLCERT
//
// Step 1: figure out how much space we need
//
int cbHTTPClCert = 1;
for (pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->Head());
pRequestHit != NULL;
pRequestHit = static_cast<CRequestHit *>(pRequestHit->m_pNext))
{
CClCert *pClCert = pRequestHit->m_pClCertData;
if (pClCert)
cbHTTPClCert += pClCert->GetHTTPClCertSize() + pRequestHit->m_cbKey + 1;
}
STACK_BUFFER( tempClCert, 256);
if (!tempClCert.Resize(cbHTTPClCert)) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
char *szHTTPClCert = static_cast<char *>(tempClCert.QueryPtr());
// Step 2: create the value of CLCERT
//
char *szDest = szHTTPClCert;
for (pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->Head());
pRequestHit != NULL;
pRequestHit = static_cast<CRequestHit *>(pRequestHit->m_pNext))
{
CClCert *pClCert = pRequestHit->m_pClCertData;
if (pClCert)
{
strcpy(szDest, reinterpret_cast<char *>(pRequestHit->m_pKey));
szDest = strchr(szDest, '\0');
*szDest++ = '=';
szDest = pClCert->GetHTTPClCert(szDest);
if (pRequestHit->m_pNext)
*szDest++ = ';';
}
}
*szDest = '\0';
// Now we have the value, so return it.
//
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(szHTTPClCert, 0, &bstrT)))
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
V_VT(pvarReturn) = VT_BSTR;
V_BSTR(pvarReturn) = bstrT;
goto LExit;
}
// Other error, FALL THROUGH to wrong type case
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
V_VT(pvarReturn) = VT_DISPATCH;
V_DISPATCH(pvarReturn) = NULL;
if (vt == VT_BSTR)
{
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey)))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = convKey.GetString();
}
pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
}
else
{
// Look up item by index
int iCount;
iCount = V_I4(pvarKey);
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_dwCount)) || ((iCount > 0) && ((int) m_dwCount == 0)))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pRequestHit = m_rgRequestHit[iCount - 1];
}
if (pRequestHit)
{
CClCert *pDictionary = pRequestHit->m_pClCertData;
if (pDictionary == NULL)
goto LNotFound;
if (FAILED(pDictionary->QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(&V_DISPATCH(pvarReturn)))))
Assert (FALSE);
goto LExit;
}
LNotFound: // Return "Empty"
if (!m_pEmptyClCert)
{
// create on demand
if ((m_pEmptyClCert = new CClCert) != NULL)
hrReturn = m_pEmptyClCert->Init();
else
hrReturn = E_OUTOFMEMORY;
}
if (m_pEmptyClCert)
hrReturn = m_pEmptyClCert->QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(&V_DISPATCH(pvarReturn)));
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CClCerts::get_Key
Function called from DispInvoke to get keys from the certificate 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.
===================================================================*/
HRESULT CClCerts::get_Key(VARIANT varKey, VARIANT *pVar)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey; // ascii version of the key
CWCharToMBCS convKey;
CRequestHit *pRequestHit; // pointer to request bucket
// Initialize things
//
VariantInit(pVar);
VARIANT *pvarKey = &varKey;
V_VT(pVar) = VT_BSTR;
V_BSTR(pVar) = NULL;
HRESULT hrReturn = S_OK;
// 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 ((vt != 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;
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
if (m_pRequest->m_pData->m_fLoadClCerts)
{
if (FAILED(hrReturn = m_pRequest->LoadVariables(CLCERT, reinterpret_cast<char *>(m_pRequest->GetIReq()), m_pRequest->GetCodePage())))
{
goto LExit;
}
m_pRequest->m_pData->m_fLoadClCerts = FALSE;
}
if (vt == VT_BSTR)
{
// convert BSTR version to ANSI version of the key using current Session.CodePage
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey), m_pRequest->GetCodePage()))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = convKey.GetString();
}
pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
}
else
{
int iCount;
iCount = V_I4(pvarKey);
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_dwCount)) || ((iCount > 0) && ((int) m_dwCount == 0)))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
pRequestHit = m_rgRequestHit[iCount - 1];
}
if (pRequestHit)
{
// Create a BSTR containing the key for this variant
BSTR bstrT = NULL;
SysAllocStringFromSz((char *)pRequestHit->m_pKey,0,&bstrT,m_pRequest->GetCodePage());
if (!bstrT)
return E_OUTOFMEMORY;
V_BSTR(pVar) = bstrT;
}
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CClCerts::get_Count
Parameters:
pcValues - count is stored in *pcValues
===================================================================*/
STDMETHODIMP CClCerts::get_Count(int *pcValues)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
HRESULT hrReturn = S_OK;
if (m_pRequest->m_pData->m_fLoadClCerts)
{
if (FAILED(hrReturn = m_pRequest->LoadVariables(CLCERT, reinterpret_cast<char *>(m_pRequest->GetIReq()), m_pRequest->GetCodePage())))
{
goto LExit;
}
m_pRequest->m_pData->m_fLoadClCerts = FALSE;
}
*pcValues = m_dwCount;
LExit:
return hrReturn;
}
/*===================================================================
CClCerts::get__NewEnum
Return a new enumerator
===================================================================*/
HRESULT CClCerts::get__NewEnum(IUnknown **ppEnumReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
return m_pRequest->GetRequestEnumerator(CLCERT, ppEnumReturn);
}
/*------------------------------------------------------------------
* C S e r v e r V a r i a b l e s
*/
/*===================================================================
CServerVariables::CServerVariables
Constructor
Parameters:
pRequest Pointer to main Request object
pUnkOuter LPUNKNOWN of a controlling unknown.
Returns:
Nothing.
Note:
This object is NOT ref counted since it is created & destroyed
automatically by C++.
===================================================================*/
CServerVariables::CServerVariables(CRequest *pRequest, IUnknown *pUnkOuter)
: m_ISupportErrImp(this, pUnkOuter, IID_IRequestDictionary),
m_pIterator(NULL)
{
m_punkOuter = pUnkOuter;
if (pRequest)
pRequest->AddRef();
m_pRequest = pRequest;
CDispatch::Init(IID_IRequestDictionary);
}
/*===================================================================
CServerVariables::~CServerVariables
Destructor
Parameters:
None
Returns:
Nothing.
Note:
This object is NOT ref counted since it is created & destroyed
automatically by C++.
===================================================================*/
CServerVariables::~CServerVariables( )
{
if (m_pRequest)
m_pRequest->Release();
if (m_pIterator)
m_pIterator->Release();
}
/*===================================================================
CServerVariables::QueryInterface
CServerVariables::AddRef
CServerVariables::Release
IUnknown members for CFormInputs object.
===================================================================*/
STDMETHODIMP CServerVariables::QueryInterface(REFIID iid, void **ppvObj)
{
*ppvObj = NULL;
if (iid == IID_IUnknown || iid == IID_IRequestDictionary || iid == IID_IDispatch)
*ppvObj = this;
else if (iid == IID_ISupportErrorInfo)
*ppvObj = &m_ISupportErrImp;
if (*ppvObj != NULL)
{
static_cast<IUnknown *>(*ppvObj)->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CServerVariables::AddRef(void)
{
return m_punkOuter->AddRef();
}
STDMETHODIMP_(ULONG) CServerVariables::Release(void)
{
return m_punkOuter->Release();
}
/*===================================================================
CServerVariables::get_Item
Function called from DispInvoke to get values from the ServerVariables
collection.
Parameters:
vKey VARIANT [in], which parameter to get the value of
pvarReturn VARIANT *, [out] value of the requested parameter
Returns:
S_OK on success, E_FAIL on failure.
NOTE:
This code is basically an enacpsulation from the SERVER_GET macro,
only more efficient, since it only looks up the key once on average
unfortunately, the only way to get good memory utilization with
ISAPI is to use _alloca() with lookups, which means we can't
encapsulate the lookup logic very well.
===================================================================*/
HRESULT CServerVariables::get_Item(VARIANT varKey, VARIANT *pvarReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
DWORD dwValSize; // buffer size
char *szKey; // pointer to ASCII value of varKey
char *szValue;
WCHAR *wszValue;
BOOL fSuccess; // TRUE when call to GetServerVariable succeeds
UINT uCodePage = GetACP();
CWCharToMBCS convKey;
BOOL fUnicodeVar = FALSE;
STACK_BUFFER( tempVal, 128 );
dwValSize = tempVal.QuerySize();
szValue = (char *)tempVal.QueryPtr();
wszValue = (WCHAR *)tempVal.QueryPtr();
// 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 ((vt != 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);
V_VT(pvarReturn) = VT_DISPATCH;
V_DISPATCH(pvarReturn) = NULL; // initial value of Nothing
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) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_NOT_ALLOWED);
hrReturn = E_FAIL;
goto LExit;
}
// Other error, FALL THROUGH to wrong type case
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
uCodePage = m_pRequest->GetCodePage();
if (vt == VT_BSTR) {
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey), uCodePage))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = CharUpperA(convKey.GetString());
}
}
else {
// Look up item by index
int iCount;
iCount = V_I4(pvarKey);
// We use the CServVarsIterator to manange
// the count of sv and integer index
if (!m_pIterator) {
m_pIterator = new CServVarsIterator;
if (!m_pIterator) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
if (FAILED (hrReturn = m_pIterator->Init(m_pRequest->m_pData->m_pIReq)))
goto LExit;
}
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_pIterator->m_cKeys)) || ((iCount > 0) && ((int) m_pIterator->m_cKeys == 0))) {
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
if (FAILED(hrReturn = convKey.Init(m_pIterator->m_rgwszKeys[iCount - 1], uCodePage))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = CharUpperA(convKey.GetString());
}
}
if (strncmp(convKey.GetString(), "UNICODE_", 7) == 0) {
fSuccess = false;
goto SkipLookup;
}
// in IIS6, there are a number of variables that are UNICODE. To
// access them, you simply place UNICODE_ infront of the name.
// Two approaches could be taken here. One would be to always
// try for a UNICODE_ var and fallback to the non-UNICODE var
// if the lookup fails. This can be costly. The second, and
// chosen method here, would be to maintain a list of vars
// that have UNICODE_ versions.
// this char array is declared on the stack and is currently only
// 32 chars. It only needs to be as big as the largest UNICODE
// var name. Which is UNICODE_UNMAPPED_REMOTE_USER.
char szUNICODEName[32];
// search the list to see if this is one of the UNICODE_ vars.
// the list is sorted by length of string. The current list is
// not all that long, so a sequential search is not that expensive
// in the scheme of things.
for (int i=0;
(g_sUNICODEVars[i].varLen != -1)
&& (convKey.GetStringLen() >= g_sUNICODEVars[i].varLen);
i++) {
// the 'for' loop allows in anything which is at least as long
// as the current entry. The following 'if' will check for
// for an exact length match and then a string compare.
if ((convKey.GetStringLen() == g_sUNICODEVars[i].varLen)
&& (strcmp(convKey.GetString(), g_sUNICODEVars[i].szVarName) == 0)) {
// if a hit is made, set the fUnicodeVar = TRUE so that the
// right ISAPI lookup routine is called and the right StringList
// AddValue is called.
fUnicodeVar = TRUE;
// build up the UNICODE_ version into the stack temp array
strcpyExA(strcpyExA(szUNICODEName,"UNICODE_"),convKey.GetString());
// reassign the key name to this value
szKey = szUNICODEName;
break;
}
}
fSuccess = fUnicodeVar
? m_pRequest->GetIReq()->GetServerVariableW(szKey, wszValue, &dwValSize)
: m_pRequest->GetIReq()->GetServerVariableA(szKey, szValue, &dwValSize);
if (!fSuccess && (dwValSize > tempVal.QuerySize())) {
if (dwValSize > REQUEST_ALLOC_MAX) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_STACK_OVERFLOW);
hrReturn = E_FAIL;
goto LExit;
}
if (tempVal.Resize(dwValSize) == FALSE) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
szValue = static_cast<char *>(tempVal.QueryPtr());
wszValue = static_cast<WCHAR *>(tempVal.QueryPtr());
fSuccess = fUnicodeVar
? m_pRequest->GetIReq()->GetServerVariableW(szKey, wszValue, &dwValSize)
: m_pRequest->GetIReq()->GetServerVariableA(szKey, szValue, &dwValSize);
}
SkipLookup:
if (fSuccess) {
// Create return value
CStringList *pValue = new CStringList;
if (pValue == NULL) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
// add the value and QueryInterface for IDispatch interface - strdup the input string
if (FAILED(hrReturn = (fUnicodeVar
? pValue->AddValue(wszValue, TRUE)
: pValue->AddValue(szValue, TRUE, uCodePage))))
goto LExit;
if (FAILED(pValue->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&V_DISPATCH(pvarReturn)))))
Assert (FALSE);
// Release temporary (QueryInterface AddRef'd)
pValue->Release();
goto LExit;
}
else {
if (FAILED(m_pRequest->m_pData->GetEmptyStringList(&V_DISPATCH(pvarReturn))))
hrReturn = E_FAIL;
}
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CServerVariables::get_Key
Function called from DispInvoke to get keys from the server variables 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.
===================================================================*/
HRESULT CServerVariables::get_Key(VARIANT varKey, VARIANT *pVar)
{
HRESULT hrReturn = S_OK;
int iCount = 0;
BSTR bstrT = NULL;
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
char *szKey; // ascii version of the key
CRequestHit *pRequestHit; // pointer to request bucket
IDispatch *pSListReturn; // value of the key
CWCharToMBCS convKey;
// Initialize things
//
VariantInit(pVar);
VARIANT *pvarKey = &varKey;
V_VT(pVar) = VT_BSTR;
V_BSTR(pVar) = NULL;
// 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 ((vt != 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;
vt = V_VT(pvarKey);
// fallthru to VT_I4
case VT_I4:
case VT_BSTR:
break;
default:
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
hrReturn = E_FAIL;
goto LExit;
}
// At this point the VT of pvarKey should be VT_I4 or VT_BSTR
Assert((vt == VT_I4) || (vt == VT_BSTR));
if (vt == VT_I4)
{
// We were passed in a number.
// Look up the key by integer index
iCount = V_I4(pvarKey);
// We use the CServVarsIterator to manange
// the count of sv and integer index
if (!m_pIterator)
{
m_pIterator = new CServVarsIterator;
if (!m_pIterator)
{
hrReturn = E_OUTOFMEMORY;
goto LExit;
}
if (FAILED(hrReturn = m_pIterator->Init(m_pRequest->m_pData->m_pIReq)))
goto LExit;
}
// BUG 86117 test passes when m_dwCount == 0
if ( ((iCount < 1) || (iCount > (int) m_pIterator->m_cKeys)) || ((iCount > 0) && ((int) m_pIterator->m_cKeys == 0)))
{
hrReturn = E_FAIL;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_BAD_ARRAY_INDEX);
goto LExit;
}
// Create a BSTR containing the key for this variant
bstrT = SysAllocString(m_pIterator->m_rgwszKeys[iCount - 1]);
if (!bstrT)
{
hrReturn = E_OUTOFMEMORY;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
}
else
{
// We were passed in a BSTR. Check to see if there
// is a server variable for this key
char szBuffer;
DWORD dwValSize = sizeof(szBuffer);
UINT uCodePage = m_pRequest->GetCodePage();
if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey), uCodePage))) {
if (hrReturn == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
hrReturn = NO_ERROR;
szKey = "";
}
else {
szKey = CharUpperA(convKey.GetString());
}
BOOL fSuccess = m_pRequest->GetIReq()->GetServerVariableA(szKey, &szBuffer, &dwValSize);
DWORD dwError = 0;
if (!fSuccess)
{
dwError = GetLastError();
}
// If the error was that we had insufficient buffer then
// there is a server variable for that key
if (fSuccess || dwError == ERROR_INSUFFICIENT_BUFFER)
{
bstrT = SysAllocString(V_BSTR(pvarKey));
if (!bstrT)
{
hrReturn = E_OUTOFMEMORY;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
goto LExit;
}
}
else if (dwError != ERROR_INVALID_INDEX)
{
// Any other error indicates an unexpected failure
hrReturn = HRESULT_FROM_WIN32(dwError);
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_UNEXPECTED);
goto LExit;
}
}
// If we found a key, copy it into the out parmater
if (bstrT)
{
V_BSTR(pVar) = bstrT;
}
LExit:
VariantClear(&varKeyCopy);
return hrReturn;
}
/*===================================================================
CServerVariables::get_Count
Parameters:
pcValues - count is stored in *pcValues
===================================================================*/
STDMETHODIMP CServerVariables::get_Count(int *pcValues)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
HRESULT hrReturn = S_OK;
// We use the CServVarsIterator to manange
// the count of sv and integer index
if (!m_pIterator)
{
m_pIterator = new CServVarsIterator;
if (!m_pIterator)
{
*pcValues = 0;
return E_OUTOFMEMORY;
}
if (FAILED (hrReturn = m_pIterator->Init(m_pRequest->m_pData->m_pIReq)))
return hrReturn;
}
*pcValues = m_pIterator->m_cKeys;
return hrReturn;
}
/*===================================================================
CServerVariables::get__NewEnum
Return a new enumerator
===================================================================*/
HRESULT CServerVariables::get__NewEnum(IUnknown **ppEnumReturn)
{
HRESULT hrReturn = S_OK;
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
*ppEnumReturn = NULL;
CServVarsIterator *pIterator = new CServVarsIterator;
if (pIterator == NULL)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
return E_OUTOFMEMORY;
}
if (FAILED(hrReturn = pIterator->Init(m_pRequest->GetIReq())))
{
delete pIterator;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, ((hrReturn==E_OUTOFMEMORY)? IDE_OOM : IDE_UNEXPECTED));
return hrReturn;
}
*ppEnumReturn = pIterator;
return S_OK;
}
/*------------------------------------------------------------------
* C R e q u e s t D a t a
*/
/*===================================================================
CRequestData::CRequestData
Constructor
Parameters:
CRequest *pRequest
Returns:
Nothing.
===================================================================*/
CRequestData::CRequestData
(
CRequest *pRequest
)
: m_ISupportErrImp(static_cast<IRequest *>(pRequest), this, IID_IRequest),
m_QueryString(pRequest, this),
m_ServerVariables(pRequest, this),
m_FormInputs(pRequest, this),
m_Cookies(pRequest, this),
m_ClCerts(pRequest, this),
m_cRefs(1)
{
m_pIReq = NULL;
m_pHitObj = NULL;
m_FormDataStatus = AVAILABLE;
m_pbAvailableData = NULL;
m_cbAvailable = 0;
m_cbTotal = 0;
m_szFormData = NULL;
m_cbFormData = 0;
m_szFormClone = NULL;
m_szCookie = NULL;
m_cbCookie = 0;
m_szClCert = NULL;
m_cbClCert = 0;
m_szQueryString = NULL;
m_fLoadForm = TRUE;
m_fLoadQuery = TRUE;
m_fLoadCookies = TRUE;
m_fLoadClCerts = TRUE;
m_pEmptyString = NULL;
}
/*===================================================================
CRequestData::~CRequestData
Destructor
Parameters:
Returns:
Nothing.
===================================================================*/
CRequestData::~CRequestData()
{
CRequestHit *pNukeElem = static_cast<CRequestHit *>
(
m_mpszStrings.Head()
);
while (pNukeElem != NULL) {
CRequestHit *pNext = static_cast<CRequestHit *>(pNukeElem->m_pNext);
delete pNukeElem;
pNukeElem = pNext;
}
m_mpszStrings.UnInit();
if (m_pEmptyString)
m_pEmptyString->Release();
if (m_szFormData)
free(m_szFormData);
if (m_szFormClone)
free(m_szFormClone);
if (m_szCookie)
free(m_szCookie);
if (m_szClCert)
free(m_szClCert);
if (m_szQueryString)
free(m_szQueryString);
}
/*===================================================================
CRequestData::Init
Init
Parameters:
Returns:
Nothing.
===================================================================*/
HRESULT CRequestData::Init()
{
HRESULT hr = S_OK;
if (SUCCEEDED(hr))
hr = m_mpszStrings.Init();
if (SUCCEEDED(hr))
hr = m_QueryString.Init();
if (SUCCEEDED(hr))
hr = m_FormInputs.Init();
if (SUCCEEDED(hr))
hr = m_Cookies.Init();
if (SUCCEEDED(hr))
hr = m_ClCerts.Init();
if (SUCCEEDED(hr))
hr = m_ServerVariables.Init();
return hr;
}
/*===================================================================
CRequestData::ReInit
ReInit -- associate with new CIsapiReqInfo and HitObj
Parameters:
Returns:
Nothing.
===================================================================*/
HRESULT CRequestData::ReInit
(
CIsapiReqInfo *pIReq,
CHitObj *pHitObj
)
{
CRequestHit *pNukeElem = static_cast<CRequestHit *>
(
m_mpszStrings.Head()
);
while (pNukeElem != NULL)
{
CRequestHit *pNext = static_cast<CRequestHit *>
(
pNukeElem->m_pNext
);
delete pNukeElem;
pNukeElem = pNext;
}
m_mpszStrings.ReInit();
m_QueryString.ReInit();
m_FormInputs.ReInit();
m_Cookies.ReInit();
m_ClCerts.ReInit();
m_pIReq = pIReq;
m_pHitObj = pHitObj;
m_fLoadForm = TRUE;
m_fLoadQuery = TRUE;
m_fLoadCookies = TRUE;
m_fLoadClCerts = TRUE;
m_FormDataStatus = AVAILABLE;
if (pIReq)
{
m_pbAvailableData = pIReq->QueryPbData();
m_cbAvailable = pIReq->QueryCbAvailable();
m_cbTotal = pIReq->QueryCbTotalBytes();
}
else
{
m_pbAvailableData = NULL;
m_cbAvailable = 0;
m_cbTotal = 0;
}
if (m_szFormData)
{
m_szFormData[0] = '\0';
m_szFormClone[0] = '\0';
}
if (m_szCookie)
m_szCookie[0] = '\0';
if (m_szClCert)
m_szClCert[0] = '\0';
return S_OK;
}
/*===================================================================
CRequestData::GetEmptyStringList
Get empty string list's IDispatch *
Create empty string list on demand
===================================================================*/
HRESULT CRequestData::GetEmptyStringList
(
IDispatch **ppdisp
)
{
if (!m_pEmptyString)
{
m_pEmptyString = new CStringList;
if (!m_pEmptyString)
{
*ppdisp = NULL;
return E_FAIL;
}
}
return m_pEmptyString->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(ppdisp));
}
/*===================================================================
CRequestData::QueryInterface
CRequestData::AddRef
CRequestData::Release
IUnknown members for CRequestData object.
===================================================================*/
STDMETHODIMP CRequestData::QueryInterface
(
REFIID iid,
void **ppvObj
)
{
if (iid == IID_IUnknown)
{
*ppvObj = this;
AddRef();
return S_OK;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CRequestData::AddRef()
{
return ++m_cRefs;
}
STDMETHODIMP_(ULONG) CRequestData::Release(void)
{
if (--m_cRefs)
return m_cRefs;
delete this;
return 0;
}
DWORD CRequestData::GetRequestEntityLimit()
{
if (m_pHitObj)
return m_pHitObj->QueryAppConfig()->dwRequestEntityLimit();
else
return DEFAULT_REQUEST_ENTITY_LIMIT;
}
/*------------------------------------------------------------------
* C R e q u e s t
*/
/*===================================================================
CRequest::CRequest
Constructor
Parameters:
punkOuter object to ref count (can be NULL)
===================================================================*/
CRequest::CRequest(IUnknown *punkOuter)
:
m_fInited(FALSE),
m_fDiagnostics(FALSE),
m_pUnkFTM(NULL),
m_pData(NULL)
{
CDispatch::Init(IID_IRequest);
if (punkOuter)
{
m_punkOuter = punkOuter;
m_fOuterUnknown = TRUE;
}
else
{
m_cRefs = 1;
m_fOuterUnknown = FALSE;
}
#ifdef DBG
m_fDiagnostics = TRUE;
#endif // DBG
}
/*===================================================================
CRequest::~CRequest
Destructor
Parameters:
None
Returns:
Nothing.
===================================================================*/
CRequest::~CRequest()
{
Assert(!m_fInited);
Assert(m_fOuterUnknown || m_cRefs == 0); // must have 0 ref count
if ( m_pUnkFTM != NULL )
{
m_pUnkFTM->Release();
m_pUnkFTM = NULL;
}
}
/*===================================================================
CRequest::CleanUp
Deallocates members and removes m_pData
Parameters:
None
Returns:
HRESULT (S_OK)
===================================================================*/
HRESULT CRequest::CleanUp()
{
if (m_pData)
{
m_pData->Release();
m_pData = NULL;
}
return S_OK;
}
/*===================================================================
CRequest::Init
Allocates m_pData.
Performs any intiailization of a CRequest that's prone to failure
that we also use internally before exposing the object outside.
Parameters:
None
Returns:
S_OK on success.
===================================================================*/
HRESULT CRequest::Init()
{
HRESULT hr = S_OK;
if (m_fInited)
return S_OK; // already inited
Assert(!m_pData);
// Create the FTM
if (m_pUnkFTM == NULL)
{
hr = CoCreateFreeThreadedMarshaler((IUnknown*)((IRequestImpl*)this), &m_pUnkFTM );
if ( FAILED(hr) )
{
Assert( m_pUnkFTM == NULL );
return (hr);
}
}
Assert( m_pUnkFTM != NULL );
m_pData = new CRequestData(this);
if (!m_pData)
return E_OUTOFMEMORY;
hr = m_pData->Init();
if (SUCCEEDED(hr))
m_fInited = TRUE;
else
CleanUp();
return hr;
}
/*===================================================================
CRequest::UnInit
Remove m_pData. Back to UnInited state
Parameters:
None
Returns:
HRESULT
===================================================================*/
HRESULT CRequest::UnInit()
{
if (!m_fInited)
return S_OK; // already uninited
Assert(m_pData);
CleanUp();
Assert(!m_pData);
// Disconnect proxies NOW (in case we are in shutdown, or enter shutdown later & a proxy has a ref.)
CoDisconnectObject(static_cast<IRequestImpl *>(this), 0);
m_fInited = FALSE;
return S_OK;
}
/*===================================================================
Request::ReInit
Each Request we service will have a new CIsapiReqInfo.
This function is used to set the value of the CIsapiReqInfo.
Parameters:
CIsapiReqInfo *pIReq CIsapiReqInfo
CHitObj *pHitObj HitObj
Returns:
HRESULT
===================================================================*/
HRESULT CRequest::ReInit
(
CIsapiReqInfo *pIReq,
CHitObj *pHitObj
)
{
Assert(m_fInited);
Assert(m_pData);
return m_pData->ReInit(pIReq, pHitObj);
}
/*===================================================================
CRequest::GetCodePage
GetCodePage from current HitObj
Parameters:
Returns:
CodePage
===================================================================*/
UINT CRequest::GetCodePage()
{
Assert(m_fInited);
Assert(m_pData);
Assert(m_pData->m_pHitObj);
return m_pData->m_pHitObj->GetCodePage();
}
/*===================================================================
CRequest::LoadCookies
Load the Request map with values from the HTTP_COOKIE variable.
Parameters:
bstrVar BSTR, which parameter to get the value of
pbstrRet BSTR FAR *, return value of the requested parameter
Returns:
S_OK on success. E_FAIL on failure.
Bugs:
This code assumes that dictionary cookies are well-formed.
If they are not, then the results will be unpredictable.
The dictionary cookies are gauranteed to be well-formed if
Response.Cookies is used. If other means, such as a direct
use of the <META> tag, or if Response.SetCookie is used, we
are at the mercy of the script writer.
===================================================================*/
HRESULT CRequest::LoadCookies(char *szData)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
HRESULT hResult;
if (szData == NULL)
return S_OK;
// Each cookie definition is moved to a buffer so that we don't
// overwrite the value of HTTP_COOKIE. We can save a strcpy()
// call since 'DecodeFromURL' can copy for us.
//
size_t cbCookie = strlen(szData) + 1;
if (cbCookie > m_pData->m_cbCookie)
{
char *pszCookie = static_cast <char*> ((m_pData->m_cbCookie == 0) ?
malloc(cbCookie) :
realloc(m_pData->m_szCookie, cbCookie));
if (pszCookie == NULL)
{
//
// Free up memory already allocated to the cookies
//
if(m_pData->m_szCookie)
{
free (m_pData->m_szCookie);
m_pData->m_szCookie = NULL;
m_pData->m_cbCookie = 0;
}
return E_OUTOFMEMORY;
}
m_pData->m_szCookie = pszCookie;
m_pData->m_cbCookie = cbCookie;
}
char *szDest = m_pData->m_szCookie;
char chDelimiter; // delimiter that we found to stop the scan
while (*szData != '\0')
{
char *szName, *szPartialValue;
// Get the cookie name
chDelimiter = DecodeFromURL(&szData, ";=", szName = szDest, GetCodePage(), FALSE);
szDest = strchr(szDest, '\0') + 1;
if (chDelimiter == '=')
{
// if DecodeFromURL stop scanning because of an equal sign, then the browser sent
// a value for this cookie
// Get the cookie's value
chDelimiter = DecodeFromURL(&szData, ";=", szPartialValue = szDest, GetCodePage(), FALSE);
szDest = strchr(szDest, '\0') + 1;
// discard the denali session ID
if (strncmp(szName, SZ_SESSION_ID_COOKIE_PREFIX, CCH_SESSION_ID_COOKIE_PREFIX) == 0)
{
// DENALISESSIONID better not have non-alphabetics in it! expecting
// termination with ';' or NUL.
//
continue;
}
}
else if (*szName == '\0')
{
continue;
}
else
{
// either we hit a ';' char or end of string. In either case, this indicates that
// the cookie has no value. Set the szPartialValue to an empty string and set the
// delimiter to ';' to trick the remainder of this function into thinking that the
// cookie does have a value and that is a simple value (i.e. no sub-cookies).
chDelimiter = ';';
szPartialValue = "";
}
// Add this cookie to the Request
CRequestHit *pRequestHit = static_cast<CRequestHit *>(GetStrings()->FindElem(szName, strlen(szName)));
if (pRequestHit == NULL)
{
pRequestHit = new CRequestHit;
if (pRequestHit == NULL)
return E_OUTOFMEMORY;
if (FAILED(pRequestHit->Init(szName))) {
delete pRequestHit;
return E_FAIL;
}
GetStrings()->AddElem(pRequestHit);
// This is a new request hit, add it to the array of request hits
if (!m_pData->m_Cookies.AddRequestHit(pRequestHit))
{
return E_OUTOFMEMORY;
}
}
else if (pRequestHit->m_pCookieData) // a cookie by this name already exists
{
if (chDelimiter == '=') // eat the rest of this cookie
DecodeFromURL(&szData, ";", szDest, GetCodePage()); // no need to advance szDest
continue; // discard later cookies
}
// The cookie value may be in the form <key1=value1&key2=value2...>
// or not. If there is an '=' sign present, that lets us know if it
// is a cookie dictionary or a simple value.
//
// We assume that '=' signs that are part of the cookie are escaped in hex.
//
if (chDelimiter != '=')
{
if (FAILED(hResult = pRequestHit->AddValue(COOKIE, szPartialValue, m_pData->m_pIReq, GetCodePage())))
return hResult;
}
else
{
char *szKey = szPartialValue; // already got the key
for (;;)
{
char *szValue;
chDelimiter = DecodeFromURL(&szData, ";&", szValue = szDest, GetCodePage(), FALSE);
szDest = strchr(szDest, '\0') + 1;
if (FAILED(hResult = pRequestHit->AddKeyAndValue(COOKIE, szKey, szValue, m_pData->m_pIReq, GetCodePage())))
return hResult;
if (chDelimiter == ';' || chDelimiter == '\0')
break;
// get the key, exit when NUL terminator found
chDelimiter = DecodeFromURL(&szData, "=;", szKey = szDest, GetCodePage(), FALSE);
if (chDelimiter == ';' || chDelimiter == '\0')
break;
szDest = strchr(szDest, '\0') + 1;
}
}
}
return S_OK;
}
#define CB_CERT_DEFAULT 4096
/*===================================================================
CRequest::LoadClCerts
Load the Request map with values from the CIsapiReqInfo
Parameters:
szData - ptr to CIsapiReqInfo
Returns:
S_OK on success. E_FAIL on failure.
===================================================================*/
HRESULT CRequest::LoadClCerts(char *szData, UINT lCodePage)
{
HRESULT hres = S_OK;
CERT_CONTEXT_EX CertContextEx;
CCertRequest CertReq( this );
STACK_BUFFER( tempCert, CB_CERT_DEFAULT );
ZeroMemory( &CertContextEx, sizeof(CERT_CONTEXT_EX) );
if (FAILED(CheckForTombstone()))
return E_FAIL;
CIsapiReqInfo *pIReq = reinterpret_cast<CIsapiReqInfo *>(szData);
// allocate certificate buffer
CertContextEx.cbAllocated = tempCert.QuerySize();
CertContextEx.CertContext.pbCertEncoded = static_cast<BYTE *>(tempCert.QueryPtr());
// get certificate info from web server
if ( !pIReq->ServerSupportFunction( HSE_REQ_GET_CERT_INFO_EX,
&CertContextEx,
NULL,
NULL ) )
{
DWORD dwErr = GetLastError();
if ( dwErr == ERROR_INSUFFICIENT_BUFFER )
{
// buffer was too small - realloc and call again
Assert( CertContextEx.cbAllocated < CertContextEx.CertContext.cbCertEncoded );
CertContextEx.cbAllocated = CertContextEx.CertContext.cbCertEncoded;
// If CB_CERT_DEFAULT wasn't enough, we want to allocate from the heap, rather then the stack
if (tempCert.Resize(CertContextEx.cbAllocated) == FALSE) {
hres = E_OUTOFMEMORY;
goto LExit;
}
CertContextEx.CertContext.pbCertEncoded = static_cast<BYTE *>(tempCert.QueryPtr());
if ( !pIReq->ServerSupportFunction(
HSE_REQ_GET_CERT_INFO_EX,
&CertContextEx,
NULL,
NULL ) )
{
// if we fail a second time, just bail
// NOTE this should never happen?
dwErr = GetLastError();
Assert(dwErr != ERROR_INSUFFICIENT_BUFFER);
hres = HRESULT_FROM_WIN32(dwErr);
goto LExit;
}
}
else if ( dwErr == ERROR_INVALID_PARAMETER )
{
// not supported (old IIS)
hres = S_OK;
goto LExit;
}
else
{
hres = HRESULT_FROM_WIN32(dwErr);
goto LExit;
}
}
if(CertContextEx.CertContext.cbCertEncoded == 0)
{
hres = CertReq.NoCertificate();
}
else
{
hres = CertReq.ParseCertificate( CertContextEx.CertContext.pbCertEncoded,
CertContextEx.CertContext.cbCertEncoded,
CertContextEx.CertContext.dwCertEncodingType,
CertContextEx.dwCertificateFlags,
lCodePage );
}
LExit:
SecureZeroMemory( &CertContextEx, sizeof(CERT_CONTEXT_EX));
return hres;
}
/*===================================================================
CRequest::LoadVariables
Load the Request map with values from a URL encoded string
WARNING: This function modifies the passed szData!!
Note: this is part of bug 682, but we are not going to fix it for
performance reasons. Just be aware that this function
screws up the passed in string.
Parameters:
bstrVar BSTR, which parameter to get the value of
pbstrRet BSTR FAR *, return value of the requested parameter
lCodePage UINT, the codepage used in retrieving the data
Returns:
S_OK on success. E_FAIL on failure.
===================================================================*/
HRESULT CRequest::LoadVariables(CollectionType Source, char *szData, UINT lCodePage)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
HRESULT hResult;
if (Source == COOKIE) // cookies are a special case
return LoadCookies(szData); // handle them specially
if (Source == CLCERT) // clcerts are a special case
return LoadClCerts(szData, lCodePage); // handle them specially
if (szData == NULL) // treat NULL as "no data available"
return S_OK;
if (Source == QUERYSTRING) {
if (m_pData->m_szQueryString) {
free(m_pData->m_szQueryString);
}
if (!(m_pData->m_szQueryString = (char *)malloc(strlen(szData)+1)))
return E_OUTOFMEMORY;
strcpy(m_pData->m_szQueryString, szData);
szData = m_pData->m_szQueryString;
}
while (*szData != '\0')
{
char *szName, *szValue;
DecodeFromURL(&szData, "=", szName = szData, lCodePage, FALSE);
DecodeFromURL(&szData, "&", szValue = szData, lCodePage, FALSE);
// this is to handle the case where an un-named pair was passed.
// skip it and process the next named pair
//
if(*szName == '\0')
continue;
CRequestHit *pRequestHit = static_cast<CRequestHit *>
(
GetStrings()->FindElem(szName, strlen(szName))
);
if (pRequestHit == NULL)
{
pRequestHit = new CRequestHit;
if (pRequestHit == NULL)
return E_OUTOFMEMORY;
if (FAILED(pRequestHit->Init(szName))) {
delete pRequestHit;
return E_FAIL;
}
GetStrings()->AddElem(pRequestHit);
// This is a new request hit, so we should add it
// to the array of request
if (Source == QUERYSTRING)
{
if (!m_pData->m_QueryString.AddRequestHit(pRequestHit))
{
return E_FAIL;
}
}
else if (Source == FORM)
{
if (!m_pData->m_FormInputs.AddRequestHit(pRequestHit))
{
return E_FAIL;
}
}
}
if (FAILED(hResult = pRequestHit->AddValue(Source, szValue, m_pData->m_pIReq, lCodePage)))
return hResult;
}
return S_OK;
}
/*===================================================================
CRequest::QueryInterface
CRequest::AddRef
CRequest::Release
IUnknown members for CRequest object.
===================================================================*/
STDMETHODIMP CRequest::QueryInterface(REFIID iid, void **ppvObj)
{
*ppvObj = NULL;
// BUG FIX 683 added IID_IDenaliIntrinsic to prevent the user from
// storing intrinsic objects in the application and session object
if (iid == IID_IUnknown || iid == IID_IDispatch || iid == IID_IRequest || iid == IID_IDenaliIntrinsic)
*ppvObj = static_cast<IRequest *>(this);
else if (iid == IID_ISupportErrorInfo)
{
if (m_pData)
*ppvObj = &(m_pData->m_ISupportErrImp);
}
// Support IStream for ADO/XML
else if (iid == IID_IStream )
{
*ppvObj = static_cast<IStream *>(this);
}
else if (IID_IMarshal == iid)
{
Assert( m_pUnkFTM != NULL );
if ( m_pUnkFTM == NULL )
{
return E_UNEXPECTED;
}
return m_pUnkFTM->QueryInterface( iid, ppvObj);
}
if (*ppvObj != NULL)
{
static_cast<IUnknown *>(*ppvObj)->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CRequest::AddRef(void)
{
if (m_fOuterUnknown)
return m_punkOuter->AddRef();
return InterlockedIncrement((LPLONG)&m_cRefs);
}
STDMETHODIMP_(ULONG) CRequest::Release(void)
{
if (m_fOuterUnknown)
return m_punkOuter->Release();
DWORD cRefs = InterlockedDecrement((LPLONG)&m_cRefs);
if (cRefs)
return cRefs;
delete this;
return 0;
}
/*===================================================================
CRequest::CheckForTombstone
Tombstone stub for IRequest methods. If the object is
tombstone, does ExceptionId and fails.
Parameters:
Returns:
HRESULT E_FAIL if Tombstone
S_OK if not
===================================================================*/
HRESULT CRequest::CheckForTombstone()
{
if (m_fInited)
{
// inited - good object
Assert(m_pData); // must be present for inited objects
return S_OK;
}
ExceptionId
(
IID_IRequest,
IDE_REQUEST,
IDE_INTRINSIC_OUT_OF_SCOPE
);
return E_FAIL;
}
/*===================================================================
CRequest::get_QueryString
Return the QueryString dictionary
===================================================================*/
HRESULT CRequest::get_QueryString(IRequestDictionary **ppDictReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
return m_pData->m_QueryString.QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(ppDictReturn));
}
/*===================================================================
CRequest::get_Form
Return the Form dictionary
===================================================================*/
HRESULT CRequest::get_Form(IRequestDictionary **ppDictReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
return m_pData->m_FormInputs.QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(ppDictReturn));
}
/*===================================================================
CRequest::get_Body
Return the Body dictionary (alias for Form dictionary)
===================================================================*/
HRESULT CRequest::get_Body(IRequestDictionary **ppDictReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
return m_pData->m_FormInputs.QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(ppDictReturn));
}
/*===================================================================
CRequest::get_Cookies
Return the Cookies dictionary
===================================================================*/
HRESULT CRequest::get_Cookies(IRequestDictionary **ppDictReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
return m_pData->m_Cookies.QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(ppDictReturn));
}
/*===================================================================
CRequest::get_ClientCertificate
Return the ClCerts dictionary
===================================================================*/
HRESULT CRequest::get_ClientCertificate(IRequestDictionary **ppDictReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
return m_pData->m_ClCerts.QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(ppDictReturn));
}
/*===================================================================
CRequest::get_ServerVariables
Return the Form dictionary
===================================================================*/
HRESULT CRequest::get_ServerVariables(IRequestDictionary **ppDictReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
return m_pData->m_ServerVariables.QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(ppDictReturn));
}
/*===================================================================
CRequest::get_Item
Function called from DispInvoke to get values from any one of four
collections. Search order is "ServerVariables", "QueryString",
"Form", "Cookies", "ClientCertificate"
Parameters:
bstrVar BSTR, which parameter to get the value of
pVarReturn VARIANT *, return value of the requested parameter
Returns:
S_OK on success. E_FAIL on failure.
===================================================================*/
HRESULT CRequest::get_Item(BSTR bstrName, IDispatch **ppDispReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
if (bstrName == NULL)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
return E_FAIL;
}
UINT lCodePage = GetACP();
CWCharToMBCS convName;
char *szName;
// If BinaryRead has been called, the Form collection is no longer available
// so we insist that the script writer specify which collection to use
if (m_pData->m_FormDataStatus != AVAILABLE &&
m_pData->m_FormDataStatus != FORMCOLLECTIONONLY)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_REQUEST_GENERICCOLLECTION_NA);
return E_FAIL;
}
// At this point, we are forced to load the QueryString, Form, Cookies
// and ClientCertificate
// collections even though it will only come from one of these.
//
if (m_pData->m_fLoadQuery)
{
// QueryString can contains DBCS string
lCodePage = GetCodePage();
if (FAILED(LoadVariables(QUERYSTRING, GetIReq()->QueryPszQueryString(), lCodePage)))
return E_FAIL;
m_pData->m_fLoadQuery = FALSE;
}
if (m_pData->m_fLoadCookies)
{
char *szCookie = GetIReq()->QueryPszCookie();
if (FAILED(LoadVariables(COOKIE, szCookie, lCodePage)))
return E_FAIL;
m_pData->m_fLoadCookies = FALSE;
}
if (m_pData->m_fLoadClCerts)
{
lCodePage = GetCodePage();
if (FAILED(LoadVariables(CLCERT, (char*)GetIReq(), lCodePage)))
return E_FAIL;
m_pData->m_fLoadClCerts = FALSE;
}
if (m_pData->m_fLoadForm)
{
HRESULT hrGetData = CopyClientData();
if (FAILED(hrGetData))
return hrGetData;
// Form can contain DBCS string
lCodePage = GetCodePage();
if (FAILED(LoadVariables(FORM, m_pData->m_szFormData, lCodePage)))
return E_FAIL;
m_pData->m_fLoadForm = FALSE;
}
// Convert name to ANSI
//
HRESULT hr;
if (FAILED(hr = convName.Init(bstrName, lCodePage))) {
if (hr == E_OUTOFMEMORY) {
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
return hr;
}
hr = NO_ERROR;
szName = "";
}
else {
szName = convName.GetString();
}
// Look up the name in the collections
//
CRequestHit *pRequestHit = static_cast<CRequestHit *>(GetStrings()->FindElem(szName, strlen(szName)));
if (pRequestHit)
{
IUnknown *pValues = NULL;
if (pRequestHit->m_pQueryData)
pValues = pRequestHit->m_pQueryData;
else if (pRequestHit->m_pFormData)
pValues = pRequestHit->m_pFormData;
else if (pRequestHit->m_pCookieData)
pValues = pRequestHit->m_pCookieData;
else if (pRequestHit->m_pClCertData)
pValues = pRequestHit->m_pClCertData;
if (pValues == NULL)
goto NotFound;
if (FAILED(pValues->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(ppDispReturn))))
return E_FAIL;
return S_OK;
}
NotFound:
// Look in server variables
VARIANT varKey, varValue;
V_VT(&varKey) = VT_BSTR;
V_BSTR(&varKey) = bstrName;
if (m_pData->m_ServerVariables.get_Item(varKey, &varValue) == S_OK)
{
Assert (V_VT(&varValue) == VT_DISPATCH);
*ppDispReturn = V_DISPATCH(&varValue);
return S_OK;
}
if (FAILED(m_pData->GetEmptyStringList(ppDispReturn)))
return E_FAIL;
return S_OK;
}
/*===================================================================
CRequest::CopyClientData
Load the form data (stdin) by using either ReadClient or the
ISAPI buffer
===================================================================*/
HRESULT CRequest::CopyClientData()
{
HRESULT hr = S_OK;
if (FAILED(CheckForTombstone()))
return E_FAIL;
STACK_BUFFER(tempContent, 1024 );
CIsapiReqInfo *pIReq = m_pData->m_pIReq;
// assert that the data is in the format we want
//
// we need to scan the content type for the supported header,
// the client my send multiple headers so use strstr to search
// the header string this is a HOT_FIX for NT BUG:208530
//
if (pIReq->QueryPszContentType())
{
size_t cbQueryPszContentType = (strlen(pIReq->QueryPszContentType()) + 1);
if (cbQueryPszContentType > REQUEST_ALLOC_MAX)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_STACK_OVERFLOW);
return E_FAIL;
}
if (tempContent.Resize(cbQueryPszContentType) == FALSE)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
return E_FAIL;
}
CHAR *szQueryPszContentType = _strlwr(
strcpy(
static_cast<char *>(tempContent.QueryPtr()),
pIReq->QueryPszContentType()
));
if (strstr(szQueryPszContentType, "application/x-www-form-urlencoded") == NULL)
return S_OK;
}
else
return S_OK;
//
// Determine if it is chunked or not.
//
DWORD dwVarSize = 0;
STACK_BUFFER( varBuff, 128 );
if (SERVER_GET(pIReq, "HTTP_TRANSFER_ENCODING", &varBuff, &dwVarSize) &&
(!stricmp(static_cast<char *>(varBuff.QueryPtr()),"chunked")))
hr = CopyChunkedClientData();
else
hr = CopyNonChunkedClientData();
// Clone the data (LoadVariables will destroy the data)
// Allocate memory for clone. It should theoritically be equal to the size of FormData.
if (SUCCEEDED(hr))
{
m_pData->m_szFormClone = static_cast<char *>(malloc(m_pData->m_cbFormData));
if (m_pData->m_szFormClone == NULL)
return E_OUTOFMEMORY;
// Actually perform the copy of data.
memcpy(m_pData->m_szFormClone, m_pData->m_szFormData, m_pData->m_cbFormData);
}
return hr;
}
/*===================================================================
CRequest::CopyChunkedClientData
Load the form data (stdin) by using either ReadClient or the
ISAPI buffer. This case is called when Data is being sent in a chunks.
===================================================================*/
HRESULT CRequest::CopyChunkedClientData ()
{
CIsapiReqInfo *pIReq = m_pData->m_pIReq;
// Try to initially allocate units 4K,16K,32K....
// For the current implementation we shall stop at 32K
// Which will bring us to an allocation of (48) +4+8+16+32 +32 +32 + .....
//
DWORD allocUnit = 4096; // 0001 0000 0000 0000 B
DWORD cbAvailable = pIReq->QueryCbAvailable();
// Copy the data available and 1 byte for NULL_TERMINATOR.
DWORD cbFormData = (cbAvailable + 1);
// Check for rollover. If cbAvailable is greater than cbAvailable+1 then "Houston, we have a problem."
if (cbAvailable >= cbFormData || cbAvailable > m_pData->GetRequestEntityLimit())
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_NOT_ALLOWED);
return E_FAIL;
}
// Alloc the 4K extra memory.
cbFormData += allocUnit;
// Check again for overflow. Be paranoid.
if (cbAvailable >= cbFormData)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_NOT_ALLOWED);
return E_FAIL;
}
char * pszFormData = m_pData->m_szFormData;; // The pointer to the previous memory location in case realloc fails.
if (m_pData->m_cbFormData == 0)
{
m_pData->m_cbFormData = cbFormData;
m_pData->m_szFormData = static_cast<char *>(malloc(m_pData->m_cbFormData));
}
else if (cbFormData > m_pData->m_cbFormData)
{
m_pData->m_cbFormData = cbFormData;
m_pData->m_szFormData = static_cast<char *>(realloc(m_pData->m_szFormData, m_pData->m_cbFormData));
}
if (m_pData->m_szFormData == NULL)
{
if (pszFormData)
free (pszFormData);
m_pData->m_cbFormData = 0;
return E_OUTOFMEMORY;
}
char * pszOffset;
// Once we start to read the form data only the form collection can use it
m_pData->m_FormDataStatus = FORMCOLLECTIONONLY;
memcpy( m_pData->m_szFormData,
pIReq->QueryPbData(),
cbAvailable );
pszOffset = m_pData->m_szFormData + cbAvailable;
DWORD cBytesToRead = allocUnit;
DWORD cBytesRead = cBytesToRead;
DWORD cbCurrentFormData = cbFormData;
//
// Call ReadClient until we have read all the data
//
while (cBytesToRead > 0)
{
if ((!pIReq->SyncReadClient(pszOffset, &cBytesRead)) || (cBytesRead == 0))
break;
cBytesToRead -= cBytesRead;
if ((DIFF(pszOffset - m_pData->m_szFormData) + cBytesRead) > m_pData->GetRequestEntityLimit())
{
// We exceeded the requestEntity limit
free (m_pData->m_szFormData);
m_pData->m_szFormData = NULL; // So that the destructor will not free it too.
m_pData->m_cbFormData = 0;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_NOT_ALLOWED);
return E_FAIL;
}
if (cBytesToRead == 0)
{
// Dont allocatate anything larger than 32K unit else double the size of the allocation Unit.
if (allocUnit < 0x8000)
allocUnit = allocUnit << 1;
cbCurrentFormData = cbFormData;
// Adjust buffer size
cbFormData += allocUnit;
// save pointer to location before realloc in case of failure.
pszFormData = m_pData->m_szFormData;
if (cbCurrentFormData >= cbFormData)
{
// A rollover occurred, we need to free the memory and return failure.
if (pszFormData)
{
free (pszFormData);
m_pData->m_szFormData = NULL;
}
m_pData->m_cbFormData = 0;
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_NOT_ALLOWED);
return E_FAIL;
}
// Allocate new memory.
m_pData->m_szFormData = static_cast<char *>(realloc(m_pData->m_szFormData,
m_pData->m_cbFormData = cbFormData));
// Check for out of memory.
if (m_pData->m_szFormData == NULL)
{
if (pszFormData)
free (pszFormData);
m_pData->m_cbFormData = 0;
return E_OUTOFMEMORY;
}
// Adjust offset.
// Need to subtract 1 to compensate for the '\0' which we have allocated memory for
// while calculating cbFormData. The '\0' will be added at the very end.
pszOffset = m_pData->m_szFormData + cbFormData - allocUnit -1;
cBytesToRead = allocUnit;
}
else
{
pszOffset += cBytesRead;
}
cBytesRead = cBytesToRead;
}
//
// Adjust cbFormData to read the currect count of data.
//
m_pData->m_cbFormData -= cBytesToRead;
//
// Add the NULL terminator. The -1 is necessary to compensate for the +1 during cbFormData.
//
m_pData->m_szFormData[m_pData->m_cbFormData - 1] = '\0';
return S_OK;
}
/*===================================================================
CRequest::CopyNonChunkedClientData
Load the form data (stdin) by using either ReadClient or the
ISAPI buffer. This case is called when the Content Length is known and
===================================================================*/
HRESULT CRequest::CopyNonChunkedClientData ()
{
CIsapiReqInfo *pIReq = m_pData->m_pIReq;
//
// Allocate enough space for the form data and a copy
//
size_t cbTotal = pIReq->QueryCbTotalBytes();
// Copy the data available and 1 byte for NULL_TERMINATOR.
size_t cbFormData = (cbTotal + 1);
// Check for rollover. If cbAvailable is greater than cbAvailable+1 then "Houston, we have a problem."
if (cbTotal >= cbFormData || cbTotal > m_pData->GetRequestEntityLimit())
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_NOT_ALLOWED);
return E_FAIL;
}
char * pszFormData = m_pData->m_szFormData;
if (m_pData->m_cbFormData == 0)
{
m_pData->m_cbFormData = cbFormData;
m_pData->m_szFormData = static_cast<char *>(malloc(m_pData->m_cbFormData));
}
else if (cbFormData > m_pData->m_cbFormData)
{
m_pData->m_cbFormData = cbFormData;
m_pData->m_szFormData = static_cast<char *>(realloc(m_pData->m_szFormData, m_pData->m_cbFormData));
}
if (m_pData->m_szFormData == NULL)
{
if (pszFormData)
free (pszFormData);
m_pData->m_cbFormData = 0;
return E_OUTOFMEMORY;
}
char * pszOffset;
// Once we start to read the form data only the form collection can use it
m_pData->m_FormDataStatus = FORMCOLLECTIONONLY;
// Load the data
//
if (pIReq->QueryCbTotalBytes() <= pIReq->QueryCbAvailable())
{
memcpy( m_pData->m_szFormData,
pIReq->QueryPbData(),
pIReq->QueryCbTotalBytes() ); // bytes are available now
}
else
{
// Some bytes are in the CIsapiReqInfo buffer, we must call ReadClient for others
// First copy the data in the CIsapiReqInfo buffer
//
// To fall into this case, QueryCbAvailable should be less than cbTotal.
memcpy( m_pData->m_szFormData,
pIReq->QueryPbData(),
pIReq->QueryCbAvailable() );
DWORD cBytesToRead = pIReq->QueryCbTotalBytes() - pIReq->QueryCbAvailable();
DWORD cBytesRead = cBytesToRead;
pszOffset = m_pData->m_szFormData + pIReq->QueryCbAvailable();
// Call ReadClient until we have read all the data
//
while (cBytesToRead > 0)
{
if ((!pIReq->SyncReadClient(pszOffset, &cBytesRead)) || (cBytesRead == 0))
{
// Client closed the connection. We need to free allocated memory.
if (m_pData->m_szFormData)
{
free (m_pData->m_szFormData);
m_pData->m_szFormData = NULL;
m_pData->m_cbFormData = 0;
}
return E_FAIL;
}
cBytesToRead -= cBytesRead;
pszOffset += cBytesRead;
cBytesRead = cBytesToRead;
}
}
m_pData->m_szFormData[pIReq->QueryCbTotalBytes()] = '\0';
return S_OK;
}
/*===================================================================
CResponse::GetRequestIterator
Provide a default implementation of get__NewEnum for the Request
collections because most of the collections can use this
implementation.
Parameters:
Collection - the type of iterator to create
ppEnumReturn - on return, this points to the new enumeration
Returns:
Can return E_FAIL or E_OUTOFMEMORY
Side effects:
None.
===================================================================*/
HRESULT CRequest::GetRequestEnumerator(CollectionType WhichCollection, IUnknown **ppEnumReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
*ppEnumReturn = NULL;
CRequestIterator *pIterator = new CRequestIterator(this, WhichCollection);
if (pIterator == NULL)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
return E_OUTOFMEMORY;
}
HRESULT hrInit = pIterator->Init();
if (FAILED(hrInit))
{
delete pIterator;
ExceptionId(IID_IRequestDictionary,
IDE_REQUEST,
(hrInit == E_OUTOFMEMORY)? IDE_OOM : IDE_UNEXPECTED);
return hrInit;
}
*ppEnumReturn = pIterator;
return S_OK;
}
/*===================================================================
CResponse::get_TotalBytes
Presents the number of bytes to expect in the request body
Parameters:
pcBytes - pointer to long where we will place the number
of bytes to expect in the request body
Returns:
Can return E_FAIL
Side effects:
None.
===================================================================*/
HRESULT CRequest::get_TotalBytes(long *pcbTotal)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
if (pcbTotal == NULL)
return E_FAIL;
Assert(m_pData->m_pIReq);
*pcbTotal = (long) m_pData->m_pIReq->QueryCbTotalBytes();
return S_OK;
}
/*===================================================================
CResponse::BinaryRead
Read bytes from the Request Body to a SafeArray of VT_U1.
Parameters:
pvarCount - pointer to variant where we will find the number
of bytes to read in the request body, and where
we will store the number of bytes we read.
pvarReturn - pointer to variant that will contain the SafeArray we create
Returns:
HRESULT
Side effects:
Allocates memory.
===================================================================*/
HRESULT CRequest::BinaryRead(VARIANT *pvarCount, VARIANT *pvarReturn)
{
if (FAILED(CheckForTombstone()))
return E_FAIL;
HRESULT hr = S_OK;
SAFEARRAYBOUND rgsabound[1];
size_t cbToRead = 0;
size_t cbRead = 0;
BYTE *pbData = NULL;
Assert(m_pData->m_pIReq);
Assert(pvarCount);
Assert(pvarReturn);
// Set the variant type of the output parameter
V_VT(pvarReturn) = VT_ARRAY|VT_UI1;
V_ARRAY(pvarReturn) = NULL;
if (m_pData->m_FormDataStatus == FORMCOLLECTIONONLY)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_REQUEST_BINARYREAD_NA);
hr = E_FAIL;
goto error;
}
// Convert the byte count variant to a long
if (FAILED(hr = VariantChangeTypeEx(pvarCount, pvarCount, m_pData->m_pHitObj->GetLCID(), 0, VT_I4)))
{
switch (hr)
{
case E_OUTOFMEMORY:
ExceptionId(IID_IResponse, IDE_REQUEST, IDE_OOM);
break;
case DISP_E_OVERFLOW:
hr = E_FAIL;
ExceptionId(IID_IResponse, IDE_REQUEST, IDE_RESPONSE_UNABLE_TO_CONVERT);
break;
case DISP_E_TYPEMISMATCH:
ExceptionId(IID_IResponse, IDE_REQUEST, IDE_TYPE_MISMATCH);
break;
default:
ExceptionId(IID_IResponse, IDE_REQUEST, IDE_UNEXPECTED);
}
goto error;
}
cbToRead = V_I4(pvarCount);
V_I4(pvarCount) = 0;
if ((signed long) cbToRead < 0)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_REQUEST_BINREAD_BAD_ARG);
hr = E_FAIL;
goto error;
}
// If 0 bytes are requested, or available we're done
if (cbToRead == 0 || m_pData->m_cbTotal == 0)
return S_OK;
// Allocate a SafeArray for the data
// If they've asked for more bytes then the request
// contains, give them all the bytes in the request.
rgsabound[0].lLbound = 0;
if (cbToRead > m_pData->m_cbTotal)
cbToRead = m_pData->m_cbTotal;
// If more than RequestEntityLimit bytes have been requested return failure.
// Since we allocate cbToRead and finally copy only cbToRead this is a safe place to check
if (cbToRead > m_pData->GetRequestEntityLimit())
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_NOT_ALLOWED);
hr = E_FAIL;
goto error;
}
rgsabound[0].cElements = cbToRead;
V_ARRAY(pvarReturn) = SafeArrayCreate(VT_UI1, 1, rgsabound);
if (V_ARRAY(pvarReturn) == NULL)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
hr = E_OUTOFMEMORY;
goto error;
}
if (FAILED(SafeArrayAccessData(V_ARRAY(pvarReturn), (void **) &pbData)))
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_UNEXPECTED);
hr = E_UNEXPECTED;
goto error;
}
// There is no turning back now. The Request.Form collection will
// no longer be available.
if (m_pData->m_FormDataStatus == AVAILABLE)
{
m_pData->m_FormDataStatus = BINARYREADONLY;
m_pData->m_fLoadForm = FALSE;
}
// If the number of bytes requested is less then the number of
// bytes available (as maintained by the request object),
// then copy the requested bytes from the request object copy
// of the pointer to the CIsapiReqInfo buffer, decrement the number of bytes
// available, and increment the pointer to the CIsapiReqInfo buffer.
// Otherwise, copy all available bytes from the CIsapiReqInfo buffer, and
// then issue a call to ReadClient to get the remaining needed bytes.
if (cbToRead <= m_pData->m_cbAvailable)
{
memcpy(pbData, m_pData->m_pbAvailableData, cbToRead);
m_pData->m_pbAvailableData += cbToRead;
m_pData->m_cbAvailable -= cbToRead;
m_pData->m_cbTotal -= cbToRead;
V_I4(pvarCount) = cbToRead;
}
else
{
if (m_pData->m_cbAvailable > 0)
{
memcpy(pbData, m_pData->m_pbAvailableData, m_pData->m_cbAvailable);
V_I4(pvarCount) = m_pData->m_cbAvailable;
cbToRead -= m_pData->m_cbAvailable;
m_pData->m_cbTotal -= m_pData->m_cbAvailable;
pbData += m_pData->m_cbAvailable;
}
m_pData->m_pbAvailableData = NULL;
m_pData->m_cbAvailable = 0;
while (cbToRead)
{
cbRead = cbToRead;
if (!GetIReq()->SyncReadClient(pbData, (DWORD *)&cbRead) || (cbRead == 0))
{
SafeArrayUnaccessData(V_ARRAY(pvarReturn));
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_UNEXPECTED);
hr = E_FAIL;
goto error;
}
pbData += cbRead;
V_I4(pvarCount) += cbRead;
m_pData->m_cbTotal -= cbRead;
cbToRead -= cbRead;
}
}
SafeArrayUnaccessData(V_ARRAY(pvarReturn));
return S_OK;
error:
VariantClear(pvarReturn);
return(hr);
}
/*===================================================================
IStream implementation for ADO/XML
===================================================================*/
STDMETHODIMP CRequest::Read(
void *pv,
ULONG cb,
ULONG *pcbRead)
{
if (pv == NULL)
return E_POINTER;
ULONG cbReadDummy;
if (pcbRead == NULL)
pcbRead = &cbReadDummy;
if (m_pData->m_FormDataStatus != AVAILABLE &&
m_pData->m_FormDataStatus != ISTREAMONLY)
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST,
IDE_REQUEST_STREAMONLY);
return E_FAIL;
}
// If they've asked for more bytes then the request
// contains, give them all the bytes in the request.
if (cb > m_pData->m_cbTotal)
cb = m_pData->m_cbTotal;
// If they requested more data than we have have set in the RequestEntityLimit return a NOT Allowed error.
// Since cb is the maximum data that will be copied. It is safe to do the check here.
if (cb > m_pData->GetRequestEntityLimit())
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_NOT_ALLOWED);
return E_FAIL;
}
// There is no turning back now. The Request.Form collection and
// Request.BinaryRead will no longer be available.
if (m_pData->m_FormDataStatus == AVAILABLE)
{
m_pData->m_FormDataStatus = ISTREAMONLY;
m_pData->m_fLoadForm = FALSE;
}
// If the number of bytes requested is less then the number of
// bytes available (as maintained by the request object),
// then copy the requested bytes from the request object copy of
// the pointer to the CIsapiReqInfo buffer, decrement the number of bytes
// available, and increment the pointer to the CIsapiReqInfo buffer.
// Otherwise, copy all available bytes from the CIsapiReqInfo buffer, and
// then issue a call to ReadClient to get the remaining needed bytes.
BYTE* pbData = static_cast<BYTE*>(pv);
if (cb <= m_pData->m_cbAvailable)
{
memcpy(pbData, m_pData->m_pbAvailableData, cb);
m_pData->m_pbAvailableData += cb;
m_pData->m_cbAvailable -= cb;
m_pData->m_cbTotal -= cb;
*pcbRead = cb;
}
else
{
*pcbRead = 0;
if (m_pData->m_cbAvailable > 0)
{
memcpy(pbData, m_pData->m_pbAvailableData, m_pData->m_cbAvailable);
*pcbRead = m_pData->m_cbAvailable;
cb -= m_pData->m_cbAvailable;
m_pData->m_cbTotal -= m_pData->m_cbAvailable;
pbData += m_pData->m_cbAvailable;
}
m_pData->m_pbAvailableData = NULL;
m_pData->m_cbAvailable = 0;
while (cb > 0)
{
DWORD cbRead = cb;
if ((!GetIReq()->SyncReadClient(pbData, &cbRead)) || (cbRead == 0))
{
ExceptionId(IID_IRequestDictionary, IDE_REQUEST,
IDE_UNEXPECTED);
return E_FAIL;
}
pbData += cbRead;
*pcbRead += cbRead;
m_pData->m_cbTotal -= cbRead;
cb -= cbRead;
}
}
return S_OK;
}
STDMETHODIMP CRequest::Write(
const void *pv,
ULONG cb,
ULONG *pcbWritten)
{
return E_NOTIMPL;
}
STDMETHODIMP CRequest::Seek(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER *plibNewPosition)
{
// We can only do a seek if we're in the first, pre-read portion of the
// form data
if (m_pData->m_pbAvailableData == NULL)
return E_FAIL;
BYTE* pbAvailableData;
switch (dwOrigin)
{
case STREAM_SEEK_SET:
// relative to beginning of stream
pbAvailableData = m_pData->m_pIReq->QueryPbData() + dlibMove.LowPart;
break;
case STREAM_SEEK_CUR:
// relative to current position in stream
pbAvailableData = m_pData->m_pbAvailableData + dlibMove.LowPart;
break;
case STREAM_SEEK_END:
// relative to end of stream; not supported
return E_FAIL;
};
// Does the new offset fall within the initial header?
if (m_pData->m_pIReq->QueryPbData() <= pbAvailableData
&& pbAvailableData < m_pData->m_pIReq->QueryPbData()
+ m_pData->m_pIReq->QueryCbAvailable())
{
DWORD dwDiff = DIFF(pbAvailableData - m_pData->m_pIReq->QueryPbData());
m_pData->m_pbAvailableData = pbAvailableData;
m_pData->m_cbAvailable = m_pData->m_pIReq->QueryCbAvailable() - dwDiff;
m_pData->m_cbTotal = m_pData->m_pIReq->QueryCbTotalBytes() - dwDiff;
// Return the new position, if wanted
if (plibNewPosition != NULL)
plibNewPosition->LowPart = dwDiff;
return S_OK;
}
return E_FAIL;
}
STDMETHODIMP CRequest::SetSize(
ULARGE_INTEGER libNewSize)
{
return E_NOTIMPL;
}
STDMETHODIMP CRequest::CopyTo(
IStream *pstm,
ULARGE_INTEGER cb,
ULARGE_INTEGER *pcbRead,
ULARGE_INTEGER *pcbWritten)
{
return E_NOTIMPL;
}
STDMETHODIMP CRequest::Commit(
DWORD grfCommitFlags)
{
return E_NOTIMPL;
}
STDMETHODIMP CRequest::Revert()
{
return E_NOTIMPL;
}
STDMETHODIMP CRequest::LockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType)
{
return E_NOTIMPL;
}
STDMETHODIMP CRequest::UnlockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType)
{
return E_NOTIMPL;
}
STDMETHODIMP CRequest::Stat(
STATSTG *pstatstg,
DWORD grfStatFlag)
{
return E_NOTIMPL;
}
STDMETHODIMP CRequest::Clone(
IStream **ppstm)
{
return E_NOTIMPL;
}
#ifdef DBG
/*===================================================================
CRequest::AssertValid
Test to make sure that the CRequest object is currently correctly formed
and assert if it is not.
Returns:
Side effects:
None.
===================================================================*/
VOID CRequest::AssertValid() const
{
}
#endif // DBG
/*===================================================================
HexToChar
Convert two digit hex string to a hex byte
Parameters:
szHex - pointer to two digit hex string
Return Value:
the character value of the hex string
===================================================================*/
char HexToChar(LPSTR szHex)
{
char chResult, chDigit;
chDigit = (char)CharUpperA((LPSTR)szHex[0]);
chResult = (chDigit >= 'A'? (chDigit - 'A' + 0xA) : (chDigit - '0')) << 4;
chDigit = (char)CharUpperA((LPSTR)szHex[1]);
chResult |= chDigit >= 'A'? (chDigit - 'A' + 0xA) : (chDigit - '0');
return chResult;
}
/*===================================================================
DecodeFromURL
Convert two digit hex string to a hex byte
WARNING: This function modifies the passed pszSource!!
Note: this is part of bug 682, but we are not going to fix it for
performance reasons. Just be aware that this function
screws up the passed in string.
Parameters:
pszSource - in/out parameter points to a substring in the URL which
contains a Name=Value pair
szDelimiters - a set of delimiters for this field
szDest - pointer to buffer to hold the substring
Return Value:
Returns the actual delimiter that caused parsing to halt.
===================================================================*/
char DecodeFromURL(char **pszSource, char *szDelimiters, char *szDest, UINT uCodePage, BOOL fIgnoreCase)
{
char ch;
char *szSource = *pszSource;
char *pszDestStart = szDest;
CPINFO CpInfo;
BOOL fIschLeadingByte = TRUE;
BOOL InvalidPercent = FALSE;
GetCPInfo(uCodePage, (LPCPINFO)&CpInfo);
while ((ch = *szSource++) != '\0' &&
((!strchr(szDelimiters, ch) && fIschLeadingByte) || (!fIschLeadingByte))) {
InvalidPercent = FALSE;
switch (ch) {
case ' ': // skip whitespace - assume that all whitespace that we need is escaped
case '\t': // all these chars are out of trailing byte range
case '\r':
case '\n':
case '\f':
case '\v':
Assert(fIschLeadingByte);
continue;
case '+': // '+' is out of trailing byte range, can never be a trailing byte
*szDest++ = ' ';
Assert(fIschLeadingByte);
break;
case '%': // '%' is out of trailing byte range, can never be a trailing byte
if (*szSource == 'u') {
if (isxdigit((UCHAR)*(szSource+1)) &&
isxdigit((UCHAR)*(szSource+2)) &&
isxdigit((UCHAR)*(szSource+3)) &&
isxdigit((UCHAR)*(szSource+4))) {
WCHAR wch[2];
int cch = 1;
wch[0] = (UCHAR)HexToChar(&szSource[1]) << 8;
wch[0] |= (UCHAR)HexToChar(&szSource[3]);
szSource += 5;
// if the current UNICODE value falls into the
// range of valid high-Surrogate, check to see if
// the next character is in the low-Surrogate
// range.
if (IsSurrogateHigh(wch[0])
&& (szSource[0] == '%')
&& (szSource[1] == 'u')
&& isxdigit((UCHAR)szSource[2])
&& isxdigit((UCHAR)szSource[3])
&& isxdigit((UCHAR)szSource[4])
&& isxdigit((UCHAR)szSource[5])) {
// Well, the current UNICODE value is in the high
// range and the next portion of the string is
// a UNICODE encoding. Decode it.
wch[1] = (UCHAR)HexToChar(&szSource[2]) << 8;
wch[1] |= (UCHAR)HexToChar(&szSource[4]);
// Now see if it falls in the range of low-Surrogates
if (IsSurrogateLow(wch[1])) {
// it does!!! Up the number of characters in the
// string that WideCharToMultiByte is going to
// convert. And advance the source string past this
// location.
cch = 2;
szSource += 6;
}
}
szDest += WideCharToMultiByte( uCodePage, 0, wch, cch, szDest, 6, NULL, NULL );
} else {
// What to do here ?
// since we have at least the u char after the %,
// keep the u and let the show go on
}
break;
}
else {
if (isxdigit((UCHAR)*szSource) && isxdigit((UCHAR)*(szSource+1))) {
ch = HexToChar(szSource);
szSource += 2;
}
else
{
// the spurious encoding MUST be removed
InvalidPercent = TRUE;
}
}
// FALL THROUGH to "Normal" case
default:
if (fIschLeadingByte == TRUE) {
if (CpInfo.MaxCharSize > 1) {
// if this is a Leading byte, then, the next one is a trailing byte, we need
// not process the next char even the next char is in szDelimiter, next char
// is just the second byte of a DBCS char.
if (IsDBCSLeadByteEx(uCodePage, ch))
fIschLeadingByte = FALSE;
}
}
else { // A trailing byte
// If we skip a DBCS trailing byte, then, the next char we check is a leading byte
Assert(CpInfo.MaxCharSize == 2);
fIschLeadingByte = TRUE;
}
if (!InvalidPercent) {
*szDest++ = ch;
}
}
}
if (ch == '\0') // End of String - undo increment of szSource
--szSource;
*szDest = '\0';
if (fIgnoreCase)
CharUpperA(pszDestStart);
*pszSource = szSource;
return ch;
}
/*------------------------------------------------------------------
* C S e r v V a r s I t e r a t o r
*/
/*===================================================================
CServVarsIterator::CServVarsIterator
Constructor
===================================================================*/
CServVarsIterator::CServVarsIterator()
{
m_rgwszKeys = NULL;
m_pwszKey = NULL;
m_pwchAllHttp = NULL;
m_cRefs = 1;
m_cKeys = 0;
}
/*===================================================================
CServVarsIterator::~CServVarsIterator
Destructor
===================================================================*/
CServVarsIterator::~CServVarsIterator()
{
delete m_rgwszKeys;
delete m_pwchAllHttp;
}
/*===================================================================
CServVarsIterator::Init
Initialize the iterator by:
* Getting the value of ALL_HTTP, and parsing it to get the
extra keys
* creating a dynamic memory area to hold the ALL_HTTP keys
* setting m_rgwszKeys by copying pointers from rgwszStandardKeys
and from ALL_HTTP keys
Parameters:
pIReq - pointer to CIsapiReqInfo used to query for extra headers
Return Value:
Returns E_OUTOFMEMORY or S_OK
===================================================================*/
HRESULT CServVarsIterator::Init
(
CIsapiReqInfo *pIReq
)
{
static wchar_t *rgwszStandardKeys[] = {
L"ALL_HTTP",
L"ALL_RAW",
L"APPL_MD_PATH",
L"APPL_PHYSICAL_PATH",
L"AUTH_PASSWORD",
L"AUTH_TYPE",
L"AUTH_USER",
L"CERT_COOKIE",
L"CERT_FLAGS",
L"CERT_ISSUER",
L"CERT_KEYSIZE",
L"CERT_SECRETKEYSIZE",
L"CERT_SERIALNUMBER",
L"CERT_SERVER_ISSUER",
L"CERT_SERVER_SUBJECT",
L"CERT_SUBJECT",
L"CONTENT_LENGTH",
L"CONTENT_TYPE",
L"GATEWAY_INTERFACE",
// Purposely left out of IIS 4.0 L"HTTP_CFG_ENC_CAPS",
// Purposely left out of IIS 4.0 L"HTTP_REQ_PWD_EXPIRE",
// Purposely left out of IIS 4.0 L"HTTP_REQ_REALM",
L"HTTPS",
L"HTTPS_KEYSIZE",
L"HTTPS_SECRETKEYSIZE",
L"HTTPS_SERVER_ISSUER",
L"HTTPS_SERVER_SUBJECT",
L"INSTANCE_ID",
L"INSTANCE_META_PATH",
L"LOCAL_ADDR",
L"LOGON_USER",
L"PATH_INFO",
L"PATH_TRANSLATED",
L"QUERY_STRING",
L"REMOTE_ADDR",
L"REMOTE_HOST",
L"REMOTE_USER",
L"REQUEST_METHOD",
// Deleted bogus variable in IIS 4.0 L"SCRIPT_MAP",
L"SCRIPT_NAME",
L"SERVER_NAME",
L"SERVER_PORT",
L"SERVER_PORT_SECURE",
L"SERVER_PROTOCOL",
L"SERVER_SOFTWARE",
// Purposely left out of IIS 4.0 L"UNMAPPED_REMOTE_USER",
L"URL"
};
const int cStandardKeys = sizeof(rgwszStandardKeys) / sizeof(rgwszStandardKeys[0]);
// Style note:
//
// pwchExtraKeys points not to just one NUL terminated wide string
// but a whole sequence of NUL terminated wide string followed by
// a double NUL terminator. I therefore chose not to use the
// standard "wsz" hungarian prefix, instead using "pwch" as
// "pointer to wide characters"
//
int cwchAlloc = 0, cRequestHeaders = 0;
DWORD dwHeaderSize = 0;
STACK_BUFFER( extraKeysBuff, 2048 );
if (!SERVER_GET(pIReq, "ALL_HTTP", &extraKeysBuff, &dwHeaderSize)) {
if (GetLastError() == ERROR_OUTOFMEMORY) {
return E_OUTOFMEMORY;
}
else {
return E_FAIL;
}
}
char *szExtraKeys = (char *)extraKeysBuff.QueryPtr();
CMBCSToWChar convStr;
HRESULT hrConvResult;
if (FAILED(hrConvResult = convStr.Init(szExtraKeys))) {
return hrConvResult;
}
wchar_t *pwchExtraKeys = convStr.GetString();
if (!CreateKeys(pwchExtraKeys, &cwchAlloc, &cRequestHeaders))
return E_FAIL;
// At this point, pwchExtraKeys has the strings. Copy them
// into more permanent storage.
//
if (cwchAlloc)
{
Assert(pwchExtraKeys != NULL);
if ((m_pwchAllHttp = new wchar_t [cwchAlloc]) == NULL)
return E_OUTOFMEMORY;
memcpy(m_pwchAllHttp, pwchExtraKeys, cwchAlloc * sizeof(wchar_t));
}
else
m_pwchAllHttp = NULL;
// Allocate the array of keys, m_rgwszKeys, and copy the standard
// ISAPI keys, the extra keys from the request headers, and a
// terminating NULL to easily mark the end of an iteration.
//
if ((m_rgwszKeys = new wchar_t *[cStandardKeys + cRequestHeaders + 1]) == NULL)
return E_OUTOFMEMORY;
m_cKeys = cStandardKeys + cRequestHeaders;
wchar_t **pwszKey = m_rgwszKeys;
int i;
for (i = 0; i < cStandardKeys; ++i)
*pwszKey++ = rgwszStandardKeys[i];
wchar_t *pwch = m_pwchAllHttp;
for (i = 0; i < cRequestHeaders; ++i)
{
*pwszKey++ = pwch;
pwch = wcschr(pwch, L'\0') + 1;
}
// make sure that cRequestHeaders was equal to the actual number of strings
// in the pwchAllHttp string table. (Do this by making sure that we stored
// the exact amount of bytes and are now at the NULL terminator)
//
Assert (*pwch == L'\0' && (pwch - m_pwchAllHttp + 1) == cwchAlloc);
*pwszKey = NULL; // terminate the array
return Reset(); // reset the iterator
}
/*===================================================================
CServVarsIterator::CreateKeys
Parse the string from Request.ServerVariables["ALL_HTTP"], then
transform the string into a list of NUL terminated wide strings
in place, terminated with a double NUL.
Parameters:
pwchKeys -
Input: Contains the value of Request.ServerVariables["ALL_HTTP"]
as a wide string
Output: Contains the keys from Request.ServerVariables["ALL_HTTP"],
each key is separated by a NUL terminator, and the entire
list of keys is terminated by a double NUL.
pwchAlloc -
Output: Contains the number of wide characters that should be
allocated to contain the entire list of strings pointed
to by pwchKeys
pcRequestHeaders -
Output: Contains the number of keys that were found in
Request.ServerVariables["ALL_HTTP"].
Return Value:
TRUE if successful
===================================================================*/
BOOL CServVarsIterator::CreateKeys(wchar_t *pwchKeys, int *pcwchAlloc, int *pcRequestHeaders)
{
wchar_t *pwchSrc = pwchKeys; // source
wchar_t *pwchDest = pwchKeys; // destination
if (pwchKeys == NULL)
{
*pcwchAlloc = 0;
*pcRequestHeaders = 0;
return TRUE;
}
// Loop over pwchKeys until we hit the NUL terminator
//
*pcRequestHeaders = 0;
while (*pwchSrc)
{
// Copy characters up to the ':' and store in pwchDest
//
while (*pwchSrc != L':')
{
if (*pwchSrc == L'\0') // better not find End of String yet
return FALSE;
*pwchDest++ = *pwchSrc++;
}
// now NUL terminate pwchDest, advance pwchSrc, increment cRequestHeaders
//
*pwchDest++ = L'\0';
++pwchSrc;
++*pcRequestHeaders;
// Skip characters until we find a \r OR \n
//
// If wcspbrk returns NULL here, it means there was no terminating
// \r or \n. In this case we can exit the loop because there
// are no more keys (the value must have ran to the end of the
// string without termination)
//
pwchSrc = wcspbrk(pwchSrc, L"\r\n");
if (! pwchSrc)
break;
// we found either \r OR \n. Skip the remaining whitspace char.
//
while (*pwchSrc == L'\r' || *pwchSrc == L'\n')
++pwchSrc;
// pwchSrc now points to the next key.
}
// terminate with the final NUL.
*pwchDest++ = L'\0';
*pcwchAlloc = DIFF(pwchDest - pwchKeys);
return TRUE;
}
/*===================================================================
CServVarsIterator::QueryInterface
CServVarsIterator::AddRef
CServVarsIterator::Release
IUnknown members for CServVarsIterator object.
===================================================================*/
STDMETHODIMP CServVarsIterator::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) CServVarsIterator::AddRef()
{
return ++m_cRefs;
}
STDMETHODIMP_(ULONG) CServVarsIterator::Release()
{
if (--m_cRefs > 0)
return m_cRefs;
delete this;
return 0;
}
/*===================================================================
CServVarsIterator::Clone
Clone this iterator (standard method)
NOTE:
Cloning this iterator is quite involved. (It essentially
involves copying the allocated memory, then adjusting
ONLY the dynamic pointers in the rgwszKeys array.)
Right now, this is NYI, as our client (VBScript)
does not clone this iterator.
===================================================================*/
STDMETHODIMP CServVarsIterator::Clone(IEnumVARIANT **ppEnumReturn)
{
return E_NOTIMPL;
}
/*===================================================================
CServVarsIterator::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 CServVarsIterator::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_pwszKey != NULL)
{
BSTR bstrT = SysAllocString(*m_pwszKey);
if (bstrT == NULL)
return E_OUTOFMEMORY;
V_VT(rgVariant) = VT_BSTR;
V_BSTR(rgVariant) = bstrT;
++m_pwszKey;
++rgVariant;
--cElements;
++*pcElementsFetched;
}
// initialize the remaining variants
//
while (cElements-- > 0)
VariantInit(rgVariant++);
return (*pcElementsFetched == cElementsRequested)? S_OK : S_FALSE;
}
/*===================================================================
CServVarsIterator::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 CServVarsIterator::Skip(unsigned long cElements)
{
/* Loop through the collection until either we reach the end or
* cElements becomes zero
*/
while (cElements > 0 && *m_pwszKey != NULL)
{
--cElements;
++m_pwszKey;
}
return (cElements == 0)? S_OK : S_FALSE;
}
/*===================================================================
CServVarsIterator::Reset
Reset the iterator (standard method)
===================================================================*/
STDMETHODIMP CServVarsIterator::Reset()
{
m_pwszKey = &m_rgwszKeys[0];
return S_OK;
}
/*------------------------------------------------------------------
* C R e q u e s t I t e r a t o r
*/
/*===================================================================
CRequestIterator::CRequestIterator
Constructor
NOTE: CRequest is (currently) not refcounted. AddRef/Release
added to protect against future changes.
===================================================================*/
CRequestIterator::CRequestIterator(CRequest *pRequest, CollectionType Collection)
{
m_Collection = Collection;
m_pRequest = pRequest;
m_cRefs = 1;
m_pRequestHit = NULL; // Init() will change this pointer anyway...
m_pRequest->AddRef();
}
/*===================================================================
CRequestIterator::CRequestIterator
Destructor
===================================================================*/
CRequestIterator::~CRequestIterator()
{
m_pRequest->Release();
}
/*===================================================================
CRequestIterator::Init
Initialize the iterator by loading the collection that we are
about to iterate over.
Return Value:
Returns E_FAIL if there were problems loading the collection,
and possibly E_OUTOFMEMORY.
===================================================================*/
HRESULT CRequestIterator::Init()
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
switch (m_Collection)
{
case QUERYSTRING:
if (m_pRequest->m_pData->m_fLoadQuery)
{
if (FAILED(m_pRequest->LoadVariables(QUERYSTRING, m_pRequest->GetIReq()->QueryPszQueryString(), m_pRequest->GetCodePage())))
return E_FAIL;
m_pRequest->m_pData->m_fLoadQuery = FALSE;
}
break;
case FORM:
if (m_pRequest->m_pData->m_fLoadForm)
{
HRESULT hrGetData = m_pRequest->CopyClientData();
if (FAILED(hrGetData))
return hrGetData;
if (FAILED(m_pRequest->LoadVariables(FORM, m_pRequest->m_pData->m_szFormData, m_pRequest->GetCodePage())))
return E_FAIL;
m_pRequest->m_pData->m_fLoadForm = FALSE;
}
break;
case COOKIE:
if (m_pRequest->m_pData->m_fLoadCookies)
{
char *szCookie = m_pRequest->GetIReq()->QueryPszCookie();
if (FAILED(m_pRequest->LoadVariables(COOKIE, szCookie, m_pRequest->GetCodePage())))
return E_FAIL;
m_pRequest->m_pData->m_fLoadCookies = FALSE;
}
break;
case CLCERT:
if (m_pRequest->m_pData->m_fLoadClCerts)
{
if (FAILED(m_pRequest->LoadVariables(CLCERT, (char*)m_pRequest->GetIReq(), m_pRequest->GetCodePage())))
return E_FAIL;
m_pRequest->m_pData->m_fLoadClCerts = FALSE;
}
break;
}
return Reset();
}
/*===================================================================
CRequestIterator::QueryInterface
CRequestIterator::AddRef
CRequestIterator::Release
IUnknown members for CRequestIterator object.
===================================================================*/
STDMETHODIMP CRequestIterator::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) CRequestIterator::AddRef()
{
return ++m_cRefs;
}
STDMETHODIMP_(ULONG) CRequestIterator::Release()
{
if (--m_cRefs > 0)
return m_cRefs;
delete this;
return 0;
}
/*===================================================================
CRequestIterator::Clone
Clone this iterator (standard method)
===================================================================*/
STDMETHODIMP CRequestIterator::Clone(IEnumVARIANT **ppEnumReturn)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
CRequestIterator *pNewIterator = new CRequestIterator(m_pRequest, m_Collection);
if (pNewIterator == NULL)
return E_OUTOFMEMORY;
// new iterator should point to same location as this.
pNewIterator->m_pRequestHit = m_pRequestHit;
*ppEnumReturn = pNewIterator;
return S_OK;
}
/*===================================================================
CRequestIterator::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 CRequestIterator::Next(unsigned long cElementsRequested, VARIANT *rgVariant, unsigned long *pcElementsFetched)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
// 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_pRequestHit != NULL)
{
BOOL fHaveData = FALSE;
switch (m_Collection)
{
case QUERYSTRING:
fHaveData = m_pRequestHit->m_pQueryData != NULL;
break;
case FORM:
fHaveData = m_pRequestHit->m_pFormData != NULL;
break;
case COOKIE:
fHaveData = m_pRequestHit->m_pCookieData != NULL;
break;
case CLCERT:
fHaveData = m_pRequestHit->m_pClCertData != NULL;
}
if (fHaveData)
{
BSTR bstrT;
if (FAILED(SysAllocStringFromSz(reinterpret_cast<char *>(m_pRequestHit->m_pKey), 0, &bstrT,m_pRequest->GetCodePage())))
return E_OUTOFMEMORY;
V_VT(rgVariant) = VT_BSTR;
V_BSTR(rgVariant) = bstrT;
++rgVariant;
--cElements;
++*pcElementsFetched;
}
m_pRequestHit = static_cast<CRequestHit *>(m_pRequestHit->m_pPrev);
}
// initialize the remaining variants
//
while (cElements-- > 0)
VariantInit(rgVariant++);
return (*pcElementsFetched == cElementsRequested)? S_OK : S_FALSE;
}
/*===================================================================
CRequestIterator::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 CRequestIterator::Skip(unsigned long cElements)
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
/* Loop through the collection until either we reach the end or
* cElements becomes zero
*/
while (cElements > 0 && m_pRequestHit != NULL)
{
BOOL fHaveData = FALSE;
switch (m_Collection)
{
case QUERYSTRING:
fHaveData = m_pRequestHit->m_pQueryData != NULL;
break;
case FORM:
fHaveData = m_pRequestHit->m_pFormData != NULL;
break;
case COOKIE:
fHaveData = m_pRequestHit->m_pCookieData != NULL;
break;
case CLCERT:
fHaveData = m_pRequestHit->m_pClCertData != NULL;
}
if (fHaveData)
--cElements;
m_pRequestHit = static_cast<CRequestHit *>(m_pRequestHit->m_pPrev);
}
return (cElements == 0)? S_OK : S_FALSE;
}
/*===================================================================
CRequestIterator::Reset
Reset the iterator (standard method)
===================================================================*/
STDMETHODIMP CRequestIterator::Reset()
{
if (FAILED(m_pRequest->CheckForTombstone()))
return E_FAIL;
m_pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->Tail());
return S_OK;
}