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.
 
 
 
 
 
 

1389 lines
42 KiB

//
// Microsoft Corporation - Copyright 1997
//
//
// MULTPARS.CPP - Parses incoming stream for files
//
#include "pch.h"
// Local globals
const char g_cszBoundaryKeyword[] = "boundary="; // don't include space
const DWORD g_cbBoundaryKeyword = sizeof( g_cszBoundaryKeyword ) - 1;
const char g_cszCheck[] = "check="; // don't include space
const DWORD g_cbCheck = sizeof( g_cszCheck ) - 1;
const char g_cszBoundaryIndicator[] = "\r\n--";
const DWORD g_cbBoundaryIndicator = sizeof( g_cszBoundaryIndicator ) - 1;
const char g_cszExtraBytes[] = "Extra bytes";
const char g_cszBodyData[] = "Body Data";
//
// Parsing table
//
PARSETABLE g_LexTable[ ] = {
// Parser search string, Lexicon ,Length, Color , Comment
{ (LPSTR)g_cszBoundaryIndicator, LEX_BOUNDARY , 0, 0xFFFFFF, "Boundary String" },
{ (LPSTR)g_cszBoundaryIndicator, LEX_EOT , 0, 0xFFFFFF, "Ending Boundary" },
{ (LPSTR)g_cszBoundaryIndicator +2, LEX_STARTBOUNDARY, 0, 0xFFFFFF, "Starting Boundary" },
// must be after LEX_BOUNDARY
{ "\r\n", LEX_CRLF , 0, 0x7F7F7F, NULL },
{ "content-disposition:", LEX_CONTENTDISP , 0, 0x0000FF, "Header Field" },
{ "content-type:", LEX_CONTENTTYPE , 0, 0x0000FF, "Header Field" },
{ "name=", LEX_NAMEFIELD , 0, 0x0000FF, "Field Param" },
{ "filename=", LEX_FILENAMEFIELD , 0, 0x0000FF, "Field Param" },
// MIME types
{ "multipart", LEX_MULTIPART , 0, 0x003FFF, "MIME Type" },
{ "text", LEX_TEXT , 0, 0x003FFF, "MIME Type" },
{ "application", LEX_APPLICATION , 0, 0x003FFF, "MIME Type" },
{ "audio", LEX_AUDIO , 0, 0x003FFF, "MIME Type" },
{ "image", LEX_IMAGE , 0, 0x003FFF, "MIME Type" },
{ "message", LEX_MESSAGE , 0, 0x003FFF, "MIME Type" },
{ "video", LEX_VIDEO , 0, 0x003FFF, "MIME Type" },
{ "x-", LEX_MIMEEXTENSION , 0, 0x003FFF, "MIME Extension" },
{ "iana-", LEX_MIMEEXTENSION , 0, 0x003FFF, "MIME Extension" },
// MIME subtypes
{ "form-data", LEX_FORMDATA , 0, 0x003FFF, "MIME Subtype" },
{ "attachment", LEX_ATTACHMENT , 0, 0x003FFF, "MIME Subtype" },
{ "mixed", LEX_MIXED , 0, 0x003FFF, "MIME Subtype" },
{ "plain", LEX_PLAIN , 0, 0x003FFF, "MIME Subtype" },
{ "x-msdownload", LEX_XMSDOWNLOAD , 0, 0x003FFF, "MIME Subtype" },
{ "octet-stream", LEX_OCTETSTREAM , 0, 0x003FFF, "MIME Subtype" },
{ "binary", LEX_BINARY , 0, 0x003FFF, "MIME Subtype" },
// special chacacters
{ " ", LEX_SPACE , 0, 0x7F7F7F, NULL },
// tabs are treated as spaces
{ "\t", LEX_SPACE , 0, 0x7F7F7F, NULL },
{ "/", LEX_SLASH , 0, 0x003FFF, NULL },
{ "\"", LEX_QUOTE , 0, 0x000000, NULL },
{ ";", LEX_SEMICOLON , 0, 0x7F7F7F, NULL },
{ "(", LEX_BEGIN_COMMENT , 0, 0x7F7F7F, "Comment" },
{ ")", LEX_END_COMMENT , 0, 0x7F7F7F, NULL },
// end of table
{ NULL, LEX_UNKNOWN , 0, 0x7F7F7F, NULL }
// NOTE: Length should always be zero. We will calculate this the first
// time we encounter the string as save it here to be used on
// future passes.
};
//
// Constructor / Destructor
//
CMultipartParse::CMultipartParse(
LPECB lpEcb,
LPSTR *lppszOut,
LPSTR *lpszDebug,
LPDUMPTABLE lpDT )
:CBase( lpEcb, lppszOut, lpszDebug, lpDT )
{
DebugMsg( lpszOut, g_cszTableHeader, "\
(NOTE: To turn on detailed debugging information, add '?debug' to the end of action URL in the orginating HTML file.)\
<br>\
<H2>Multipart Form Data (METHOD=POST, ENCTYPE=MULTIPART/FORM-DATA)</H2>\
",
"TBLMULTIFORM" );
_cbDT = 0; // empty DUMPTABLE
} // CMultipartParse( )
CMultipartParse::~CMultipartParse( )
{
if ( _lpszBoundary )
{
GlobalFree( _lpszBoundary );
}
} // ~CMultipartParse( )
//
// METHODS
//
//
// What: PreParse
//
// Desc: Parses incoming form from the root header. It is assumed that by
// this point that we are parsing a MUTLIPART / FORM-DATA POST.
//
BOOL CMultipartParse::PreParse( LPBYTE lpbData, LPDWORD lpdwParsed )
{
BOOL fReturn;
LPSTR lpszHeader;
TraceMsg( TF_FUNC | TF_PARSE, "PreParse( )" );
_lpbLastParse = _lpbParse = _lpbData = lpbData;
// Grab content_type from server header
fReturn = GetServerVarString( lpEcb, "CONTENT_TYPE", &lpszHeader, NULL );
if ( !fReturn )
goto Cleanup;
// Create the boundary string that we will be searching for
fReturn = GetBoundaryString( (LPBYTE) lpszHeader );
GlobalFree( lpszHeader );
if ( !fReturn )
goto Cleanup;
// Parse body headers
fReturn = ParseBody( );
Cleanup:
// End table output
StrCat( lpszOut, g_cszTableEnd );
// End the DUMPTABLE
lpDT[ _cbDT ].lpAddr = NULL; // indicates EOTable
*lpdwParsed = ( _lpbParse - _lpbData );
TraceMsg( TF_FUNC | TF_PARSE, "PreParse( ) Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // PreParse( )
//
// What: ParseBody
//
// Desc: Body header parsing state entered. We start by finding the
// next boundary string which indicates the beginning of a
// body header.
//
BOOL CMultipartParse::ParseBody( )
{
BOOL fReturn = TRUE; // assume success;
DWORD eLex;
BOOL fStartBoundaryOk = TRUE;
TraceMsg( TF_FUNC | TF_PARSE, "ParseBody( )" );
// Bypass possible crap
eLex = Lex( );
while (( eLex != LEX_BOUNDARY ) && ( eLex !=LEX_EOT ) && ( eLex != LEX_STARTBOUNDARY ))
eLex = Lex( );
while (( fReturn) && ((DWORD) ( _lpbParse - _lpbData ) != lpEcb->cbTotalBytes ))
{
switch ( eLex )
{
case LEX_STARTBOUNDARY:
if ( !fStartBoundaryOk )
{
DebugMsg( lpszDebug, "Found a mulformed boundary string. Missing preceeding CRLF at %u (0x%x) bytes.\n",
( _lpbParse - _lpbData ), ( _lpbParse - _lpbData ) );
fReturn = FALSE;
break;
}
// fall thru
case LEX_BOUNDARY:
DebugMsg( lpszDebug, "Found boundary. Content starts at %u (0x%x) bytes.\n",
( _lpbParse - _lpbData ), ( _lpbParse - _lpbData ) );
fReturn = BodyHeader( );
break;
case LEX_EOT:
{
DWORD cbParsed = _lpbParse - _lpbData;
fReturn = ( cbParsed == lpEcb->cbTotalBytes );
DebugMsg( lpszDebug, "EOT found.\n" );
DebugMsg( lpszDebug, "TotalBytesReceived == BytesParsed? %s \n",
BOOLTOSTRING( fReturn ) );
if ( !fReturn )
{
DebugMsg( lpszDebug, "Header 'Content-length': %u bytes. Parsed: %u bytes\n",
lpEcb->cbTotalBytes, ( _lpbParse - _lpbData ) );
DebugMsg( lpszDebug, "( Note: If bytes parsed greater than 'content-length', it \
is possible that the server read the entire packet in its first pass and \
that the actual packet size is larger than indicated by the content-length \
header field entry. )\n");
}
}
goto Cleanup;
default:
DebugMsg( lpszDebug, "Did not find a boundary string after %u (0x%x) bytes.\n",
_lpbParse - _lpbData, _lpbParse - _lpbData );
fReturn = FALSE;
goto Cleanup;
}
fStartBoundaryOk = FALSE;
eLex = Lex( );
} // while ( _lpbParse - _lpbData )
DebugMsg( lpszDebug, "Parsed %u (0x%x) bytes.\n", ( _lpbParse - _lpbData ), ( _lpbParse - _lpbData ) );
Cleanup:
TraceMsg( TF_FUNC | TF_PARSE, "ParseBody( ) Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // ParseBody( )
//
// What: BodyHeader
//
// Desc: State has entered the bodyheader.
//
// Return: TRUE unless a unrecognized token is found.
//
BOOL CMultipartParse::BodyHeader( )
{
BOOL fReturn = TRUE;
BODYHEADERINFO sBHI;
LEXICON eLex;
TraceMsg( TF_FUNC | TF_PARSE, "BodyHeader()" );
// defaults
sBHI.eContentDisposition = LEX_FORMDATA;
sBHI.lpszNameField = NULL;
sBHI.lpszFilenameField = NULL;
sBHI.lpszBodyContents = NULL;
sBHI.dwContentType = LEX_TEXT;
sBHI.dwContentSubtype = LEX_PLAIN;
while ( fReturn )
{
eLex = Lex( );
switch ( eLex )
{
// expected
case LEX_CONTENTDISP:
fReturn = ContentDisposition( &sBHI );
break;
case LEX_CONTENTTYPE:
fReturn = ContentType( &sBHI );
break;
case LEX_CRLF: // indicate end of header
goto EndofHeader;
// ignored
case LEX_SPACE:
break;
case LEX_BEGIN_COMMENT:
fReturn = HandleComments( );
break; // ingored
default:
DebugMsg( lpszDebug, "Unexpected '%s' found at %u (0x%x) bytes.\n",
FindTokenName( eLex ), ( _lpbParse - _lpbData ), ( _lpbParse - _lpbData ) );
fReturn = FALSE;
break;
}
}
EndofHeader:
// now retrieve the content...
if ( fReturn )
{
fReturn = BodyContent( &sBHI );
}
// add results to output table
if ( sBHI.lpszNameField )
{
DebugMsg( lpszOut, "<TR ID=TR%s><TD ID=TD%s>%s</TD><TD ID=%s>",
sBHI.lpszNameField, sBHI.lpszNameField,
sBHI.lpszNameField, sBHI.lpszNameField );
}
else
{
DebugMsg( lpszOut, "<TR><TD> 'not given' </TD><TD>" );
}
if ( sBHI.lpszFilenameField )
{
DebugMsg( lpszOut, "%s", sBHI.lpszFilenameField );
}
else if ( sBHI.lpszBodyContents )
{
DebugMsg( lpszOut, "%s", sBHI.lpszBodyContents );
}
else
{
DebugMsg( lpszOut, "'unknown'" );
}
DebugMsg( lpszOut, "</TD></TR>" );
// Free alloced memory
if ( sBHI.lpszFilenameField )
{
GlobalFree( sBHI.lpszFilenameField );
}
if ( sBHI.lpszNameField )
{
GlobalFree( sBHI.lpszNameField );
}
if ( sBHI.lpszBodyContents )
{
GlobalFree( sBHI.lpszBodyContents );
}
TraceMsg( TF_FUNC | TF_PARSE, "BodyHeader() Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // BodyHeader( )
//
// What: ContentDisposition
//
// Desc: Content-Disposition state reached. Parse header entry.
//
// In/Out: lpBHI is a pointer to the body header information structure
// that will be filled in as arguments are parsed.
//
// Return: TRUE unless a unrecognized token is found.
//
BOOL CMultipartParse::ContentDisposition( LPBODYHEADERINFO lpBHI )
{
BOOL fReturn = TRUE;
LEXICON eLex;
TraceMsg( TF_FUNC | TF_PARSE, "ContentDisposition( lpBHI=0x%x )",
lpBHI );
// What forms do we support?
while ( fReturn )
{
eLex = Lex( );
switch ( eLex )
{
// supported
case LEX_FORMDATA:
lpBHI->eContentDisposition = eLex;
break; // LEX_FORMDATA
// unsupported
case LEX_ATTACHMENT:
DebugMsg( lpszDebug, "'%s' (Multiple files) not supported (yet). Aborting...\n",
FindTokenName( LEX_ATTACHMENT ) );
fReturn = FALSE;
break; // LEX_ATTACHMENT
// possible optional fields
case LEX_NAMEFIELD:
fReturn = GetQuotedString( &lpBHI->lpszNameField );
DebugMsg( lpszDebug, "Name Field='%s'\n", lpBHI->lpszNameField );
break;
case LEX_FILENAMEFIELD:
fReturn = GetQuotedString( &lpBHI->lpszFilenameField );
DebugMsg( lpszDebug, "Filename Field='%s'\n", lpBHI->lpszFilenameField );
break;
// ignored
case LEX_SPACE:
case LEX_SEMICOLON:
break;
case LEX_BEGIN_COMMENT:
fReturn = HandleComments( );
break; // ingored
case LEX_CRLF:
eLex = Lex( );
if ( eLex != LEX_SPACE )
{
fReturn = BackupLex( eLex );
goto Cleanup; // end of field
}
// otherwise we ignore the CRLF and keep going
break;
// unexpected things
default:
DebugMsg( lpszDebug, "Unexpected '%s' found at %u (0x%x) bytes.\n",
FindTokenName( eLex), ( _lpbParse - _lpbData ), ( _lpbParse - _lpbData ) );
fReturn = FALSE;
}
} // while eLex
Cleanup:
TraceMsg( TF_FUNC | TF_PARSE, "ContentDisposition() Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // ContentDisposition( )
//
// What: ContentType
//
// Desc: Content-Type state reached. Parse header entry.
//
// Return: TRUE unless a unrecognized token is found.
//
BOOL CMultipartParse::ContentType( LPBODYHEADERINFO lpBHI )
{
BOOL fReturn = TRUE;
BOOL fAfterSlash = FALSE;
LEXICON eLex;
TraceMsg( TF_FUNC | TF_PARSE, "ContentType( lpBHI=0x%x )",
lpBHI );
while ( fReturn )
{
eLex = Lex( );
switch ( eLex )
{
// MIME types
case LEX_MULTIPART:
case LEX_TEXT:
case LEX_APPLICATION:
case LEX_AUDIO:
case LEX_IMAGE:
case LEX_MESSAGE:
case LEX_VIDEO:
lpBHI->dwContentType = eLex;
break;
// separator
case LEX_SLASH:
fAfterSlash = TRUE;
break;
// MIME subtypes
case LEX_FORMDATA:
case LEX_ATTACHMENT:
case LEX_MIXED:
case LEX_PLAIN:
case LEX_XMSDOWNLOAD:
case LEX_OCTETSTREAM:
case LEX_BINARY:
lpBHI->dwContentSubtype = eLex;
break;
// these are prefixes (like "x-") so we ignore the rest of the
// token name.
case LEX_MIMEEXTENSION:
fReturn = GetToken( ); // ignore
if ( !fAfterSlash )
{
lpBHI->dwContentType = eLex;
}
else
{
lpBHI->dwContentSubtype = eLex;
}
break;
// ignored
case LEX_SPACE:
case LEX_SEMICOLON:
break;
case LEX_BEGIN_COMMENT:
fReturn = HandleComments( );
break; // ingored
case LEX_CRLF:
eLex = Lex( );
if ( eLex != LEX_SPACE )
{
fReturn = BackupLex( eLex );
goto Cleanup; // end of field
}
// otherwise we ignore the CRLF and keep going
break;
default:
if ( !fAfterSlash )
{
DebugMsg( lpszDebug, "Unexpected '%s' found at %u (0x%x) bytes.\n",
FindTokenName( eLex ), ( _lpbParse - _lpbData ), ( _lpbParse - _lpbData ) );
fReturn = FALSE;
}
else
{
fReturn = GetToken( );
}
} // switch eLex
} // while fReturn
Cleanup:
TraceMsg( TF_FUNC | TF_PARSE, "ContentType() Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // ContentType( )
//
// What: GetBoundaryString
//
// Desc: Searches CONTENT_TYPE for boundary string and copies into
// buffer (GlobalAlloced).
//
// Return: TRUE is boundary found and copied, otherwise FALSE.
//
BOOL CMultipartParse::GetBoundaryString( LPBYTE lpbData )
{
BOOL fReturn;
LPSTR lpstr;
TraceMsg( TF_FUNC | TF_PARSE, "GetBoundaryString( )" );
// assume failure
_lpszBoundary = NULL;
_cbBoundary = 0;
fReturn = FALSE;
// find the boundary keyword
lpstr = StrStrI( (LPSTR) lpbData, g_cszBoundaryKeyword );
if ( !lpstr )
goto Cleanup;
// move forward of keyword
lpstr += g_cbBoundaryKeyword;
// point to boundary string
_lpszBoundary = StrDup( lpstr );
_cbBoundary = lstrlen( _lpszBoundary );
DebugMsg( lpszDebug, "Boundary String : 'CRLF--%sCRLF' %u (0x%x) bytes\n",
_lpszBoundary, _cbBoundary + 6, _cbBoundary + 6 );
DebugMsg( lpszDebug, "EndOfContent Str: 'CRLF--%s--CRLF' %u (0x%x) bytes\n",
_lpszBoundary, _cbBoundary + 8, _cbBoundary + 8 );
fReturn = TRUE;
Cleanup:
// _lpszBoundary will be cleaned up on the destruction of the
// class.
TraceMsg( TF_FUNC | TF_PARSE, "GetBoundaryString( ) Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // GetBoundaryString( )
//
// What: HandleComments
//
// Desc: pull down comments and discard them.
// ISSUE: Doesn't handle embedded comments.
//
// Return: TRUE unless an error occurs.
//
BOOL CMultipartParse::HandleComments( )
{
BOOL fReturn = TRUE;
TraceMsg( TF_FUNC | TF_PARSE, "HandleComments( )" );
LEXICON eLex = LEX_UNKNOWN;
while( eLex != LEX_END_COMMENT )
eLex = Lex( ); // parse away...
TraceMsg( TF_FUNC | TF_PARSE, "HandleComments( ) Exit" );
return fReturn;
} // HandleComments( )
//
// What: BodyContent
//
// Desc: Handles the content part of the body.
//
// In: lpBHI is the body header information structure that
// contains arguments found in the body header.
//
// Return: TRUE unless a unrecognized token is found.
//
BOOL CMultipartParse::BodyContent( LPBODYHEADERINFO lpBHI )
{
BOOL fReturn = TRUE;
TraceMsg( TF_FUNC | TF_PARSE, "BodyContent( lpBHI=0x%x )",
lpBHI );
// Is it a file?
if (( lpBHI->lpszFilenameField )
&& ( lpBHI->lpszFilenameField[ 0 ] != 0 ))
{ // file content....
fReturn = HandleFile( lpBHI->lpszFilenameField );
if ( fReturn )
DebugMsg( lpszDebug, "Body Data= FILE CONTENTS\n" );
}
else
{ // TODO: we need to handle different "Content-Types".
// For now we will assume that it is text/plain.
LEXICON eLex = LEX_UNKNOWN;
LPBYTE lpbStart = _lpbParse;
DWORD dwSize;
fReturn = FindNextBoundary( &dwSize );
// neaten up to be displayed and copied
CHAR cTemp = *_lpbParse; // save
*_lpbParse = 0; // null to display
DebugMsg( lpszDebug, "Body Data='%s'\n", lpbStart );
lpBHI->lpszBodyContents = StrDup( (LPSTR) lpbStart );
*_lpbParse = cTemp; // restore
}
TraceMsg( TF_FUNC | TF_PARSE, "BodyContent( _lpbParse=0x%x ) Exit = %s",
_lpbParse, BOOLTOSTRING( fReturn ) );
return fReturn;
} // BodyContent( )
//
// What: GetQuotedString
//
// Desc: Retrieves a quoted string from data.
//
// In/Out: lppszBuf is a passed in buffer pointer which will be assigned to
// to the field name.
//
// Return: TRUE unless an error occurs.
//
BOOL CMultipartParse::GetQuotedString( LPSTR *lppszBuf )
{
BOOL fReturn = TRUE;
LPBYTE lpbStart;
TraceMsg( TF_FUNC | TF_PARSE, "GetQuotedString( )" );
LEXICON eLex = Lex( );
fReturn = ( eLex == LEX_QUOTE );
if ( !fReturn )
{
DebugMsg( lpszDebug, "Excepted the beginning of a quoted string at %u (0x%x) bytes.\n",
( _lpbParse - _lpbData ), ( _lpbParse - _lpbData ) );
goto Cleanup;
}
lpbStart = _lpbParse;
while (( (DWORD) ( _lpbParse - _lpbData ) < lpEcb->cbTotalBytes )
&& ( *_lpbParse != '\"' ))
_lpbParse++;
if ( _lpbParse == (LPBYTE) lpEcb->cbTotalBytes )
{
fReturn = FALSE;
*lppszBuf = StrDup( "Error!" );
}
else
{
CHAR cTmp = *_lpbParse;
*_lpbParse = 0; // save
*lppszBuf = StrDup( (LPSTR) lpbStart );
*_lpbParse = cTmp; // restore
_lpbParse++; // get past the quote
}
Cleanup:
TraceMsg( TF_FUNC | TF_PARSE, "GetQuoteString( ) Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // GetQuotedString( )
//
// What: GetToken
//
// Desc: Scans past unknow content and validates that the characters
// used are acceptable ( see RFC 1521, page 9 ). If an invalid
// character is found, it will exit (no error).
//
// Return: FALSE if we run past the end of the data buffer, otherwise TRUE.
//
BOOL CMultipartParse::GetToken( )
{
BOOL fReturn = TRUE;
static const char szInvalidChars[] = "()<>@,;:\\\"/[]?=";
// plus SPACE and CTLs (in if statement)
TraceMsg( TF_FUNC | TF_PARSE, "GetToken()" );
while ( (DWORD) ( _lpbParse - _lpbData ) < lpEcb->cbTotalBytes )
{
if (( *_lpbParse <= 32 )
|| ( StrChr( szInvalidChars, *_lpbParse ) ))
break;
_lpbParse++;
}
if ( (DWORD) ( _lpbParse - _lpbData ) == lpEcb->cbTotalBytes )
{
DebugMsg( lpszDebug, "Could not find the end of the token.\n" );
fReturn = FALSE;
}
TraceMsg( TF_FUNC | TF_PARSE, "GetToken() Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // GetToken( )
//
// What: HandleFile
//
// Desc: Retrieves "file" part of the body.
//
// In: lpszFilename is the name of the file from the form submission.
//
// Return: TRUE unless an error occurs.
//
BOOL CMultipartParse::HandleFile( LPSTR lpszFilename )
{
BOOL fReturn = TRUE;
DWORD eLex = LEX_UNKNOWN;
DWORD dwSize;
LPBYTE lpbStart;
TraceMsg( TF_FUNC | TF_PARSE, "HandleFile( \"%s\" )", lpszFilename );
// find the end of the file
lpbStart = _lpbParse;
fReturn = FindNextBoundary( &dwSize );
// fix filename for server use
if ( fReturn )
{
DebugMsg( lpszDebug, "Filesize= %u (0x%x) bytes\n", dwSize, dwSize );
fReturn = FixFilename( lpszFilename, &lpszFilename );
}
// Check the filename for ""
if (( fReturn ) && ( StrCmp( lpszFilename, "" ) ))
{
#ifndef FILE_SAVE
fReturn = FileCompare( lpbStart, lpszFilename, dwSize );
#else // FILE_SAVE
fReturn = FileSave( lpbStart, lpszFilename, dwSize );
#endif // FILE_SAVE
}
if ( !fReturn )
{
LogMsg( lpEcb->lpszLogData, NULL, "Error: File possibly corrupt." );
}
TraceMsg( TF_FUNC | TF_PARSE, "HandleFile( \"%s\" ) Exit = %s",
lpszFilename, BOOLTOSTRING( fReturn ) );
return fReturn;
} // HandleFile( )
//
// What: FindNextBoundary
//
// Desc: Finds the next boundary in body content and the length of
// the block skipped.
//
// Out: *lpdwSize will contain the size of the block skipped.
//
// Return: TRUE unless an error occurs.
//
BOOL CMultipartParse::FindNextBoundary( LPDWORD lpdwSize )
{
BOOL fReturn = FALSE; // assume failure
TraceMsg( TF_FUNC | TF_PARSE, "FindNextBoundary()" );
lpDT[ _cbDT ].lpAddr = _lpbParse;
lpDT[ _cbDT ].dwColor = 0x000000;
lpDT[ _cbDT ].lpszComment = (LPSTR) g_cszBodyData;
_cbDT++;
if ( _cbDT >= MAX_DT )
{
DebugMsg( lpszDebug, "*** DEBUG ERROR *** Exceeded Dump Table Limit\n" );
_cbDT = MAX_DT - 1;
}
// find the end of the file
LPBYTE lpbStart = _lpbParse;
while ( (DWORD) ( _lpbParse - _lpbData ) < lpEcb->cbTotalBytes )
{
// possible boundary?
if ( !StrCmpN( (LPSTR) _lpbParse, g_cszBoundaryIndicator, g_cbBoundaryIndicator ) )
{ // yes... check further
LPBYTE lpb = _lpbParse; // save
LEXICON eLex = Lex( );
_lpbParse = lpb; // restore
_cbDT--; // ignore this parse
if (( eLex == LEX_BOUNDARY ) || ( eLex == LEX_EOT ))
{
fReturn = TRUE;
break; // exit loop
}
}
_lpbParse++;
}
// figure out the size
*lpdwSize = _lpbParse - lpbStart;
if ( !fReturn )
{
DebugMsg( lpszDebug, "FindNextBoundary( ): Did not find boundary after %u (0x%x) bytes.\n",
lpbStart - _lpbData, lpbStart - _lpbData );
_lpbParse--; // back up one byte
_cbDT--; // back up one parse table entry
}
TraceMsg( TF_FUNC | TF_PARSE, "FindNextBoundary( *lpdwSize = %u (0x%x) ) Exit = %s",
*lpdwSize, *lpdwSize, BOOLTOSTRING( fReturn ) );
return fReturn;
} // FindNextBoundary( )
#ifndef FILE_SAVE
//
// What: MemoryCompare
//
// Desc: Compares memory chunks and determines if they match
//
// In: lpbSrc1 and lpbSrc2 and the memory blocks to compare.
// dwSize is the length of the blocks.
//
// Return: TRUE if they match, otherwise FALSE
//
BOOL CMultipartParse::MemoryCompare( LPBYTE lpbSrc1, LPBYTE lpbSrc2, DWORD dwSize )
{
DWORD cb = 0;
while (( cb < dwSize ) && ( *lpbSrc1 == *lpbSrc2 ))
{
lpbSrc1++;
lpbSrc2++;
cb++;
}
return ( cb == dwSize );
} // MemoryCompare( )
//
// What: FileCompare
//
// Desc: Compares bits in memory with contents of the file lpszFilename.
//
// In: lpbStart is the starting memory address.
// lpszFilename is the filename to use.
// dwSize is the length of valid bits after lpbStart.
//
// Return: TRUE unless an error occurs.
//
BOOL CMultipartParse::FileCompare( LPBYTE lpbStart, LPSTR lpszFilename, DWORD dwSize )
{
#define BIG_FILE_SIZE 4096
BOOL fReturn = TRUE;
DWORD dwRead;
LPBYTE lpBuffer;
TraceMsg( TF_FUNC | TF_PARSE, "FileCompare( lpbStart=0x%x, lpszFilename='%s', dwSize=%u )",
lpbStart, lpszFilename, dwSize );
HANDLE hFile = CreateFile( lpszFilename, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL );
if ( hFile == INVALID_HANDLE_VALUE )
{
DebugMsg( lpszDebug, "CreateFile() failed to open '%s'.\n",
lpszFilename );
fReturn = FALSE;
goto Cleanup;
}
dwRead = GetFileSize( hFile, NULL );
if ( dwSize != dwRead )
{
LogMsg( lpEcb->lpszLogData, lpszDebug, "FileChecking: File sizes don't match: '%s'",
lpszFilename );
DebugMsg( lpszDebug, "FileChecking: Client sent=%u (0x%x) bytes and File length=%u (0x%x) bytes.\n",
lpszFilename, dwSize, dwSize, dwRead, dwRead );
DebugMsg( lpszDebug, "FileChecking: I will compare the bits I can.\n" );
fReturn = FALSE;
// shorten if needed
dwSize = ( dwSize > dwRead ? dwRead : dwSize );
}
if ( dwSize > BIG_FILE_SIZE )
{ // read/compare only the beginning and the end bytes
DWORD dwShortSize = BIG_FILE_SIZE / 2;
DebugMsg( lpszDebug, "FileChecking: **** Very large file ( > %u (0x%x) bytes )****\n",
BIG_FILE_SIZE, BIG_FILE_SIZE );
DebugMsg( lpszDebug, "FileChecking: Checking only the beginning %u (0x%x) bytes and the last %u (0x%x) bytes.\n",
dwShortSize, dwShortSize, dwShortSize, dwShortSize);
lpBuffer = (LPBYTE) GlobalAlloc( GMEM_FIXED, dwShortSize );
if ( !lpBuffer )
{
DebugMsg( lpszDebug, "Out of memory(?).\n" );
fReturn = FALSE;
goto Cleanup;
}
// read beginning bytes
if ( !ReadFile( hFile, lpBuffer, dwShortSize, &dwRead, NULL ) )
{
DebugMsg( lpszDebug, "begin bytes ReadFile() failed.\n" );
fReturn = FALSE;
goto Cleanup;
}
if ( dwShortSize != dwRead )
{
DebugMsg( lpszDebug, "FileChecking(begin bytes): Unable to read %u (0x%x) bytes from file '%s'.\n",
dwShortSize, dwShortSize, lpszFilename );
fReturn = FALSE;
goto Cleanup;
}
fReturn = MemoryCompare( lpBuffer, lpbStart, dwShortSize );
if ( !fReturn )
goto Cleanup;
// seek to end bytes
if ( 0xFFFFffff == SetFilePointer( hFile, dwSize - dwShortSize, NULL, FILE_BEGIN ) )
{ // error
DebugMsg( lpszDebug, "SetFilePointer() failed!\n" );
fReturn = FALSE;
goto Cleanup;
}
// read end bytes
if ( !ReadFile( hFile, lpBuffer, dwShortSize, &dwRead, NULL ) )
{
DebugMsg( lpszDebug, "end bytes ReadFile() failed.\n" );
fReturn = FALSE;
goto Cleanup;
}
if ( dwShortSize != dwRead )
{
DebugMsg( lpszDebug, "FileChecking(end bytes): Unable to read %u (0x%x) bytes from file '%s'.\n",
dwShortSize, dwShortSize, lpszFilename );
fReturn = FALSE;
goto Cleanup;
}
fReturn = MemoryCompare( lpBuffer, lpbStart + dwSize - dwShortSize, dwShortSize );
}
else // if ( dwSize > BIG_FILE_SIZE )
{ // read/compare entire file
lpBuffer = (LPBYTE) GlobalAlloc( GMEM_FIXED, dwSize );
if ( !lpBuffer )
{
DebugMsg( lpszDebug, "Out of memory(?).\n" );
fReturn = FALSE;
goto Cleanup;
}
if ( !ReadFile( hFile, lpBuffer, dwSize, &dwRead, NULL ) )
{
DebugMsg( lpszDebug, "ReadFile() failed.\n" );
fReturn = FALSE;
goto Cleanup;
}
if ( dwSize != dwRead )
{
DebugMsg( lpszDebug, "FileChecking: Unable to read %u (0x%x) bytes from file '%s'.\n",
dwSize, dwSize, lpszFilename );
fReturn = FALSE;
goto Cleanup;
}
fReturn = MemoryCompare( lpBuffer, lpbStart, dwSize );
} // if ( dwSize > BIG_FILE_SIZE )
Cleanup:
if ( hFile != INVALID_HANDLE_VALUE )
CloseHandle( hFile ); // close file
GlobalFree( lpBuffer );
// Output to IIS log
if ( fReturn )
{
LogMsg( lpEcb->lpszLogData, lpszDebug, "FileChecking: Files matched!" );
}
else
{
LogMsg( lpEcb->lpszLogData, lpszDebug, "FileChecking: Files don't match or an error occured." );
}
TraceMsg( TF_FUNC | TF_PARSE, "FileCompare( lpbStart=0x%x, lpszFilename='%s', dwSize=%u ) Exit = %s",
lpbStart, lpszFilename, dwSize, BOOLTOSTRING( fReturn ) );
return fReturn;
} // FileCompare( )
#else // FILE_SAVE
//
// What: FileSave
//
// Desc: Save bits starting at lpbStart to a file called lpszFilename.
//
// In: lpbStart is the starting memory address.
// lpszFilename is the filename to use.
// dwSize is the length of valid bits after lpbStart.
//
// Return: TRUE unless an error occurs.
//
BOOL CMultipartParse::FileSave( LPBYTE lpbStart, LPSTR lpszFilename, DWORD dwSize )
{
BOOL fReturn = TRUE;
TraceMsg( TF_FUNC | TF_PARSE, "FileSave( lpbStart=0x%x, lpszFilename='%s', dwSize=%u )",
lpbStart, lpszFilename, dwSize );
if ( fReturn )
{
HANDLE hFile = CreateFile( lpszFilename, GENERIC_WRITE,
FILE_SHARE_READ, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL );
if ( hFile != INVALID_HANDLE_VALUE )
{
DWORD dwWrote;
if ( WriteFile( hFile, lpbStart, dwSize, &dwWrote, NULL ) )
{
fReturn = ( dwSize == dwWrote );
if ( !fReturn )
{
LogMsg( lpEcb->lpszLogData, lpszDebug, "Could not write entire file '%s'. Only wrote %u (0x%x) bytes.",
lpszFilename, dwWrote, dwWrote );
}
}
else
{
LogMsg( lpEcb->lpszLogData, lpszDebug, "Could not write file '%s'.",
lpszFilename );
fReturn = FALSE;
}
CloseHandle( hFile );
}
else
{
LogMsg( lpEcb->lpszLogData, lpszDebug, "CreateFile() failed to create '%s'.",
lpszFilename );
fReturn = FALSE;
} // if hFile
} // if fReturn
TraceMsg( TF_FUNC | TF_PARSE, "FileSave( lpbStart=0x%x, lpszFilename='%s', dwSize=%u ) Exit = ",
lpbStart, lpszFilename, dwSize, BOOLTOSTRING( fReturn ) );
return fReturn;
} // FileSave( )
#endif // FILE_SAVE
//
// What: FixFilename
//
// Desc: Adjusts filename to be used on the server. It creates a path
// to the same directory as the DLL. It then appends the just
// the filename ( not the path info ).
//
// In: lpszFilename is the filename submitted by user.
//
// In/Out: lppszNameFilename will return a pointer to the newly constructed
// filename.
//
// Return: TRUE unless an error occurs.
//
BOOL CMultipartParse::FixFilename( LPSTR lpszFilename, LPSTR *lppszNewFilename )
{
BOOL fReturn = TRUE;
CHAR szFilename[ MAX_PATH ]; // temp buffer
TraceMsg( TF_FUNC | TF_PARSE, "FixFilename( lpszFilename='%s', *lppszNewFilename=0x%x )",
lpszFilename, *lppszNewFilename );
// Strip c:\ stuff
LPSTR lpszShortFilename = StrRChr( lpszFilename, lpszFilename + lstrlen( lpszFilename ), '\\' );
if ( lpszShortFilename )
{
lpszShortFilename++; // move ahead of '\'
}
else
{
lpszShortFilename = lpszFilename; // maybe it's already short.
}
// retrieve download directory file URL info
LPSTR lpszPath;
fReturn = GetServerVarString( lpEcb, "PATH_TRANSLATED", &lpszPath, NULL );
if ( lpszPath )
{
StrCpy( szFilename, lpszPath );
DWORD cch = lstrlen( szFilename );
// check for '\' at the end...
if ( szFilename[ cch - 1 ] != '\\' )
{ // not there... add it
szFilename[ cch ] = '\\';
cch++;
}
// append filename
StrCpy( &szFilename[ cch ], lpszShortFilename );
}
else
{
StrCpy( szFilename, lpszShortFilename );
}
*lppszNewFilename = StrDup( szFilename );
fReturn = ( *lppszNewFilename != NULL );
TraceMsg( TF_FUNC | TF_PARSE, "FixFilename( lpszFilename='%s', *lppszNewFilename=0x%x ) Exit = %s",
lpszFilename, *lppszNewFilename, BOOLTOSTRING( fReturn ) );
return fReturn;
} // FixFilename( )
//
// What: Lex
//
// Desc: Turns the next bytes into a token
//
// Return: TRUE unless an error occurs.
//
LEXICON CMultipartParse::Lex( )
{
TraceMsg( TF_FUNC | TF_PARSE | TF_LEX, "Lex( _lpbParse=0x%x )",
_lpbParse );
lpDT[ _cbDT ].lpAddr = _lpbParse;
lpDT[ _cbDT ].dwColor = 0xFFFFFF;
lpDT[ _cbDT ].lpszComment = (LPSTR) g_cszExtraBytes;
if ( _lpbParse > _lpbData + lpEcb->cbTotalBytes )
{
return LEX_EOT; // over the limit!
}
for( int i = 0; g_LexTable[ i ].lpszName != NULL; i++ )
{
// remember str length sizes to speed up on next pass
if ( !g_LexTable[ i ].cLength )
{
g_LexTable[ i ].cLength = lstrlen( g_LexTable[ i ].lpszName );
}
if ( !StrCmpNI( (LPSTR)_lpbParse, g_LexTable[ i ].lpszName, g_LexTable[ i ].cLength ) )
break; // found... exit
} // for i
// SPECIAL CASE
LEXICON eLex = g_LexTable[ i ].eLex;
if ( eLex == LEX_BOUNDARY )
{ // check for a specific boundary marker
_lpbParse += g_LexTable[ i ].cLength;
eLex = LEX_UNKNOWN;
if ( !StrCmpN( (LPSTR) _lpbParse , _lpszBoundary, _cbBoundary ) )
{ // match
// check to see if it is an EOT Marker
if ( !StrCmpN( (LPSTR) ( _lpbParse + _cbBoundary ), "--\r\n", 4 ) )
{
_lpbParse += _cbBoundary + 4;
eLex = LEX_EOT;
}
// else make sure that it ends with a CRLF - rfc1521 Page 30
else if ( !StrCmpN( (LPSTR) ( _lpbParse + _cbBoundary ), "\r\n", 2 ) )
{
_lpbParse += _cbBoundary + 2;
eLex = LEX_BOUNDARY;
}
// else it isn't a boundary
}
}
else if ( eLex == LEX_STARTBOUNDARY )
{ // check for a specific boundary marker
_lpbParse += g_LexTable[ i ].cLength;
eLex = LEX_UNKNOWN;
if ( !StrCmpN( (LPSTR) _lpbParse , _lpszBoundary, _cbBoundary ) )
{ // match
// check to see if it is an EOT Marker
if ( !StrCmpN( (LPSTR) ( _lpbParse + _cbBoundary ), "\r\n", 2 ) )
{
_lpbParse += _cbBoundary + 2;
eLex = LEX_STARTBOUNDARY;
}
// else it isn't a boundary
}
}
else if ( eLex == LEX_UNKNOWN )
{
_lpbParse++;
}
else // move past symbol
{
_lpbParse += g_LexTable[ i ].cLength;
}
lpDT[ _cbDT ].dwColor = g_LexTable[ i ].dwColor;
lpDT[ _cbDT ].lpszComment = g_LexTable[ i ].lpszComment;
_cbDT++;
if ( _cbDT >= MAX_DT )
{
DebugMsg( lpszDebug, "*** DEBUG ERROR *** Exceeded Dump Table Limit\n" );
_cbDT = MAX_DT - 1;
}
TraceMsg( TF_FUNC | TF_PARSE | TF_LEX, "Lex( _lpbParse=0x%x ) Exit = %u ( %u, '%s')",
_lpbParse, eLex, i,
( g_LexTable[ i ].lpszName ? g_LexTable[ i ].lpszName : "NULL" ) );
return eLex;
} // Lex( )
//
// What: Find Token
//
// Desc: Searches tokens and displays friendly name.
//
// In: eLex is the Lexicon to find
//
// Return: TRUE unless an error occurs.
//
LPSTR CMultipartParse::FindTokenName( LEXICON eLex )
{
TraceMsg( TF_FUNC | TF_PARSE, "FindTokenName( )" );
LPSTR lpszReturn = "<not found>"; // default
for( int i = 0; g_LexTable[ i ].lpszName != NULL; i++ )
{
if ( g_LexTable[ i ].eLex == eLex )
break; // found... exit
} // for i
// SPECIAL CASE
if ( eLex == LEX_BOUNDARY )
{
lpszReturn = _lpszBoundary;
}
else if ( eLex == LEX_EOT )
{
lpszReturn = _lpszBoundary;
}
else if ( eLex == LEX_UNKNOWN )
{
LPBYTE lpb = _lpbParse - 1;
while (( *lpb ) && ( *lpb > 32 ))
lpb++;
*lpb = 0;
lpszReturn = (LPSTR) _lpbParse;
}
else if ( g_LexTable[ i ].lpszName )
{
lpszReturn = g_LexTable[ i ].lpszName;
}
TraceMsg( TF_FUNC | TF_PARSE, "FindTokenName( ) Exit" );
return lpszReturn;
} // FindTokenName( )
//
// What: BackupLex
//
// Desc: Backs parser up eLex's byte count.
//
// In: eLex is the token ID to back up
//
// Return: TRUE unless an error occurs.
//
BOOL CMultipartParse::BackupLex( LEXICON eLex )
{
BOOL fReturn = TRUE;
TraceMsg( TF_FUNC | TF_PARSE, "BackupLex( eLex=%s )",
FindTokenName( eLex ) );
for( int i = 0; g_LexTable[ i ].lpszName != NULL; i++ )
{
if ( g_LexTable[ i ].eLex == eLex )
break; // found... exit
} // for i
// SPECIAL CASES
if ( eLex == LEX_BOUNDARY )
{
// "CRLF--" + boundary string + "CRLF"
_lpbParse -= ( g_LexTable[ i ].cLength + _cbBoundary + 2 );
}
else if ( eLex == LEX_EOT )
{
// "CRLF--" + boundary string + "--CRLF"
_lpbParse -= ( g_LexTable[ i ].cLength + _cbBoundary + 4 );
}
else if ( eLex == LEX_UNKNOWN )
{
_lpbParse--;
}
else if ( g_LexTable[ i ].lpszName )
{
_lpbParse -= g_LexTable[ i ].cLength;
}
if ( fReturn )
{
_cbDT--; // remove one
}
TraceMsg( TF_FUNC | TF_PARSE, "BackupLex( eLex=%s ) Exit",
FindTokenName( eLex ) );
return fReturn;
} // BackupLex( )
/*****************************************************************************
//
// FUNCTION TEMPLATE
//
// ***************************************************************************
//
// What:
//
// Desc:
//
// In:
//
// Out:
//
// Return:
//
BOOL CMultipartParse::( )
{
BOOL fReturn = TRUE;
TraceMsg( TF_FUNC | TF_PARSE, "()" );
TraceMsg( TF_FUNC | TF_PARSE, "() Exit = %s",
BOOLTOSTRING( fReturn ) );
return fReturn;
} // ( )
******************************************************************************/