1533 lines
36 KiB
C++
1533 lines
36 KiB
C++
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name: keys.cpp
|
||
|
||
Abstract:
|
||
|
||
This module contains functions which deal with keys and values
|
||
on inbound form data. Please, see keys.h for details.
|
||
|
||
--*/
|
||
#define WIN32_LEAN_AND_MEAN // the bare essential Win32 API
|
||
#include <windows.h>
|
||
#include <httpext.h>
|
||
|
||
//
|
||
// If your application plans to receive huge amounts of data
|
||
// and you don't want megabytes of memory allocated, define
|
||
// the USE_TEMPORARY_FILES. It is recommended to leave the
|
||
// define line below commented out unless absolutely necessary.
|
||
//
|
||
|
||
//#define USE_TEMPORARY_FILES
|
||
|
||
#include "keys.h"
|
||
|
||
#ifndef USE_MEMORY
|
||
|
||
#ifndef USE_TEMPORARY_FILES
|
||
#define USE_MEMORY
|
||
#endif
|
||
|
||
#endif
|
||
|
||
|
||
//
|
||
// If you want to record errors, modify this macro definition to
|
||
// call your own logging function. This sample does not save
|
||
// error strings.
|
||
//
|
||
#define LOG(errorstring) // OutputDebugString( errorstring ); \
|
||
// OutputDebugString ( "\r\n" )
|
||
|
||
//
|
||
//
|
||
// Intended external interface:
|
||
//
|
||
// GetKeyList Determines if data was sent, and if it was, the
|
||
// data is extracted by GetPostKeys or GetUrlKeys,
|
||
// two private functions within this file. A
|
||
// pointer to a linked list is returned ( as a
|
||
// handle ).
|
||
//
|
||
// GetKeyInfo Returns a pointer to the key name,
|
||
// the length of the data, a flag indicating if
|
||
// the data has control characters in it, and an
|
||
// instance number for duplicate key names.
|
||
//
|
||
// GetKeyBuffer Returns a pointer to the buffer holding the key's
|
||
// data.
|
||
//
|
||
// FindKey Sequentially searches linked list for key name.
|
||
//
|
||
// FreeKeyList Deallocates memory used by the linked list of
|
||
// keys. Also deletes content resources.
|
||
//
|
||
// GetKeyOffset Returns the offset to either the content memory
|
||
// buffer or the content temporary file.
|
||
//
|
||
// GetContentFile Returns a pointer to the temporary file, only
|
||
// when USE_TEMPORARY_FILES is defined.
|
||
//
|
||
// CloseContentFile Closes the content file, normally left open
|
||
// until FreeKeyList is called, available only
|
||
// when USE_TEMPORARY_FILES is defined.
|
||
//
|
||
// OpenContentFile Reopens the content file for additional use
|
||
// by GetKeyBuffer, available only when
|
||
// USE_TEMPORARY_FILES is defined.
|
||
//
|
||
// GetDataBuffer Returns a pointer to the content, only if the
|
||
// USE_TEMPORARY_FILES constant is NOT defined.
|
||
//
|
||
// Helper functions called only in this source file:
|
||
//
|
||
// GetQueryByte Similar to GetPostedByte, this function
|
||
// extracts data from the query string.
|
||
//
|
||
// HexDigitToInt Returns the decimal value of a hex character.
|
||
//
|
||
// GetFirstByte Sets up POSDATA struct and calls GetNextByte.
|
||
// Caller specifies function used to retrieve
|
||
// data, either GetPostedByte or GetQueryByte.
|
||
//
|
||
// GetNextByte Uses GetInboundByte ( specified in GetFirstByte )
|
||
// to retrieve inbound data, and decodes it using
|
||
// the URL encoding rules.
|
||
//
|
||
// BKL_Alloc Allocates memory used in GetPostKeys.
|
||
//
|
||
// BKL_Dealloc Deallocates memory used in GetPostKeys.
|
||
//
|
||
// BKL_Abort Cleans up all resources for abnormal exits from
|
||
// GetPostKeys.
|
||
//
|
||
// IsKeySeparator Returns TRUE if character is one of "=\r\n&\0".
|
||
//
|
||
// BuildKeyList Given a data extraction function ( i.e.
|
||
// GetPostedByte or GetQueryByte ), this function
|
||
// converts all keys into a linked list of POSTKEY
|
||
// structures.
|
||
//
|
||
// GetPostKeys Takes inbound data off the wire by calling
|
||
// BuildKeyList with GetPostedByte as the extraction
|
||
// function.
|
||
//
|
||
// GetUrlKeys Extracts data from the query string by calling
|
||
// BuildKeyList with GetQueryByte as the extraction
|
||
// function.
|
||
//
|
||
// GetPropAddr Calculates the address of the list's properties,
|
||
// appended to the first key in a list.
|
||
//
|
||
// The typedef for the linked list is kept privately in this file,
|
||
// and our interface isolates other source files from the
|
||
// implementation details.
|
||
//
|
||
|
||
//
|
||
// Constants for this source file only
|
||
//
|
||
|
||
#define MAX_KEY_NAME_LENGTH 256 // maximum size of an inbound key name
|
||
#define CONTENT_BUF_LENGTH 8192 // amount of content buffered before
|
||
// WriteFile call
|
||
// ( used for temporary files only )
|
||
|
||
#define GNB_NOTHING_LEFT 0 // GetNextByte return values
|
||
#define GNB_DECODED_CHAR 1
|
||
#define GNB_NORMAL_CHAR 2
|
||
|
||
|
||
//
|
||
// POSDATA struct is used with GetInboundByte to keep
|
||
// track of the position within incoming data.
|
||
// GETINBOUNDBYTE is a function pointer type.
|
||
//
|
||
|
||
typedef struct _tagPOSDATA {
|
||
EXTENSION_CONTROL_BLOCK *pECB;
|
||
int nCurrentPos; // overall position
|
||
|
||
int nBufferLength; // length of buffer
|
||
|
||
int nBufferPos; // position within buffer
|
||
|
||
int nAllocLength; // size of buffer as allocated
|
||
|
||
LPBYTE pData;
|
||
int (*GetInboundByte)(struct _tagPOSDATA * p);
|
||
} POSDATA, *PPOSDATA;
|
||
|
||
typedef int (*GETINBOUNDBYTE)(PPOSDATA p);
|
||
|
||
|
||
#ifdef USE_MEMORY
|
||
|
||
//
|
||
// LISTPROP struct is used to maintain a set of
|
||
// list-wide properties. This implementation
|
||
// uses the properties list to hold a buffer
|
||
// pointer.
|
||
//
|
||
|
||
typedef struct _tagLISTPROP {
|
||
LPBYTE lpbyBuf;
|
||
} LISTPROP, *PLISTPROP;
|
||
|
||
#elif defined USE_TEMPORARY_FILES
|
||
|
||
//
|
||
// This LISTPROP struct holds temporary
|
||
// file information.
|
||
//
|
||
|
||
typedef struct _tagLISTPROP {
|
||
char szTempFileName[MAX_PATH];
|
||
HANDLE hFile;
|
||
} LISTPROP, *PLISTPROP;
|
||
|
||
#endif
|
||
|
||
|
||
// This private helper needs a prototype
|
||
PLISTPROP GetPropAddr( HKEYLIST hKey );
|
||
|
||
|
||
int
|
||
GetPostedByte(
|
||
PPOSDATA pPosData
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
GetPostedByte returns a waiting character that is not
|
||
decoded yet. We have this function to smooth out the
|
||
inbound data: the server gives us blocks of data, one at
|
||
a time, and there can be any number of blocks.
|
||
|
||
For the first call, pPosData->nAllocLength must be zero,
|
||
and pECB must be set.
|
||
|
||
Arguments:
|
||
|
||
pPostData - pointer to POSTDATA struct
|
||
|
||
Returns:
|
||
|
||
incoming byte value or
|
||
-1 to indicate an error
|
||
|
||
--*/
|
||
{
|
||
int nBytesToCopy;
|
||
|
||
// For readability only...
|
||
EXTENSION_CONTROL_BLOCK *pECB;
|
||
|
||
pECB = pPosData->pECB;
|
||
|
||
//
|
||
// Initialize position struct on first call.
|
||
//
|
||
|
||
if ( !pPosData->nAllocLength ) {
|
||
// Initialize the members
|
||
pPosData->nCurrentPos = 0;
|
||
pPosData->nBufferPos = 0;
|
||
pPosData->nBufferLength = 0;
|
||
pPosData->nAllocLength = 0x10000; // 65536 bytes
|
||
|
||
// Allocate the memory
|
||
pPosData->pData = (LPBYTE) HeapAlloc(
|
||
GetProcessHeap( ),
|
||
HEAP_ZERO_MEMORY,
|
||
pPosData->nAllocLength );
|
||
}
|
||
//
|
||
// Was memory allocated? Is it still allocated?
|
||
// If not, return right away.
|
||
//
|
||
|
||
if ( !pPosData->pData ) {
|
||
LOG( "GetPostedByte: Buffer not allocated." );
|
||
return -1;
|
||
}
|
||
//
|
||
// Check for end. Deallocate and return if we're done.
|
||
//
|
||
|
||
if ( (DWORD) pPosData->nCurrentPos == pECB->cbTotalBytes ) {
|
||
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) pPosData->pData );
|
||
pPosData->pData = 0;
|
||
|
||
return -1;
|
||
}
|
||
|
||
//
|
||
// Check for buffer not loaded. Load if necessary.
|
||
//
|
||
|
||
if ( pPosData->nBufferPos == pPosData->nBufferLength ) {
|
||
|
||
//
|
||
// Fill the buffer with new inbound data.
|
||
// Request it via ReadClient if necessary.
|
||
//
|
||
|
||
if ( pECB->cbAvailable < 1 ) {
|
||
|
||
// Calculate how much we should go and get
|
||
nBytesToCopy = pECB->cbTotalBytes - pPosData->nCurrentPos;
|
||
if ( nBytesToCopy > pPosData->nAllocLength ) {
|
||
nBytesToCopy = pPosData->nAllocLength;
|
||
}
|
||
|
||
// Let's go get the data
|
||
if ( !pECB->ReadClient(
|
||
pECB->ConnID,
|
||
pPosData->pData,
|
||
(LPDWORD) & nBytesToCopy
|
||
)) {
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) pPosData->pData );
|
||
pPosData->pData = 0;
|
||
|
||
LOG( "GetPostedByte: Error reading data via ReadClient" );
|
||
return -1;
|
||
}
|
||
}else{
|
||
// Take at most nAllocLength bytes of data
|
||
if ( pECB->cbAvailable > (DWORD) (pPosData->nAllocLength) ) {
|
||
nBytesToCopy = pPosData->nAllocLength;
|
||
}else{
|
||
nBytesToCopy = pECB->cbAvailable;
|
||
}
|
||
|
||
// Copy the inbound data to our buffer
|
||
memcpy(
|
||
pPosData->pData,
|
||
&pECB->lpbData[pPosData->nCurrentPos],
|
||
nBytesToCopy
|
||
);
|
||
|
||
// Account for removed data
|
||
pECB->cbAvailable -= nBytesToCopy;
|
||
}
|
||
|
||
// Our buffer is now full
|
||
pPosData->nBufferLength = nBytesToCopy;
|
||
pPosData->nBufferPos = 0;
|
||
|
||
// Make sure we have something
|
||
if ( !nBytesToCopy ) {
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) pPosData->pData );
|
||
pPosData->pData = 0;
|
||
return -1;
|
||
}
|
||
}
|
||
//
|
||
// Inc current pos, buffer pos, and return a character
|
||
//
|
||
|
||
pPosData->nCurrentPos++;
|
||
pPosData->nBufferPos++;
|
||
return ( (int)pPosData->pData[pPosData->nBufferPos - 1] );
|
||
}
|
||
|
||
|
||
|
||
int
|
||
GetQueryByte(
|
||
IN OUT PPOSDATA pPosData
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
Returns a waiting character that is not
|
||
decoded yet. We have this function to match GetPostedData.
|
||
|
||
For the first call, pPosData->nAllocLength must be zero,
|
||
and pECB must be set.
|
||
|
||
|
||
Arguments:
|
||
|
||
pPostData - points to POSDATA structura
|
||
|
||
Returns:
|
||
|
||
byte value or -1 to indicate an error
|
||
|
||
--*/
|
||
{
|
||
// For readability only...
|
||
EXTENSION_CONTROL_BLOCK *pECB;
|
||
|
||
pECB = pPosData->pECB;
|
||
|
||
//
|
||
// Initialize position struct on first call.
|
||
//
|
||
|
||
if ( !pPosData->nAllocLength ) {
|
||
// Initialize the useful members
|
||
pPosData->nBufferPos = 0;
|
||
pPosData->nBufferLength = lstrlen( (LPCSTR) pECB->lpszQueryString );
|
||
pPosData->nAllocLength = -1;
|
||
|
||
char szMsg[256];
|
||
|
||
wsprintf(
|
||
szMsg,
|
||
"pPosData->nBufferLength=%i",
|
||
pPosData->nBufferLength
|
||
);
|
||
LOG( szMsg );
|
||
}
|
||
|
||
//
|
||
// Check for end. Deallocate and return if we're done.
|
||
//
|
||
|
||
if ( pPosData->nBufferPos == pPosData->nBufferLength ) {
|
||
return -1;
|
||
}
|
||
|
||
//
|
||
// Inc buffer pos and return a character
|
||
//
|
||
|
||
pPosData->nBufferPos++;
|
||
return ( (int)pECB->lpszQueryString[pPosData->nBufferPos - 1] );
|
||
}
|
||
|
||
|
||
//
|
||
// Now that we have GetPostedByte, and GetQueryByte, we can
|
||
// build a more useful function that decodes URL-style
|
||
// encoded characters.
|
||
//
|
||
// Recall that there are two special cases for this encoding:
|
||
//
|
||
// 1. Each plus sign must be converted to a space
|
||
// 2. A percent sign denotes a hex value-encoded character
|
||
//
|
||
// Percents are used to specify characters that are otherwise
|
||
// illegal. This includes percents themselves, ampersands,
|
||
// control characters, and so on.
|
||
//
|
||
// GetNextByte returns the decoded byte, plus a flag indicating
|
||
// normal character, decoded character, or failure. See top of
|
||
// file for return value constants.
|
||
//
|
||
|
||
|
||
int
|
||
HexDigitToInt(
|
||
IN char c
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
HexDigitToInt simply converts a hex-based character to an int.
|
||
|
||
Arguments:
|
||
tc - character to convert
|
||
|
||
Returns:
|
||
binary value of the character (0-15)
|
||
-1 if the character is not hex digit
|
||
|
||
--*/
|
||
{
|
||
if ( c >= '0' && c <= '9' ) {
|
||
return ( c - '0' );
|
||
}
|
||
|
||
if ( tolower( c ) >= 'a' && tolower( c ) <= 'f' ) {
|
||
return ( tolower( c ) - 'a' + 10 );
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
|
||
int
|
||
GetNextByte(
|
||
IN OUT PPOSDATA pPosData,
|
||
OUT char * pc
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
Decode single byte of the input data
|
||
|
||
Arguments:
|
||
pPostData - points to POSDATA struct
|
||
pc - points to variable to accept decoded byte
|
||
|
||
Returns:
|
||
GNB_NORMAL_CHAR, GNB_NOTHING_LEFT or GNB_DECODED_CHAR
|
||
|
||
--*/
|
||
{
|
||
int nChar;
|
||
int nDigit;
|
||
|
||
// Initialize character pointer
|
||
*pc = 0;
|
||
|
||
// Fetch the next inbound character
|
||
nChar = pPosData->GetInboundByte( pPosData );
|
||
if ( nChar == -1 ) {
|
||
return GNB_NOTHING_LEFT;
|
||
}
|
||
|
||
// Plus signs: convert to spaces
|
||
if ( nChar == '+' ) {
|
||
*pc = ' ';
|
||
return GNB_DECODED_CHAR;
|
||
}
|
||
// Percent signs: convert hex values
|
||
else if ( nChar == '%' ) {
|
||
nChar = pPosData->GetInboundByte( pPosData );
|
||
nDigit = HexDigitToInt( nChar );
|
||
if ( nDigit == -1 ) {
|
||
return GNB_NOTHING_LEFT;
|
||
}
|
||
|
||
*pc = ( char ) ( ( UINT ) nDigit << 4 );
|
||
|
||
nChar = pPosData->GetInboundByte( pPosData );
|
||
nDigit = HexDigitToInt( nChar );
|
||
if ( nDigit == -1 ) {
|
||
*pc = 0; // incomplete
|
||
|
||
return GNB_NOTHING_LEFT;
|
||
}
|
||
*pc |= ( char ) ( UINT ) nDigit;
|
||
|
||
return GNB_DECODED_CHAR;
|
||
}
|
||
// Must be normal character then
|
||
*pc = (char) nChar;
|
||
|
||
return GNB_NORMAL_CHAR;
|
||
}
|
||
|
||
|
||
int
|
||
GetFirstByte(
|
||
IN OUT PPOSDATA pPosData,
|
||
IN EXTENSION_CONTROL_BLOCK * pECB,
|
||
OUT char * pc,
|
||
IN GETINBOUNDBYTE GetInboundByte
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
GetFirstByte eliminates the guesswork from initialization.
|
||
We call GetFirstByte with an uninitialized POSDATA structure,
|
||
and we call GetNextByte from there on.
|
||
|
||
Arguments:
|
||
pPosData - points to POSDATA struct to initialize
|
||
pECB - points to the extenstion control block
|
||
pc - points to variable to accept decoded byte
|
||
GetInboundByte - points to function to get incoming bytes
|
||
|
||
|
||
Returns:
|
||
same as GetNextByte()
|
||
|
||
--*/
|
||
{
|
||
// Initialize struct
|
||
pPosData->nAllocLength = 0;
|
||
pPosData->pECB = pECB;
|
||
pPosData->GetInboundByte = GetInboundByte;
|
||
|
||
// Make the call as usual
|
||
return GetNextByte( pPosData, pc );
|
||
}
|
||
|
||
|
||
//
|
||
// Structure used in data processing - the elements of the
|
||
// key list.
|
||
//
|
||
|
||
typedef struct _tagPOSTKEY {
|
||
int nInstance; // used when key name is the same as another,
|
||
// normally 0
|
||
|
||
DWORD dwOffset; // offset into content file
|
||
DWORD dwLength; // length of data
|
||
BOOL bHasCtrlChars; // a character value < 32 is in data
|
||
struct _tagPOSTKEY *pNext; // linked list
|
||
struct _tagPOSTKEY *pHead; // first in linked list
|
||
LPBYTE lpbyBuf; // pointer to the key's data in the list
|
||
// buffer
|
||
|
||
// key string appended to structure
|
||
// for the head key, list properties are appended
|
||
} POSTKEY, *PPOSTKEY;
|
||
|
||
|
||
|
||
//
|
||
// These three helper functions isolate the memory allocation,
|
||
// deallocation and abnormal exit code. They are used only to
|
||
// keep BuildKeyList readable.
|
||
//
|
||
|
||
|
||
BOOL
|
||
BKL_Alloc(
|
||
OUT LPSTR * plpszKey,
|
||
OUT LPBYTE * plpbyBuf
|
||
)
|
||
{
|
||
// Allocate a buffer for the key name
|
||
*plpszKey = (LPSTR) HeapAlloc( GetProcessHeap( ),
|
||
HEAP_ZERO_MEMORY,
|
||
MAX_KEY_NAME_LENGTH );
|
||
|
||
if ( !*plpszKey ) {
|
||
return FALSE;
|
||
}
|
||
|
||
#ifdef USE_MEMORY
|
||
// Init buffer to NULL
|
||
*plpbyBuf = NULL;
|
||
|
||
#elif defined USE_TEMPORARY_FILES
|
||
|
||
// Allocate a buffer for the content
|
||
*plpbyBuf = (LPBYTE) HeapAlloc( GetProcessHeap( ),
|
||
HEAP_ZERO_MEMORY,
|
||
CONTENT_BUF_LENGTH );
|
||
|
||
if ( !*plpbyBuf ) {
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) * plpszKey );
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
void
|
||
BKL_Dealloc(
|
||
IN LPSTR * plpsz,
|
||
IN LPBYTE * plpby
|
||
)
|
||
{
|
||
if ( plpsz && *plpsz ) {
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) * plpsz );
|
||
}
|
||
if ( plpby && *plpby ) {
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) * plpby );
|
||
}
|
||
}
|
||
|
||
//
|
||
// This allows us to clean up... with temporary files we have to close
|
||
// and delete them. Otherwise, we have to free a lot of memory.
|
||
//
|
||
|
||
#ifdef USE_TEMPORARY_FILES
|
||
#define MACRO_AbortCleanup BKL_Abort( pHead, hDataFile, \
|
||
lpszKeyNameBuf, lpbyContentBuf );\
|
||
if ( hDataFile != INVALID_HANDLE_VALUE ) DeleteFile( szTempPath )
|
||
|
||
#elif defined USE_MEMORY
|
||
|
||
#define MACRO_AbortCleanup BKL_Abort( pHead, INVALID_HANDLE_VALUE, \
|
||
lpszKeyNameBuf,lpbyContentBuf )
|
||
#endif
|
||
|
||
|
||
void
|
||
BKL_Abort(
|
||
IN PPOSTKEY pHead,
|
||
IN HANDLE hFile,
|
||
IN LPSTR lpszKey,
|
||
IN LPBYTE lpbyBuf
|
||
)
|
||
{
|
||
if ( pHead ) {
|
||
FreeKeyList( (HKEYLIST) pHead );
|
||
}
|
||
|
||
if ( hFile != INVALID_HANDLE_VALUE ) {
|
||
CloseHandle( hFile );
|
||
}
|
||
|
||
BKL_Dealloc( &lpszKey, &lpbyBuf );
|
||
}
|
||
|
||
|
||
BOOL
|
||
IsKeySeparator(
|
||
char c
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
Identify key separators
|
||
|
||
Arguments:
|
||
c - character
|
||
|
||
Returns:
|
||
TRUE if character is a key separator,
|
||
FALSE otherwise
|
||
|
||
--*/
|
||
{
|
||
return ( c == '=' || c == '\r' || c == '\n' || c == '&' || !c );
|
||
}
|
||
|
||
|
||
PPOSTKEY
|
||
BuildKeyList(
|
||
IN EXTENSION_CONTROL_BLOCK * pECB,
|
||
IN GETINBOUNDBYTE GetInboundByte
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
Now that we have a way to get a decoded byte from the stream,
|
||
we can parse POST data. POST data comes in as:
|
||
|
||
key=data&key=data&key=data\r\n
|
||
|
||
A linked list of keys is established, and the head node
|
||
of the list is returned. A NULL indicates no keys or
|
||
an error.
|
||
|
||
|
||
Arguments:
|
||
pECB - pointer to the extension control block
|
||
GetInboundByte - pointer to function to get input data
|
||
|
||
Returns:
|
||
Pointer to the head node or NULL
|
||
|
||
--*/
|
||
{
|
||
PPOSTKEY pHead = NULL; // head of linked list ( the return val )
|
||
PPOSTKEY pTail = NULL; // last member in linked list
|
||
PPOSTKEY pNewPostKey; // pointer for unlinked, newly allocated
|
||
// objects
|
||
|
||
PPOSTKEY pListWalk; // linked list walking pointer
|
||
PLISTPROP pProp; // pointer to list properties
|
||
LPSTR lpszKeyNameBuf; // pointer to buffer, used in obtaining key
|
||
// name
|
||
|
||
int nPos; // position within key name buffer
|
||
DWORD dwOffset; // offset from start of content buffer or
|
||
// file
|
||
|
||
DWORD dwLength; // length of key data
|
||
char c; // general-purpose character
|
||
int nReturn; // general-purpose return code
|
||
POSDATA pd; // POSDATA struct needed in GetInboundByte
|
||
int nContentPos; // position within content buffer
|
||
LPBYTE lpbyContentBuf; // pointer to buffer
|
||
BOOL bHasCtrlChars; // flag to detect ctrl chars
|
||
|
||
// Call helper to allocate a buffer
|
||
if ( !BKL_Alloc( &lpszKeyNameBuf, &lpbyContentBuf ) ) {
|
||
LOG( "BuildKeyList: Memory allocation failure" );
|
||
return NULL;
|
||
}
|
||
nContentPos = dwOffset = 0;
|
||
|
||
|
||
#ifdef USE_MEMORY
|
||
//
|
||
// Allocate enough memory for all the content.
|
||
// For the POST method, the cbTotalBytes gives us the number
|
||
// of bytes that are being sent by the browser. We can
|
||
// allocate that much but we'll really only use about 75% of it.
|
||
// For the GET method, we need to allocate the size of the
|
||
// query string plus 1.
|
||
//
|
||
|
||
lpbyContentBuf = (LPBYTE) HeapAlloc( GetProcessHeap( ),
|
||
HEAP_ZERO_MEMORY,
|
||
pECB->cbTotalBytes +
|
||
lstrlen( pECB->lpszQueryString ) + 1 );
|
||
|
||
if ( !lpbyContentBuf ) {
|
||
|
||
LOG( "BuildKeyList: Error allocating content memory" );
|
||
BKL_Dealloc( &lpszKeyNameBuf, &lpbyContentBuf );
|
||
|
||
return NULL;
|
||
}
|
||
#elif defined USE_TEMPORARY_FILES
|
||
|
||
//
|
||
// When USE_TEMPORARY_FILES is chosen, we create
|
||
// a temporary file to store all the inbound data.
|
||
// This is done to support huge amounts of inbound
|
||
// data, like file uploads.
|
||
//
|
||
|
||
char szTempDir[MAX_PATH]; // directory of temporary files
|
||
char szTempPath[MAX_PATH]; // path of content file
|
||
HANDLE hDataFile; // handle to content file
|
||
DWORD dwBytesWritten; // used with WriteFile
|
||
|
||
// Get a temp file name
|
||
GetTempPath( MAX_PATH, szTempDir );
|
||
if ( !GetTempFileName( szTempDir, "key", 0, szTempPath ) ) {
|
||
|
||
LOG( "BuildKeyList: Error creating temporary file" );
|
||
BKL_Dealloc( &lpszKeyNameBuf, &lpbyContentBuf );
|
||
|
||
return NULL;
|
||
}
|
||
// Create the content file
|
||
hDataFile = CreateFile( szTempPath,
|
||
GENERIC_READ | GENERIC_WRITE,
|
||
0, // No sharing mode
|
||
NULL, // Default security attribs
|
||
CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
NULL // No template file
|
||
);
|
||
|
||
// Return if an error occured
|
||
if ( hDataFile == INVALID_HANDLE_VALUE ) {
|
||
LOG( "BuildKeyList: Error opening temporary file" );
|
||
MACRO_AbortCleanup;
|
||
return NULL;
|
||
}
|
||
#endif
|
||
|
||
|
||
//
|
||
// 'for' statement detects the start of a valid key name.
|
||
//
|
||
// To do inside 'for' loop:
|
||
// Obtain key name
|
||
// Write data to buffer or content file
|
||
// Create POSTKEY object
|
||
// Update links
|
||
//
|
||
|
||
for ( nReturn = GetFirstByte( &pd, pECB, &c, GetInboundByte );
|
||
nReturn != GNB_NOTHING_LEFT;
|
||
nReturn = GetNextByte( &pd, &c ) ) {
|
||
|
||
// If \r or \n, ignore and continue
|
||
if ( c == '\r' || c == '\n' ) {
|
||
continue;
|
||
}
|
||
|
||
// Get a key name
|
||
nPos = 0;
|
||
while ( !IsKeySeparator( c ) ) {
|
||
if ( nPos < MAX_KEY_NAME_LENGTH ) {
|
||
lpszKeyNameBuf[nPos] = c;
|
||
nPos++;
|
||
}
|
||
nReturn = GetNextByte( &pd, &c );
|
||
if ( nReturn == GNB_NOTHING_LEFT ) { // abrupt end!
|
||
break;
|
||
}
|
||
}
|
||
|
||
// If no equals sign or name too long,
|
||
// we have a browser formatting error
|
||
if ( c != '=' || nPos == MAX_KEY_NAME_LENGTH ) {
|
||
LOG( "BuildKeyList: Browser formatting error" );
|
||
|
||
MACRO_AbortCleanup;
|
||
return NULL;
|
||
}
|
||
|
||
// Truncate the name string, reset data info variables
|
||
lpszKeyNameBuf[nPos] = 0;
|
||
nPos++;
|
||
dwLength = 0;
|
||
bHasCtrlChars = FALSE;
|
||
|
||
//
|
||
// Move the data to the content buffer or file.
|
||
//
|
||
for ( nReturn = GetNextByte( &pd, &c );
|
||
!IsKeySeparator( c ) || nReturn == GNB_DECODED_CHAR;
|
||
nReturn = GetNextByte( &pd, &c ) ) {
|
||
|
||
lpbyContentBuf[nContentPos] = c;
|
||
|
||
nContentPos += sizeof ( char );
|
||
dwLength++;
|
||
|
||
// Check for ctrl chars
|
||
if ( c < 0x20 ) {
|
||
bHasCtrlChars = TRUE;
|
||
}
|
||
|
||
#ifdef USE_TEMPORARY_FILES
|
||
// If we have enough data, write buffer to disk
|
||
if ( nContentPos == CONTENT_BUF_LENGTH ) {
|
||
if ( !WriteFile( hDataFile, lpbyContentBuf,
|
||
nContentPos, &dwBytesWritten, NULL ) ) {
|
||
|
||
LOG( "BuildKeyList: Error writing to content file" );
|
||
MACRO_AbortCleanup;
|
||
return NULL;
|
||
}
|
||
nContentPos = 0;
|
||
}
|
||
#endif
|
||
|
||
} // for( nReturn
|
||
|
||
|
||
#ifdef USE_MEMORY
|
||
//
|
||
// Put a terminating NULL at the end of the key data.
|
||
//
|
||
|
||
lpbyContentBuf[nContentPos] = 0;
|
||
nContentPos++;
|
||
|
||
#elif defined USE_TEMPORARY_FILES
|
||
|
||
// Drain buffer
|
||
if ( nContentPos ) {
|
||
if ( !WriteFile( hDataFile, lpbyContentBuf,
|
||
nContentPos, &dwBytesWritten, NULL ) ) {
|
||
|
||
LOG( "BuildKeyList: Error writing to content file" );
|
||
MACRO_AbortCleanup;
|
||
return NULL;
|
||
}
|
||
nContentPos = 0;
|
||
}
|
||
#endif
|
||
|
||
|
||
// Allocate a POSTKEY object, allocate extra for first key
|
||
if ( pHead ) {
|
||
pNewPostKey = (PPOSTKEY) HeapAlloc(
|
||
GetProcessHeap( ),
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof (POSTKEY) + nPos
|
||
);
|
||
}else{
|
||
pNewPostKey = (PPOSTKEY) HeapAlloc(
|
||
GetProcessHeap( ),
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof (POSTKEY) + nPos +
|
||
sizeof (LISTPROP) );
|
||
|
||
pProp = (PLISTPROP) ( (LPBYTE)pNewPostKey +
|
||
sizeof (POSTKEY) + nPos );
|
||
}
|
||
|
||
// Check for valid pointer
|
||
if ( !pNewPostKey ) {
|
||
LOG( "BuildKeyList: POSTKEY memory allocation failure" );
|
||
MACRO_AbortCleanup;
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Set pNewPostKey members
|
||
//
|
||
|
||
// Set nInstance
|
||
pNewPostKey->nInstance = 0;
|
||
pListWalk = pHead;
|
||
while ( pListWalk ) {
|
||
// Check for duplicate key names
|
||
if ( !lstrcmpi( (LPCSTR) ( &pListWalk[1] ), lpszKeyNameBuf )) {
|
||
pNewPostKey->nInstance++;
|
||
}
|
||
pListWalk = pListWalk->pNext;
|
||
}
|
||
|
||
// Set dwOffset, dwLength, bHasCtrlChars, lpbyBuf
|
||
pNewPostKey->dwOffset = dwOffset;
|
||
pNewPostKey->dwLength = dwLength;
|
||
pNewPostKey->bHasCtrlChars = bHasCtrlChars;
|
||
|
||
#ifdef USE_MEMORY
|
||
pNewPostKey->lpbyBuf = &lpbyContentBuf[dwOffset];
|
||
dwOffset += dwLength + 1;
|
||
|
||
#elif defined USE_TEMPORARY_FILES
|
||
|
||
pNewPostKey->lpbyBuf = NULL;
|
||
dwOffset += dwLength;
|
||
#endif
|
||
|
||
|
||
// Copy key name
|
||
lstrcpy( (LPSTR) ( &pNewPostKey[1] ), lpszKeyNameBuf );
|
||
|
||
// Link
|
||
if ( pTail ) {
|
||
pTail->pNext = pNewPostKey;
|
||
}else{
|
||
|
||
#ifdef USE_TEMPORARY_FILES
|
||
// Copy content file name to list properties
|
||
lstrcpy( pProp->szTempFileName, szTempPath );
|
||
|
||
// Set handle
|
||
pProp->hFile = hDataFile;
|
||
|
||
#elif defined USE_MEMORY
|
||
|
||
// Set content buffer pointer
|
||
pProp->lpbyBuf = lpbyContentBuf;
|
||
#endif
|
||
|
||
// Set head
|
||
pHead = pNewPostKey;
|
||
}
|
||
|
||
pNewPostKey->pNext = NULL;
|
||
pTail = pNewPostKey;
|
||
|
||
pNewPostKey->pHead = pHead; // may point to itself
|
||
|
||
} // for ( nReturn
|
||
|
||
#ifdef USE_TEMPORARY_FILES
|
||
//
|
||
// If content file is empty, close it and delete it
|
||
//
|
||
|
||
if ( !pHead ) {
|
||
LOG( "Empty content file is being deleted." );
|
||
CloseHandle( hDataFile );
|
||
DeleteFile( szTempPath );
|
||
}
|
||
// Free work buffer
|
||
BKL_Dealloc( &lpszKeyNameBuf, &lpbyContentBuf );
|
||
|
||
#elif defined USE_MEMORY
|
||
|
||
// Free work buffer
|
||
BKL_Dealloc( &lpszKeyNameBuf, pHead ? NULL : &lpbyContentBuf );
|
||
#endif
|
||
|
||
|
||
return pHead;
|
||
}
|
||
|
||
|
||
//
|
||
// We are now pretty much done with anything complex. BuildKeyList
|
||
// will do all our parse work, so now we need a few wrappers to
|
||
// make a nice, clean external interface.
|
||
//
|
||
// GetPostKeys calls BuildKeyList with GetPostedByte.
|
||
//
|
||
// GetUrlKeys calls BuildKeyList with GetQueryByte.
|
||
//
|
||
|
||
PPOSTKEY
|
||
GetPostKeys(
|
||
IN EXTENSION_CONTROL_BLOCK * pECB
|
||
)
|
||
{
|
||
return BuildKeyList( pECB, GetPostedByte );
|
||
}
|
||
|
||
PPOSTKEY
|
||
GetUrlKeys(
|
||
IN EXTENSION_CONTROL_BLOCK * pECB
|
||
)
|
||
{
|
||
return BuildKeyList( pECB, GetQueryByte );
|
||
}
|
||
|
||
|
||
PLISTPROP
|
||
GetPropAddr(
|
||
IN HKEYLIST hKey
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
GetPropAddr returns the address of the end of
|
||
the first key. We stuff list properties there.
|
||
This implementation of keys.cpp keeps a pointer
|
||
to the content buffer. The second version ( used
|
||
in IS2WCGI ) appends a temporary file name
|
||
to the first key.
|
||
|
||
Arguments:
|
||
hKey - pointer to a key list
|
||
|
||
Returns:
|
||
The address of the end of the first key
|
||
|
||
--*/
|
||
{
|
||
LPCSTR lpszKeyName;
|
||
PPOSTKEY pHead;
|
||
|
||
// Safety
|
||
if ( !hKey ) {
|
||
return NULL;
|
||
}
|
||
|
||
// ContentPath follows POSTKEY struct and key name
|
||
pHead = (PPOSTKEY) hKey;
|
||
pHead = pHead->pHead;
|
||
|
||
lpszKeyName = (LPCSTR) ( &pHead[1] );
|
||
|
||
return (PLISTPROP) ( lpszKeyName + lstrlen( lpszKeyName ) + 1 );
|
||
}
|
||
|
||
|
||
HKEYLIST
|
||
GetKeyList(
|
||
IN EXTENSION_CONTROL_BLOCK * pECB
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
Examines the method and calls GetPostKeys or GetUrlKeys,
|
||
whichever is relevant.
|
||
|
||
|
||
Arguments:
|
||
pECB - points to the extension control block
|
||
|
||
Returns:
|
||
GetPropAddr returns the address of the end of
|
||
the first key. We stuff list properties there.
|
||
This implementation of keys.cpp keeps a pointer
|
||
to the content buffer. The second version ( used
|
||
in IS2WCGI ) appends a temporary file name
|
||
to the first key.
|
||
|
||
--*/
|
||
{
|
||
if ( !lstrcmpi( pECB->lpszMethod, "POST" ) ) {
|
||
LOG( "Method=POST" );
|
||
return (HKEYLIST) GetPostKeys( pECB );
|
||
}else if ( !lstrcmpi( pECB->lpszMethod, "GET" ) ) {
|
||
LOG( "Method=GET" );
|
||
return (HKEYLIST) GetUrlKeys( pECB );
|
||
}
|
||
LOG( "Unknown method" );
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
HKEYLIST
|
||
GetKeyInfo(
|
||
IN HKEYLIST hKey,
|
||
OUT LPCSTR * plpszKeyName,
|
||
OUT LPDWORD pdwLength,
|
||
OUT BOOL * pbHasCtrlChars,
|
||
OUT LPINT pnInstance
|
||
)
|
||
//
|
||
// GetKeyInfo is a wrapper for the POSTKEY linked list.
|
||
// It returns the members of the supplied POSTKEY object.
|
||
//
|
||
{
|
||
PPOSTKEY pPostKey;
|
||
|
||
// Safety
|
||
if ( !hKey ) {
|
||
return NULL;
|
||
}
|
||
|
||
pPostKey = (PPOSTKEY) hKey;
|
||
|
||
// Set the data members
|
||
if ( plpszKeyName )
|
||
*plpszKeyName = ( LPCSTR ) ( &pPostKey[1] );
|
||
if ( pdwLength )
|
||
*pdwLength = pPostKey->dwLength;
|
||
if ( pbHasCtrlChars )
|
||
*pbHasCtrlChars = pPostKey->bHasCtrlChars;
|
||
if ( pnInstance )
|
||
*pnInstance = pPostKey->nInstance;
|
||
|
||
// Return a handle to the next object in the list
|
||
return ( ( HKEYLIST ) pPostKey->pNext );
|
||
}
|
||
|
||
|
||
|
||
#ifdef USE_MEMORY
|
||
LPBYTE
|
||
GetKeyBuffer(
|
||
IN HKEYLIST hKey
|
||
)
|
||
{
|
||
//
|
||
// We have two versions of this function because
|
||
// we may want to use file i/o when the extension
|
||
// deals with massive amounts of inbound data
|
||
// ( like multi-megabyte uploads ).
|
||
//
|
||
|
||
//
|
||
// This version uses a memory buffer.
|
||
//
|
||
|
||
PPOSTKEY pKey;
|
||
|
||
// Safety
|
||
if ( !hKey ) {
|
||
return NULL;
|
||
}
|
||
|
||
pKey = (PPOSTKEY) hKey;
|
||
|
||
return (LPBYTE) pKey->lpbyBuf;
|
||
}
|
||
|
||
#elif defined USE_TEMPORARY_FILES
|
||
|
||
LPBYTE
|
||
GetKeyBuffer(
|
||
IN HKEYLIST hKey
|
||
)
|
||
{
|
||
//
|
||
// This version uses slow temporary files.
|
||
//
|
||
|
||
PLISTPROP pProp;
|
||
PPOSTKEY pKey;
|
||
DWORD dwRead;
|
||
|
||
// Get pointer to list properties
|
||
pProp = GetPropAddr( hKey );
|
||
|
||
// Safety
|
||
if ( !pProp ) {
|
||
return NULL;
|
||
}
|
||
|
||
pKey = (PPOSTKEY) hKey;
|
||
|
||
// Check if memory was already loaded for this key
|
||
if ( pKey->lpbyBuf ) {
|
||
return pKey->lpbyBuf;
|
||
}
|
||
|
||
// If not, let's allocate memory and do a ReadFile
|
||
pKey->lpbyBuf = (LPBYTE) HeapAlloc( GetProcessHeap( ),
|
||
HEAP_ZERO_MEMORY,
|
||
pKey->dwLength + 1 );
|
||
if ( !pKey->lpbyBuf ) {
|
||
LOG( "GetKeyBuffer: HeapAlloc failed" );
|
||
return NULL;
|
||
}
|
||
// Do the ReadFile
|
||
SetFilePointer( pProp->hFile, pKey->dwOffset, NULL, FILE_BEGIN );
|
||
if ( !ReadFile( pProp->hFile, pKey->lpbyBuf,
|
||
pKey->dwLength, &dwRead, NULL ) ||
|
||
dwRead != pKey->dwLength ) {
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) pKey->lpbyBuf );
|
||
pKey->lpbyBuf = NULL;
|
||
|
||
LOG( "GetKeyBuffer: ReadFile failed" );
|
||
return NULL;
|
||
}
|
||
return pKey->lpbyBuf;
|
||
}
|
||
#endif
|
||
|
||
|
||
HKEYLIST
|
||
FindKey(
|
||
IN HKEYLIST hKeyList,
|
||
IN LPCSTR lpszSearchName
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
FindKey sequentially searches the linked list for a given key.
|
||
|
||
Arguments:
|
||
|
||
hKeyList - points to key list
|
||
lpszSearchName - points to a key name to find
|
||
|
||
Returns:
|
||
|
||
The return handle points to the element within the linked list.
|
||
Use it in GetKeyInfo, but not FreeKeyList.
|
||
|
||
--*/
|
||
{
|
||
PPOSTKEY pFindKey;
|
||
|
||
pFindKey = (PPOSTKEY) hKeyList;
|
||
while ( pFindKey ) {
|
||
if ( !lstrcmpi( lpszSearchName, ( LPCSTR ) ( &pFindKey[1] ) ) ) {
|
||
return ( ( HKEYLIST ) pFindKey );
|
||
}
|
||
|
||
pFindKey = pFindKey->pNext;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
void
|
||
FreeKeyList(
|
||
IN HKEYLIST hHeadKey
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
FreeKeyList deallocates all the objects in the key list.
|
||
The content file is also deleted.
|
||
|
||
Arguments:
|
||
hHeadKey - points to the list head
|
||
|
||
--*/
|
||
{
|
||
PPOSTKEY pObject;
|
||
PPOSTKEY pDel;
|
||
PLISTPROP pProp;
|
||
|
||
// Safety
|
||
if ( !hHeadKey ) {
|
||
return;
|
||
}
|
||
|
||
#ifdef USE_TEMPORARY_FILES
|
||
// Close the content file
|
||
CloseContentFile( hHeadKey );
|
||
|
||
// delete the content file
|
||
pProp = GetPropAddr( hHeadKey );
|
||
DeleteFile( pProp->szTempFileName );
|
||
|
||
#elif defined USE_MEMORY
|
||
|
||
// delete content
|
||
pProp = GetPropAddr( hHeadKey );
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) pProp->lpbyBuf );
|
||
#endif
|
||
|
||
// delete all objects in the list
|
||
pObject = (PPOSTKEY) hHeadKey;
|
||
pObject = pObject->pHead;
|
||
while ( pObject ) {
|
||
|
||
#ifdef USE_TEMPORARY_FILES
|
||
//
|
||
// Free each buffer when using temporary files
|
||
//
|
||
|
||
if ( pObject->lpbyBuf )
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) pObject->lpbyBuf );
|
||
#endif
|
||
|
||
pDel = pObject;
|
||
pObject = pObject->pNext;
|
||
|
||
HeapFree( GetProcessHeap( ), 0, (LPVOID) pDel );
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
GetKeyOffset(
|
||
IN HKEYLIST hKey
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
GetKeyOffset returns the offset of a key into the internal
|
||
buffer or temporary file. This is provided for IS2WCGI
|
||
so it can return an offset within the content file.
|
||
|
||
Arguments:
|
||
|
||
hKey - points to a key
|
||
|
||
Returns:
|
||
|
||
Offset of a key or NULL if no key provided
|
||
|
||
--*/
|
||
{
|
||
// Safety
|
||
if ( !hKey )
|
||
return NULL;
|
||
|
||
return ( (PPOSTKEY) hKey )->dwOffset;
|
||
}
|
||
|
||
|
||
#ifdef USE_TEMPORARY_FILES
|
||
|
||
LPCSTR
|
||
GetContentFile(
|
||
IN HKEYLIST hKeyList
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
GetContentFile returns a pointer to the name of the
|
||
temporary file. This is provided for the IS2WCGI
|
||
sample.
|
||
|
||
Arguments:
|
||
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
{
|
||
PLISTPROP pProp;
|
||
|
||
// safety
|
||
if ( !hKeyList )
|
||
return NULL;
|
||
|
||
pProp = GetPropAddr( hKeyList );
|
||
|
||
return ( LPCSTR ) pProp->szTempFileName;
|
||
}
|
||
|
||
|
||
void
|
||
CloseContentFile(
|
||
IN HKEYLIST hKey
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
CloseContentFile forces the content file to be closed. This
|
||
allows you to pass the file to something else that may open
|
||
it. Call OpenContentFile before calling any other key
|
||
function.
|
||
|
||
Arguments:
|
||
|
||
--*/
|
||
{
|
||
PLISTPROP pProp;
|
||
|
||
if ( !hKey )
|
||
return;
|
||
|
||
pProp = GetPropAddr( hKey );
|
||
if ( pProp->hFile != INVALID_HANDLE_VALUE ) {
|
||
CloseHandle( pProp->hFile );
|
||
pProp->hFile = INVALID_HANDLE_VALUE;
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
OpenContentFile(
|
||
IN HKEYLIST hKey
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
OpenContentFile forces the content file to be reopened.
|
||
GetKeyBuffer will fail if the content file was closed by
|
||
CloseContentFile, but not reopened.
|
||
|
||
Arguments:
|
||
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
{
|
||
PLISTPROP pProp;
|
||
|
||
if ( !hKey )
|
||
return;
|
||
|
||
pProp = GetPropAddr( hKey );
|
||
|
||
if ( pProp->hFile != INVALID_HANDLE_VALUE )
|
||
return;
|
||
|
||
// Create the content file
|
||
pProp->hFile = CreateFile( pProp->szTempFileName,
|
||
GENERIC_READ | GENERIC_WRITE,
|
||
0, // No sharing mode
|
||
NULL, // Default security attribs
|
||
OPEN_EXISTING,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
NULL );
|
||
}
|
||
|
||
#elif defined USE_MEMORY
|
||
|
||
|
||
LPBYTE
|
||
GetDataBuffer(
|
||
IN HKEYLIST hKeyList
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
GetBufferPointer returns a pointer to the buffer used
|
||
for content storage.
|
||
|
||
Arguments:
|
||
|
||
hKeyList - points to a key list
|
||
|
||
Returns:
|
||
|
||
pointer to the content buffer or NULL
|
||
|
||
--*/
|
||
{
|
||
PLISTPROP pProp;
|
||
|
||
// safety
|
||
if ( !hKeyList )
|
||
return NULL;
|
||
|
||
pProp = GetPropAddr( hKeyList );
|
||
|
||
return pProp->lpbyBuf;
|
||
}
|
||
#endif
|