2025-04-27 07:49:33 -04:00

753 lines
20 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1995-1996 Microsoft Corporation
Module Name :
navcol.cxx
Abstract:
This module defines the functions for handling the dictionary items.
Author:
Murali R. Krishnan ( MuraliK ) 4-Dec-1996
Environment:
User Mode - Win32
Project:
Internet Application Server DLL
Functions Exported:
NAVCOL::<members>
Revision History:
--*/
/************************************************************
* Include Headers
************************************************************/
# include "stdafx.h"
# if !(REG_DLL)
# include "navcol.hxx"
/************************************************************
* Functions
************************************************************/
/************************************************************
* Member Functions of NAVCOL
************************************************************/
inline VOID
UpdatePointer( IN OUT LPCSTR * ppsz, IN const CHAR * pchOld,
IN DWORD cchLen, IN const CHAR * pchNew)
{
if ( (*ppsz >= pchOld) &&
(*ppsz < (pchOld + cchLen))
){
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
" Updating pointer [%08x] from %08x to %08x\n",
ppsz, *ppsz, ((*ppsz - pchOld) + pchNew)));
}
// update the pointer
*ppsz = ((*ppsz - pchOld) + pchNew);
}
}
NAVCOL::NAVCOL(VOID)
: m_chNull ('\0'),
m_buffData (),
m_cchData ( 0),
m_cchBuffData ( 0)
{
InitializeListHead( &m_ActiveList);
InitializeListHead( &m_FreeList);
IF_DEBUG( INIT_CLEAN) {
DBGPRINTF(( DBG_CONTEXT, "NAVCOL() => %08x\n", this));
}
Reset();
} // NAVCOL::NAVCOL()
NAVCOL::~NAVCOL( VOID)
{
// NOTHING SPECIAL TO BE DONE HERE
IF_DEBUG( INIT_CLEAN) {
DBGPRINTF(( DBG_CONTEXT, "deleted NAVCOL %08x\n", this));
}
} // NAVCOL::~NAVCOL()
VOID
NAVCOL::Reset( VOID)
{
m_cchData = 0;
m_rcInlinedData[0] = '\0';
m_cchBuffData = 0;
m_buffData.Resize( HH_MIN);
//
// Move the Name-Value chunks from active list to the free-list.
//
while ( !IsListEmpty( &m_ActiveList)) {
PLIST_ENTRY pl = m_ActiveList.Flink;
RemoveEntryList( pl);
InsertTailList( &m_FreeList, pl);
} // while
InitializeListHead( &m_ActiveList);
return;
} // NAVCOL::Reset()
VOID
NAVCOL::CancelHeader( IN LPCSTR pszName)
{
DWORD cchName = strlen( pszName);
CancelHeaderInChunks( pszName, cchName);
} // NAVCOL::CancelHeader()
BOOL
NAVCOL::StoreHeader(IN const CHAR * pszHeader, IN DWORD cchHeader,
IN const CHAR * pszValue, IN DWORD cchValue
)
{
return ( AddEntryToChunks( pszHeader, cchHeader, pszValue, cchValue));
} // NAVCOL::StoreHeader()
BOOL
NAVCOL::StoreHeader(IN const CHAR * pszHeader,
IN const CHAR * pszValue
)
{
return ( StoreHeader( pszHeader, strlen( pszHeader),
pszValue, strlen( pszValue)
)
);
} // NAVCOL::StoreHeader()
VOID
NAVCOL::PrintToBuffer( IN CHAR * pchBuffer, IN OUT LPDWORD pcchMax) const
{
DWORD cb;
PLIST_ENTRY pl;
DBG_ASSERT( pchBuffer != NULL);
DBG_ASSERT( pcchMax != NULL);
// 0. Print the summary of the object
// 1. Print all the Fast Map headers
// 2. Print the rest of the headers
if( 100 < *pcchMax) {
cb = wsprintfA( pchBuffer,
"\nNAVCOL (%08x). cchData = %d (buff = %d/%d)\n"
,
this, m_cchData,
m_cchBuffData, m_buffData.QuerySize()
);
} else {
cb = 100;
}
DWORD cb1 = (cb < *pcchMax) ? *pcchMax - cb : 0;
for ( pl = m_ActiveList.Flink; pl != &m_ActiveList; pl = pl->Flink) {
NAME_VALUE_CHUNK * pnvc =
CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
cb1 = (cb < *pcchMax) ? *pcchMax - cb : 0;
cb += pnvc->PrintToBuffer ( pchBuffer + cb, &cb1);
} // for
pchBuffer[cb] = '\0';
//
// Print the Raw header from buffer ... NYI
//
if ( cb + 2 < *pcchMax ) {
lstrcat( pchBuffer + cb, "\n\n");
cb += 2;
} else {
cb += 2;
}
*pcchMax = cb;
return;
} // NAVCOL::PrintToBuffer()
BOOL
NAVCOL::UpdatePointers(
IN const CHAR * pchOld,
IN DWORD cchLen,
IN const CHAR * pchNew)
{
DBGPRINTF(( DBG_CONTEXT,
"%08x::UpdatePointers( %08x, %d, %08x) a costly function\n",
this, pchOld, cchLen, pchNew));
DBG_ASSERT( pchOld != pchNew); // if this is true why call this function?
if ( pchOld == pchNew) {
return ( TRUE);
}
// 1. Update pointers in the name-value chunk list
PLIST_ENTRY pl;
for ( pl = m_ActiveList.Flink; pl != &m_ActiveList; pl = pl->Flink) {
NAME_VALUE_CHUNK * pnvc =
CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pnvc->UpdatePointers( pchOld, cchLen, pchNew);
} // for
return ( TRUE);
} // NAVCOL::UpdatePointers()
BOOL
NAVCOL::MakeRoomInBuffer( IN DWORD cchReqd)
{
if ( cchReqd > m_buffData.QuerySize()) {
// cache old pointer to update the other pointers properly
LPSTR pszOld = (LPSTR ) m_buffData.QueryPtr();
if ( !m_buffData.Resize( cchReqd, NAVCOL_GROW_BY)) {
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT, "%08x::Unable to allocate %d bytes\n",
this, cchReqd));
}
return ( FALSE);
}
DBG_ASSERT( cchReqd <= m_buffData.QuerySize());
LPSTR pszNew = (LPSTR ) m_buffData.QueryPtr();
if ( pszNew != pszOld) {
// Trouble starts.
// I have to update all the guys pointing inside the old blob
// especially the pointers in
// the range (pszOld to pszOld + m_cchBuffHeaders)
return ( UpdatePointers( pszOld, m_cchBuffData, pszNew));
}
// We are just lucky to be able to have reallocated at same place.
}
return ( TRUE);
} // NAVCOL::MakeRoomInBuffer()
VOID
NAVCOL::Print( VOID) const
{
CHAR pchBuffer[ 20000];
DWORD cchMax = sizeof( pchBuffer);
PrintToBuffer( pchBuffer, &cchMax);
DBGDUMP(( DBG_CONTEXT, pchBuffer));
} // NAVCOL::Print()
CHAR *
NAVCOL::FindValue( IN LPCSTR pszName, OUT LPDWORD pcchValue)
{
DWORD cchName = strlen( pszName);
// 1. Search in the slow list - name-value-chunks
NAME_VALUE_PAIR * pnp = FindValueInChunks( pszName, cchName);
if ( pnp != NULL) {
if ( pcchValue != NULL) {
DBG_ASSERT( pnp->pchValue != NULL);
*pcchValue = pnp->cchValue;
}
return ( (CHAR *) pnp->pchValue);
} else {
SetLastError( ERROR_INVALID_PARAMETER);
}
return ( NULL);
} // NAVCOL::FindValue()
NAME_VALUE_PAIR *
NAVCOL::FindValueInChunks( IN LPCSTR pszName, IN DWORD cchName)
{
PLIST_ENTRY pl;
NAME_VALUE_PAIR * pnp = NULL;
// find a Name-value-pair/chunk that holds this entry.
for ( pl = m_ActiveList.Flink;
(pnp == NULL) && (pl != &m_ActiveList);
pl = pl->Flink) {
NAME_VALUE_CHUNK *
pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pnp = pnc->FindEntry( pszName, cchName);
} // for
return ( pnp);
} // NAVCOL::FindValueInChunks()
VOID
NAVCOL::CancelHeaderInChunks( IN LPCSTR pszName, IN DWORD cchName)
{
PLIST_ENTRY pl;
NAME_VALUE_PAIR * pnp = NULL;
NAME_VALUE_CHUNK * pnc;
// NYI: This function can benefit from better implementation
// instead of moving memory around.
// Since the freq. of use of this func is less, we will not optimize :(
// find the Name-value-pair/chunk that holds this entry.
for ( pl = m_ActiveList.Flink;
(pnp == NULL) && (pl != &m_ActiveList);
pl = pl->Flink) {
pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pnp = pnc->FindEntry( pszName, cchName);
} // for
if ( pnp != NULL) {
// pnp - current item
// pnc - the current chunk
// to cancel the item, just left-shift the array of
// NAME_VALUE_PAIRS in the chunk and reset the m_nPairs value
DBG_ASSERT( (pnp >= pnc->m_rgNVP) &&
(pnp < pnc->m_rgNVP + pnc->m_nPairs));
DBG_ASSERT( (pnc->m_nPairs - (pnp - pnc->m_rgNVP)) >= 1 );
MoveMemory( pnp, (pnp + 1),
((pnc->m_nPairs - 1 - (pnp - pnc->m_rgNVP)) *
sizeof( NAME_VALUE_PAIR))
);
pnc->m_nPairs--;
// NYI: if pnc->m_nPairs == 0,
// we can move this away from the active list
}
return;
} // NAVCOL::CancelHeaderInChunks()
BOOL
NAVCOL::NextPair( IN OUT NAVCOL_ITERATOR * pni,
OUT NAME_VALUE_PAIR ** ppnp
)
{
DBG_ASSERT( pni );
DBG_ASSERT( ppnp );
//
// Find the pair in the chunk
//
return ( NextPairInChunks( pni, ppnp));
} // NAVCOL::NextPair()
BOOL
NAVCOL::NextPairInChunks( IN OUT NAVCOL_ITERATOR * pni,
OUT NAME_VALUE_PAIR ** ppnp
)
{
DBG_ASSERT( pni);
DBG_ASSERT( ppnp);
PLIST_ENTRY pl;
do {
PLIST_ENTRY pl = (PLIST_ENTRY ) pni->dwChunk;
if ( pl == &m_ActiveList) {
break;
}
NAME_VALUE_CHUNK * pnc =
(NAME_VALUE_CHUNK *) CONTAINING_RECORD( pl, NAME_VALUE_CHUNK,
m_listEntry);
if ( pni->dwPair < pnc->m_nPairs) {
// extract the current pair, update pair pointer and return
*ppnp = (NAME_VALUE_PAIR *) (pnc->m_rgNVP + pni->dwPair);
pni->dwPair++;
return ( TRUE);
}
// we could not find any in the current chunk. Move to next chunk.
pni->dwChunk = (DWORD ) pnc->m_listEntry.Flink;
pni->dwPair = 0; // pair # within the chunk
} while ( TRUE);
SetLastError( ERROR_NO_MORE_ITEMS);
return ( FALSE);
} // NAVCOL::NextPairInChunks()
BOOL
NAVCOL::AddEntryToChunks(
IN const CHAR * pszHeader,
IN DWORD cchHeader,
IN const CHAR * pszValue,
IN DWORD cchValue
)
/*++
This function stores the <header, value> pair for headers not found
in the fast-map. It checks to see if the header already exists
with some value. If it does, then the new value is just concatenated
to the old one. Else the new value is stored separately in the first
available free chunk.
If there is no free chunk available, this function also allocates a free
chunk and stores the data in the new chunk.
--*/
{
// Store the header that is not part of the Fast Map
PLIST_ENTRY pl;
NAME_VALUE_CHUNK * pnc;
NAME_VALUE_PAIR * pnp;
NAME_VALUE_CHUNK * pncFirst = NULL;
NAME_VALUE_PAIR * pnpFirst = NULL;
// find a Name-value-pair/chunk that can hold this entry.
for ( pl = m_ActiveList.Flink;
(pl != &m_ActiveList);
pl = pl->Flink) {
BOOL fFound = FALSE;
pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pnp = pnc->FindMatchingOrFreeEntry( pszHeader, cchHeader, &fFound);
if ( fFound ) {
DBG_ASSERT( pnp != NULL);
// pnc points to the chunk containing the matched item
// pnp points to the exact pair that matched up
DBG_ASSERT( (pnp->cchName == cchHeader) &&
(!_strnicmp( pnp->pchName, pszHeader, cchHeader))
);
IF_DEBUG( PARSING) {
DBGPRINTF(( DBG_CONTEXT, "Match For (%s) found at PNP=%08x\n",
pszHeader, pnp));
}
// Concat the given value to the existing value element.
// Nothing more needs to be done
BOOL fRet = ConcatToHolder( &pnp->pchValue, pszValue, cchValue);
if ( fRet) {
// update the length of the datum.
pnp->cchValue += (1 + cchValue); // 1 for the ',' concat sign.
}
return ( fRet);
} else if ( pncFirst == NULL) {
// cache it for later use, if header is never found
pncFirst = pnc;
pnpFirst = pnp;
}
} // for
if ( pncFirst == NULL ) {
// No match found. No free chunk is available.
// Pull a new one from free list or create one
if ( IsListEmpty( &m_FreeList)) {
pncFirst = new NAME_VALUE_CHUNK();
if ( NULL == pncFirst) {
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
return ( FALSE);
}
} else {
// pull one from the free list and use it.
pl = m_FreeList.Flink;
RemoveEntryList( pl);
pncFirst = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pncFirst->Reset();
}
InsertTailList( &m_ActiveList, &pncFirst->m_listEntry);
DBG_ASSERT( pncFirst->m_nPairs == 0);
pnpFirst = ((NAME_VALUE_PAIR * ) pncFirst->m_rgNVP);
}
//
// Store the new <header, value> pair in pnp and increment count of pairs.
//
DBG_ASSERT( NULL != pncFirst);
DBG_ASSERT( NULL != pnpFirst);
pnpFirst->pchName = pszHeader;
pnpFirst->cchName = cchHeader;
pnpFirst->pchValue = pszValue;
pnpFirst->cchValue = cchValue;
pncFirst->m_nPairs++;
DBG_ASSERT( pnc->m_nPairs < MAX_HEADERS_PER_CHUNK);
return ( TRUE);
} // NAVCOL::AddEntryToChunks()
BOOL
NAVCOL::ConcatToHolder( IN LPCSTR * ppsz,
IN LPCSTR pszNew,
IN DWORD cchNew
)
/*++
Given an internal pointer ppsz of the HTTP_HEADERS object,
this function appens the new value to the old value present
using ',' as the concatenation character.
It automatically allocates room and grows buffers, updates pointers, etc
if need be.
--*/
{
BOOL fRet = TRUE;
LPCSTR pszOld = *ppsz;
DBG_ASSERT( NULL != pszOld);
DWORD cchOld = strlen( pszOld);
DWORD cchReqd = cchOld + cchNew + 2;
// Find if we have enough space in the inlined buffer
if ( ( m_cchData + cchReqd < sizeof( m_rcInlinedData))
) {
// Aha we are lucky.
// Make a copy at the end and form concatenated result
*ppsz = m_rcInlinedData + m_cchData;
m_cchData += cchReqd;
} else {
// Clearly we do not have room in the Inlined Header,
// store the stuff in the aux buffer area.
// Find if space is sufficient.
// This will automatically alloc and update pointers
if ( MakeRoomInBuffer( (m_cchBuffData + cchReqd))
){
pszOld = *ppsz; // get the new pointer (since it could have moved)
LPSTR pszBuf = (LPSTR ) m_buffData.QueryPtr();
// we have space at the end of the buffer here. Use this space.
*ppsz = pszBuf + m_cchBuffData;
m_cchBuffData += cchReqd;
} else {
DBGPRINTF(( DBG_CONTEXT,
"Unable to create room for %d characters \n",
m_cchBuffData + cchOld + cchNew + 3));
return ( FALSE);
}
}
// Format the value as := <old> ',' <new>
CopyMemory( (PVOID ) *ppsz, pszOld, cchOld);
((CHAR *) *ppsz)[cchOld] = ','; // concat character
CopyMemory( (PVOID ) (*ppsz + cchOld + 1), pszNew, cchNew + 1);
DBG_ASSERT( fRet == TRUE);
return ( fRet);
} // NAVCOL::ConcatToHolder()
/**************************************************
* PARSER for the HTTP_HEADERS
**************************************************/
extern int _HTTP_LINEAR_SPACE[];
BOOL
NAVCOL::ParseInput( IN const CHAR * pchData,
IN DWORD cchData
)
{
CHAR * pchScan;
const CHAR * pchRequest;
LPSTR pszScan2;
DWORD cReq;
IF_DEBUG( PARSING) {
DBGPRINTF(( DBG_CONTEXT, "%08x::ParseInput( %08x:, %d) \n"
"Input Data:\n%s\n",
this, pchData, cchData, pchData));
}
//
// 1. Skip all the leading spaces and ignore them all.
// We do not need these fields
//
for ( pchScan = (CHAR * ) pchData;
((pchScan < (CHAR * ) pchData + cchData) && isspace( (UCHAR)(*pchScan)));
pchScan++)
;
cchData -= (pchScan - pchData);
//
// 2. Make a copy of the incoming data so that we can own the
// input headers and munge it in our own fashion
// NYI: One can optimize this by selectively copying segments that
// are worth using (values), but that will be costly due to
// multiple small CopyMemory() operations.
//
if ( cchData < sizeof( m_rcInlinedData)) {
pchRequest = m_rcInlinedData;
m_cchData = cchData;
} else {
if ( !m_buffData.Resize( cchData + 4, HH_GROW_BY)) {
return ( FALSE);
}
pchRequest = (const CHAR * ) m_buffData.QueryPtr();
m_cchBuffData = cchData;
}
// 2a. copy the data to the buffer
CopyMemory( (PVOID ) pchRequest, pchScan, cchData);
pchScan = (char * )pchRequest;
//
// 3. Extract all the name-value pairs which are of the form
// name=value and separated by an '&'
//
LPSTR pszHeader = pchScan;
cReq = (PCHAR )pchRequest + cchData - pszHeader;
LPSTR pchEnd = pszHeader + cReq;
LPSTR pszEol, pszValue;
for ( pszEol = pszHeader;
( (pszEol < pchEnd) &&
( (pszEol = (LPSTR)memchr( pszHeader, '&', cReq )) ||
(pszEol = pchEnd))
);
pszHeader = (pszEol + 1), cReq = (pchEnd - pszHeader)
)
{
DWORD cchValue = 0;
int cchName;
// Find the value for given name
if ( pszValue = (LPSTR)memchr( pszHeader, '=', pszEol - pszHeader ) )
{
// reset the '=' sign and make pszValue point ahead, calc name len
cchName = pszValue - pszHeader;
*pszValue++ = '\0';
// NYI: I need to URL unescape the Name/Value pairs
// Terminate the value string
DBG_ASSERT( pszEol > pszValue);
pszEol[0] = '\0';
cchValue = pszEol - pszValue;
} // if value present
else
{
// No value is present. Null Value.
pszValue = &m_chNull;
DBG_ASSERT( cchValue == 0);
// end the name part
cchName = pszEol - pszHeader;
*pszEol = '\0';
}
IF_DEBUG( PARSING ) {
DBGPRINTF((DBG_CONTEXT,
"\t[%s] = %s\n", pszHeader, pszValue ));
}
// Store the name-value pair
BOOL fRet = ( AddEntryToChunks( pszHeader, cchName,
pszValue, cchValue)
);
if ( !fRet) {
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
"Failed to StoreHeader %s in chunks\n",
pszHeader));
}
return ( FALSE);
}
} // for()
return ( TRUE);
} // NAVCOL::ParseInput()
#endif // !REG_DLL
/************************ End of File ***********************/