mirror of https://github.com/lianthony/NT4.0
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.
2656 lines
60 KiB
2656 lines
60 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ssinc.cxx
|
|
|
|
Abstract:
|
|
|
|
This module contains the server side include processing code. We aim
|
|
for support as specified by iis\spec\ssi.doc. The code is based on
|
|
existing SSI support done in iis\svcs\w3\server\ssinc.cxx.
|
|
|
|
Author:
|
|
|
|
Bilal Alam (t-bilala) 20-May-1996
|
|
|
|
Revision History:
|
|
|
|
See iis\svcs\w3\server\ssinc.cxx for prior log
|
|
|
|
|
|
--*/
|
|
|
|
#include "ssinc.hxx"
|
|
#include "ssicgi.hxx"
|
|
#include "ssibgi.hxx"
|
|
#include "ssimap.hxx"
|
|
|
|
//
|
|
// These are available SSI commands
|
|
//
|
|
|
|
enum SSI_COMMANDS
|
|
{
|
|
SSI_CMD_INCLUDE = 0,
|
|
SSI_CMD_ECHO,
|
|
SSI_CMD_FSIZE, // File size of specified file
|
|
SSI_CMD_FLASTMOD, // Last modified date of specified file
|
|
SSI_CMD_CONFIG, // Configure options
|
|
|
|
SSI_CMD_EXEC, // Execute CGI or CMD script
|
|
|
|
SSI_CMD_BYTERANGE, // Custom commands, not defined by NCSA
|
|
|
|
SSI_CMD_UNKNOWN
|
|
};
|
|
|
|
//
|
|
// These tags are essentially subcommands for the various SSI_COMMAND values
|
|
//
|
|
|
|
enum SSI_TAGS
|
|
{
|
|
SSI_TAG_FILE, // Used with include, fsize & flastmod
|
|
SSI_TAG_VIRTUAL,
|
|
|
|
SSI_TAG_VAR, // Used with echo
|
|
|
|
SSI_TAG_CMD, // Used with Exec
|
|
SSI_TAG_CGI,
|
|
|
|
SSI_TAG_ERRMSG, // Used with Config
|
|
SSI_TAG_TIMEFMT,
|
|
SSI_TAG_SIZEFMT,
|
|
|
|
SSI_TAG_UNKNOWN
|
|
};
|
|
|
|
//
|
|
// Variables available to #ECHO VAR = "xxx" but not available in ISAPI
|
|
//
|
|
|
|
enum SSI_VARS
|
|
{
|
|
SSI_VAR_DOCUMENT_NAME = 0,
|
|
SSI_VAR_DOCUMENT_URI,
|
|
SSI_VAR_QUERY_STRING_UNESCAPED,
|
|
SSI_VAR_DATE_LOCAL,
|
|
SSI_VAR_DATE_GMT,
|
|
SSI_VAR_LAST_MODIFIED,
|
|
|
|
SSI_VAR_UNKNOWN
|
|
};
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
UINT g_MonthToDayCount[] = {
|
|
0,
|
|
31,
|
|
31 + 28,
|
|
31 + 28 + 31,
|
|
31 + 28 + 31 + 30,
|
|
31 + 28 + 31 + 30 + 31,
|
|
31 + 28 + 31 + 30 + 31 + 30,
|
|
31 + 28 + 31 + 30 + 31 + 30 + 31,
|
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
|
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
|
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
|
|
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
|
|
} ;
|
|
|
|
|
|
//
|
|
// Prototypes
|
|
//
|
|
|
|
extern "C" {
|
|
|
|
BOOL
|
|
WINAPI
|
|
DLLEntry(
|
|
HINSTANCE hDll,
|
|
DWORD dwReason,
|
|
LPVOID lpvReserved
|
|
);
|
|
}
|
|
|
|
class SSI_ELEMENT_LIST;
|
|
|
|
SSI_ELEMENT_LIST *
|
|
SSIParse(
|
|
IN SSI_REQUEST * pRequest,
|
|
IN STR * pstrFile,
|
|
IN STR * pstrURL,
|
|
OUT BOOL * pfAccessDenied
|
|
);
|
|
|
|
BOOL
|
|
SSISend(
|
|
IN SSI_REQUEST * pRequest,
|
|
IN STR * pstrFile,
|
|
IN STR * pstrURL,
|
|
IN OUT DWORD * pcLevel
|
|
);
|
|
|
|
BOOL
|
|
ParseSSITag(
|
|
IN OUT CHAR * * ppchFilePos,
|
|
IN CHAR * pchEOF,
|
|
OUT BOOL * pfValidTag,
|
|
OUT SSI_COMMANDS * pCommandType,
|
|
OUT SSI_TAGS * pTagType,
|
|
OUT CHAR * pszTagString
|
|
);
|
|
|
|
void
|
|
FreeSELBlob(
|
|
VOID * pvCacheBlob
|
|
);
|
|
|
|
CHAR *
|
|
SSISkipTo(
|
|
IN CHAR * pchFilePos,
|
|
IN CHAR ch,
|
|
IN CHAR * pchEOF
|
|
);
|
|
|
|
CHAR *
|
|
SSISkipWhite(
|
|
IN CHAR * pchFilePos,
|
|
IN CHAR * pchEOF
|
|
);
|
|
|
|
//
|
|
// Global Data
|
|
//
|
|
|
|
DECLARE_DEBUG_PRINTS_OBJECT()
|
|
DECLARE_DEBUG_VARIABLE();
|
|
|
|
//
|
|
// This is used to access w3svc cache
|
|
//
|
|
|
|
#ifdef DO_CACHE
|
|
TSVC_CACHE * g_ptsvcCache;
|
|
#endif
|
|
|
|
//
|
|
// This is the list of supported commands
|
|
//
|
|
|
|
struct _SSI_CMD_MAP
|
|
{
|
|
CHAR * pszCommand;
|
|
DWORD cchCommand;
|
|
SSI_COMMANDS ssiCmd;
|
|
}
|
|
SSICmdMap[] =
|
|
{
|
|
"#include ", 9, SSI_CMD_INCLUDE,
|
|
"#echo ", 6, SSI_CMD_ECHO,
|
|
"#fsize ", 7, SSI_CMD_FSIZE,
|
|
"#flastmod ",10, SSI_CMD_FLASTMOD,
|
|
"#config ", 8, SSI_CMD_CONFIG,
|
|
"#exec ", 6, SSI_CMD_EXEC,
|
|
NULL, 0, SSI_CMD_UNKNOWN
|
|
};
|
|
|
|
//
|
|
// This is the list of supported tags
|
|
//
|
|
|
|
struct _SSI_TAG_MAP
|
|
{
|
|
CHAR * pszTag;
|
|
DWORD cchTag;
|
|
SSI_TAGS ssiTag;
|
|
}
|
|
SSITagMap[] =
|
|
{
|
|
"var", 3, SSI_TAG_VAR,
|
|
"file", 4, SSI_TAG_FILE,
|
|
"virtual", 7, SSI_TAG_VIRTUAL,
|
|
"errmsg", 6, SSI_TAG_ERRMSG,
|
|
"timefmt", 7, SSI_TAG_TIMEFMT,
|
|
"sizefmt", 7, SSI_TAG_SIZEFMT,
|
|
"cmd", 3, SSI_TAG_CMD,
|
|
"cgi", 3, SSI_TAG_CGI,
|
|
NULL, 0, SSI_TAG_UNKNOWN
|
|
};
|
|
|
|
//
|
|
// This is a list of #ECHO variables not supported by ISAPI
|
|
//
|
|
|
|
struct _SSI_VAR_MAP
|
|
{
|
|
CHAR * pszMap;
|
|
DWORD cchMap;
|
|
SSI_VARS ssiMap;
|
|
}
|
|
SSIVarMap[] =
|
|
{
|
|
"DOCUMENT_NAME", 13, SSI_VAR_DOCUMENT_NAME,
|
|
"DOCUMENT_URI", 12, SSI_VAR_DOCUMENT_URI,
|
|
"QUERY_STRING_UNESCAPED", 22, SSI_VAR_QUERY_STRING_UNESCAPED,
|
|
"DATE_LOCAL", 10, SSI_VAR_DATE_LOCAL,
|
|
"DATE_GMT", 8, SSI_VAR_DATE_GMT,
|
|
"LAST_MODIFIED", 13, SSI_VAR_LAST_MODIFIED,
|
|
NULL, 0, SSI_VAR_UNKNOWN
|
|
};
|
|
|
|
//
|
|
// Class Definitions
|
|
//
|
|
|
|
// class SSI_FILE
|
|
//
|
|
// File structure. All high level functions should use this
|
|
// structure instead of dealing with handle specifics themselves.
|
|
|
|
class SSI_FILE
|
|
{
|
|
private:
|
|
STR _strFilename;
|
|
#ifdef DO_CACHE
|
|
TS_OPEN_FILE_INFO * _hHandle;
|
|
#else
|
|
HANDLE _hHandle;
|
|
#endif
|
|
HANDLE _hMapHandle;
|
|
PVOID _pvMappedBase;
|
|
BOOL _fValid;
|
|
public:
|
|
SSI_FILE( IN STR * pstrFilename,
|
|
IN HANDLE hUser )
|
|
: _hHandle( NULL ),
|
|
_hMapHandle( NULL ),
|
|
_pvMappedBase( NULL ),
|
|
_fValid( FALSE )
|
|
{
|
|
#ifdef DO_CACHE
|
|
TS_OPEN_FILE_INFO * hHandle;
|
|
|
|
hHandle = TsCreateFile( *g_ptsvcCache,
|
|
pstrFilename->QueryStr(),
|
|
hUser,
|
|
TS_CACHING_DESIRED );
|
|
#else
|
|
HANDLE hHandle;
|
|
|
|
hHandle = ::CreateFile( pstrFilename->QueryStr(),
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
if ( hHandle == INVALID_HANDLE_VALUE )
|
|
{
|
|
hHandle = NULL;
|
|
}
|
|
#endif
|
|
if ( hHandle == NULL )
|
|
{
|
|
return;
|
|
}
|
|
_hHandle = hHandle;
|
|
|
|
if ( !_strFilename.Copy( pstrFilename->QueryStr() ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
_fValid = TRUE;
|
|
}
|
|
|
|
~SSI_FILE( VOID )
|
|
{
|
|
if ( _pvMappedBase != NULL )
|
|
{
|
|
SSIUnmapViewOfFile();
|
|
}
|
|
if ( _hMapHandle != NULL )
|
|
{
|
|
SSICloseMapHandle();
|
|
}
|
|
if ( _hHandle != NULL )
|
|
{
|
|
#ifdef DO_CACHE
|
|
TsCloseHandle( *g_ptsvcCache,
|
|
_hHandle );
|
|
#else
|
|
::CloseHandle( _hHandle );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
BOOL IsValid( VOID )
|
|
{
|
|
return _fValid;
|
|
}
|
|
|
|
BOOL SSICreateFileMapping( VOID )
|
|
// Creates a mapping to a file
|
|
{
|
|
HANDLE hHandle;
|
|
|
|
#ifdef DO_CACHE
|
|
hHandle = _hHandle->QueryFileHandle();
|
|
#else
|
|
hHandle = _hHandle;
|
|
#endif
|
|
if ( _hMapHandle != NULL )
|
|
{
|
|
if ( !SSICloseMapHandle() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
_hMapHandle = ::CreateFileMapping( hHandle,
|
|
NULL,
|
|
PAGE_READONLY,
|
|
0,
|
|
0,
|
|
NULL );
|
|
|
|
return _hMapHandle != NULL;
|
|
}
|
|
|
|
BOOL SSICloseMapHandle( VOID )
|
|
// Closes mapping to a file
|
|
{
|
|
if ( _hMapHandle != NULL )
|
|
{
|
|
::CloseHandle( _hMapHandle );
|
|
_hMapHandle = NULL;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SSIMapViewOfFile( VOID )
|
|
// Maps file to address
|
|
{
|
|
if ( _pvMappedBase != NULL )
|
|
{
|
|
if ( !SSIUnmapViewOfFile() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
_pvMappedBase = ::MapViewOfFile( _hMapHandle,
|
|
FILE_MAP_READ,
|
|
0,
|
|
0,
|
|
0 );
|
|
return _pvMappedBase != NULL;
|
|
}
|
|
|
|
BOOL SSIUnmapViewOfFile( VOID )
|
|
// Unmaps file
|
|
{
|
|
if ( _pvMappedBase != NULL )
|
|
{
|
|
::UnmapViewOfFile( _pvMappedBase );
|
|
_pvMappedBase = NULL;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD SSIGetFileAttributes( VOID )
|
|
// Gets the attributes of a file
|
|
{
|
|
#ifdef DO_CACHE
|
|
return _hHandle->QueryAttributes();
|
|
#else
|
|
return ::GetFileAttributes( _strFilename.QueryStr() );
|
|
#endif
|
|
}
|
|
|
|
BOOL SSIGetFileSize( OUT DWORD * pdwLowWord,
|
|
OUT DWORD * pdwHighWord )
|
|
// Gets the size of the file.
|
|
{
|
|
#ifdef DO_CACHE
|
|
return _hHandle->QuerySize( pdwLowWord,
|
|
pdwHighWord );
|
|
#else
|
|
*pdwLowWord = ::GetFileSize( _hHandle,
|
|
pdwHighWord );
|
|
return *pdwLowWord != 0xfffffff;
|
|
#endif
|
|
}
|
|
|
|
BOOL SSIGetLastModTime( OUT FILETIME * ftTime )
|
|
// Gets the Last modification time of a file.
|
|
{
|
|
HANDLE hHandle;
|
|
|
|
#ifdef DO_CACHE
|
|
hHandle = _hHandle->QueryFileHandle();
|
|
#else
|
|
hHandle = _hHandle;
|
|
#endif
|
|
return ::GetFileTime( hHandle,
|
|
NULL,
|
|
NULL,
|
|
ftTime );
|
|
}
|
|
|
|
PVOID GetMappedBase( VOID )
|
|
{
|
|
return _pvMappedBase;
|
|
}
|
|
|
|
STR & GetFilename( VOID )
|
|
{
|
|
return _strFilename;
|
|
}
|
|
};
|
|
|
|
// Class SSI_ELEMENT_ITEM
|
|
//
|
|
// Represents a SSI command in the document
|
|
|
|
class SSI_ELEMENT_ITEM
|
|
{
|
|
private:
|
|
DWORD _Signature;
|
|
SSI_COMMANDS _ssiCmd;
|
|
SSI_TAGS _ssiTag;
|
|
STR * _pstrTagValue;
|
|
|
|
DWORD _cbBegin; // Only used for Byte range command
|
|
DWORD _cbLength; // Only used for Byte range command
|
|
public:
|
|
|
|
LIST_ENTRY _ListEntry;
|
|
|
|
SSI_ELEMENT_ITEM( VOID )
|
|
: _ssiCmd ( SSI_CMD_UNKNOWN ),
|
|
_ssiTag ( SSI_TAG_UNKNOWN ),
|
|
_Signature( SIGNATURE_SEI ),
|
|
_pstrTagValue( NULL )
|
|
{
|
|
_ListEntry.Flink = NULL;
|
|
}
|
|
|
|
~SSI_ELEMENT_ITEM( VOID )
|
|
{
|
|
if ( _pstrTagValue != NULL )
|
|
{
|
|
delete _pstrTagValue;
|
|
}
|
|
TCP_ASSERT( _ListEntry.Flink == NULL );
|
|
_Signature = SIGNATURE_SEI_FREE;
|
|
}
|
|
|
|
VOID SetByteRange( IN DWORD cbBegin,
|
|
IN DWORD cbLength )
|
|
{
|
|
_ssiCmd = SSI_CMD_BYTERANGE;
|
|
_cbBegin = cbBegin;
|
|
_cbLength = cbLength;
|
|
}
|
|
|
|
BOOL SetCommand( IN SSI_COMMANDS ssiCmd,
|
|
IN SSI_TAGS ssiTag,
|
|
IN CHAR * achTag )
|
|
{
|
|
_ssiCmd = ssiCmd;
|
|
_ssiTag = ssiTag;
|
|
_pstrTagValue = new STR( achTag );
|
|
return _pstrTagValue != NULL;
|
|
}
|
|
|
|
SSI_COMMANDS QueryCommand( VOID ) const
|
|
{ return _ssiCmd; }
|
|
|
|
SSI_TAGS QueryTag( VOID ) const
|
|
{ return _ssiTag; }
|
|
|
|
STR * QueryTagValue( VOID ) const
|
|
{ return _pstrTagValue; }
|
|
|
|
BOOL CheckSignature( VOID ) const
|
|
{ return _Signature == SIGNATURE_SEI; }
|
|
|
|
DWORD QueryBegin( VOID ) const
|
|
{ return _cbBegin; }
|
|
|
|
DWORD QueryLength( VOID ) const
|
|
{ return _cbLength; }
|
|
|
|
};
|
|
|
|
// Class SSI_ELEMENT_LIST
|
|
//
|
|
// This object sits as a cache blob under a file to be processed as a
|
|
// server side include. It represents an interpreted list of data elements
|
|
// that make up the file itself.
|
|
//
|
|
|
|
class SSI_ELEMENT_LIST
|
|
{
|
|
private:
|
|
DWORD _Signature;
|
|
LIST_ENTRY _ListHead;
|
|
|
|
//
|
|
// These are for tracking the memory mapped file
|
|
//
|
|
|
|
DWORD _cRefCount;
|
|
CRITICAL_SECTION _csRef;
|
|
|
|
//
|
|
// Provides the utilities needed to open/manipulate files
|
|
//
|
|
|
|
SSI_FILE * _pssiFile;
|
|
|
|
//
|
|
// Name of URL. Used to resolve FILE="xxx" filenames
|
|
//
|
|
|
|
STR _strURL;
|
|
|
|
|
|
public:
|
|
SSI_ELEMENT_LIST( VOID )
|
|
: _Signature ( SIGNATURE_SEL ),
|
|
_pssiFile( NULL ),
|
|
_cRefCount( 0 )
|
|
{
|
|
InitializeListHead( &_ListHead );
|
|
InitializeCriticalSection( &_csRef );
|
|
}
|
|
|
|
~SSI_ELEMENT_LIST( VOID )
|
|
{
|
|
SSI_ELEMENT_ITEM * pSEI;
|
|
|
|
while ( !IsListEmpty( &_ListHead ))
|
|
{
|
|
pSEI = CONTAINING_RECORD( _ListHead.Flink,
|
|
SSI_ELEMENT_ITEM,
|
|
_ListEntry );
|
|
|
|
RemoveEntryList( &pSEI->_ListEntry );
|
|
pSEI->_ListEntry.Flink = NULL;
|
|
delete pSEI;
|
|
}
|
|
UnMap();
|
|
|
|
if( _pssiFile != NULL )
|
|
{
|
|
delete _pssiFile;
|
|
}
|
|
|
|
DeleteCriticalSection( &_csRef );
|
|
_Signature = SIGNATURE_SEL_FREE;
|
|
|
|
}
|
|
|
|
BOOL AppendByteRange( IN DWORD cbStart,
|
|
IN DWORD cbLength )
|
|
{
|
|
SSI_ELEMENT_ITEM * pSEI;
|
|
|
|
pSEI = new SSI_ELEMENT_ITEM;
|
|
|
|
if ( !pSEI )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
pSEI->SetByteRange( cbStart,
|
|
cbLength );
|
|
AppendItem( pSEI );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL AppendCommand( IN SSI_COMMANDS ssiCmd,
|
|
IN SSI_TAGS ssiTag,
|
|
IN CHAR * pszTag )
|
|
{
|
|
SSI_ELEMENT_ITEM * pSEI;
|
|
|
|
pSEI = new SSI_ELEMENT_ITEM;
|
|
|
|
if ( !pSEI )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !pSEI->SetCommand( ssiCmd,
|
|
ssiTag,
|
|
pszTag ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
AppendItem( pSEI );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID AppendItem( IN SSI_ELEMENT_ITEM * pSEI )
|
|
{
|
|
InsertTailList( &_ListHead,
|
|
&pSEI->_ListEntry );
|
|
}
|
|
|
|
CHAR * QueryData( VOID ) const
|
|
{ return (CHAR *) _pssiFile->GetMappedBase(); }
|
|
|
|
BOOL CheckSignature( VOID ) const
|
|
{ return _Signature == SIGNATURE_SEL; }
|
|
|
|
VOID Lock( VOID )
|
|
{ EnterCriticalSection( &_csRef ); }
|
|
|
|
VOID UnLock( VOID )
|
|
{ LeaveCriticalSection( &_csRef ); }
|
|
|
|
BOOL UnMap( VOID )
|
|
{
|
|
Lock();
|
|
if ( _cRefCount && !--_cRefCount )
|
|
{
|
|
TCP_REQUIRE( _pssiFile->SSIUnmapViewOfFile() );
|
|
TCP_REQUIRE( _pssiFile->SSICloseMapHandle() );
|
|
}
|
|
UnLock();
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL Map( VOID )
|
|
{
|
|
Lock();
|
|
if ( _cRefCount++ == 0 )
|
|
{
|
|
if ( !_pssiFile->SSICreateFileMapping() )
|
|
{
|
|
UnLock();
|
|
return FALSE;
|
|
}
|
|
if ( !_pssiFile->SSIMapViewOfFile() )
|
|
{
|
|
UnMap();
|
|
UnLock();
|
|
return FALSE;
|
|
}
|
|
}
|
|
UnLock();
|
|
return TRUE;
|
|
}
|
|
|
|
VOID SetFile( IN SSI_FILE * pssiFile )
|
|
{ _pssiFile = pssiFile; }
|
|
|
|
BOOL SetURL( IN STR * pstrURL )
|
|
{
|
|
return _strURL.Copy( pstrURL->QueryStr() );
|
|
}
|
|
|
|
BOOL Send( IN SSI_REQUEST *,
|
|
IN OUT DWORD * );
|
|
|
|
BOOL FindInternalVariable( IN OUT STR *,
|
|
IN OUT DWORD * );
|
|
BOOL GetFullPath( IN SSI_REQUEST *,
|
|
IN SSI_ELEMENT_ITEM *,
|
|
OUT STR *,
|
|
IN DWORD );
|
|
|
|
};
|
|
|
|
BOOL
|
|
SSI_ELEMENT_LIST::Send(
|
|
IN SSI_REQUEST * pRequest,
|
|
IN OUT DWORD * pcLevel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method walks the element list sending the appropriate chunks of
|
|
data
|
|
|
|
Arguments:
|
|
|
|
pRequest - Request context
|
|
pcLevel - Pointer to count of #include nesting level
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on any failures
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
DWORD cbSent;
|
|
STR strPath;
|
|
SSI_ELEMENT_ITEM * pSEI;
|
|
|
|
DWORD dwID;
|
|
LPCTSTR apszParms[ 2 ];
|
|
CHAR achNumberBuffer[ SSI_MAX_NUMBER_STRING ];
|
|
|
|
BOOL bSizeFmtBytes = SSI_DEF_SIZEFMT;
|
|
STR strTimeFmt;
|
|
|
|
TCP_ASSERT( CheckSignature() );
|
|
|
|
if ( !strTimeFmt.Copy( SSI_DEF_TIMEFMT ) ||
|
|
!Map() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Loop through each element and take the appropriate action
|
|
//
|
|
|
|
for ( pEntry = _ListHead.Flink;
|
|
pEntry != &_ListHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pSEI = CONTAINING_RECORD( pEntry, SSI_ELEMENT_ITEM, _ListEntry );
|
|
|
|
TCP_ASSERT( pSEI->CheckSignature() );
|
|
|
|
dwID = 0;
|
|
|
|
switch ( pSEI->QueryCommand() )
|
|
{
|
|
case SSI_CMD_BYTERANGE:
|
|
if ( !pRequest->WriteToClient( QueryData() + pSEI->QueryBegin(),
|
|
pSEI->QueryLength(),
|
|
&cbSent ) )
|
|
{
|
|
UnMap();
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case SSI_CMD_INCLUDE:
|
|
switch ( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_FILE:
|
|
case SSI_TAG_VIRTUAL:
|
|
if ( !GetFullPath( pRequest,
|
|
pSEI,
|
|
&strPath,
|
|
VROOT_MASK_READ ) )
|
|
{
|
|
apszParms[ 0 ] = strPath.QueryStr();
|
|
dwID = SSINCMSG_ERROR_HANDLING_FILE;
|
|
break;
|
|
}
|
|
|
|
(*pcLevel)++;
|
|
|
|
if ( !SSISend( pRequest,
|
|
&strPath,
|
|
pSEI->QueryTagValue(),
|
|
pcLevel ) )
|
|
{
|
|
apszParms[ 0 ] = strPath.QueryStr();
|
|
dwID = SSINCMSG_ERROR_HANDLING_FILE;
|
|
}
|
|
|
|
(*pcLevel)--;
|
|
|
|
break;
|
|
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
if ( dwID )
|
|
{
|
|
pRequest->SSISendError( dwID, apszParms );
|
|
}
|
|
break;
|
|
|
|
case SSI_CMD_FLASTMOD:
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_FILE:
|
|
case SSI_TAG_VIRTUAL:
|
|
if ( !GetFullPath( pRequest,
|
|
pSEI,
|
|
&strPath,
|
|
0 ) ||
|
|
!pRequest->DoFLastMod( &strPath,
|
|
&strTimeFmt ) )
|
|
{
|
|
_ultoa( GetLastError(), achNumberBuffer, 10 );
|
|
apszParms[ 0 ] = strPath.QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_CANT_DO_FLASTMOD;
|
|
}
|
|
break;
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
if ( dwID )
|
|
{
|
|
pRequest->SSISendError( dwID, apszParms );
|
|
}
|
|
break;
|
|
|
|
case SSI_CMD_CONFIG:
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_ERRMSG:
|
|
if ( !pRequest->SetUserErrorMessage( pSEI->QueryTagValue() ) )
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
break;
|
|
case SSI_TAG_TIMEFMT:
|
|
if ( !strTimeFmt.Copy( pSEI->QueryTagValue()->QueryStr() ) )
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
break;
|
|
case SSI_TAG_SIZEFMT:
|
|
if ( _strnicmp( SSI_DEF_BYTES,
|
|
pSEI->QueryTagValue()->QueryStr(),
|
|
SSI_DEF_BYTES_LEN ) == 0 )
|
|
{
|
|
bSizeFmtBytes = TRUE;
|
|
}
|
|
else if ( _strnicmp( SSI_DEF_ABBREV,
|
|
pSEI->QueryTagValue()->QueryStr(),
|
|
SSI_DEF_ABBREV_LEN ) == 0 )
|
|
{
|
|
bSizeFmtBytes = FALSE;
|
|
}
|
|
else
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
break;
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
if ( dwID )
|
|
{
|
|
pRequest->SSISendError( dwID, NULL );
|
|
}
|
|
break;
|
|
|
|
case SSI_CMD_FSIZE:
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_FILE:
|
|
case SSI_TAG_VIRTUAL:
|
|
if ( !GetFullPath( pRequest,
|
|
pSEI,
|
|
&strPath,
|
|
0 ) ||
|
|
!pRequest->DoFSize( &strPath,
|
|
bSizeFmtBytes ) )
|
|
{
|
|
_ultoa( GetLastError(), achNumberBuffer, 10 );
|
|
apszParms[ 0 ] = strPath.QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_CANT_DO_FSIZE;
|
|
}
|
|
break;
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
if ( dwID )
|
|
{
|
|
pRequest->SSISendError( dwID, apszParms );
|
|
}
|
|
|
|
break;
|
|
|
|
case SSI_CMD_ECHO:
|
|
if ( pSEI->QueryTag() == SSI_TAG_VAR )
|
|
{
|
|
// First let ISAPI try to evaluate variable.
|
|
if ( pRequest->DoEchoISAPIVariable( pSEI->QueryTagValue() ) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwVar;
|
|
BOOL fEchoSuccess = FALSE;
|
|
|
|
// if ISAPI couldn't resolve var, try internal list
|
|
if ( !FindInternalVariable( pSEI->QueryTagValue(),
|
|
&dwVar ) )
|
|
{
|
|
apszParms[ 0 ] = pSEI->QueryTagValue()->QueryStr();
|
|
dwID = SSINCMSG_CANT_FIND_VARIABLE;
|
|
}
|
|
else
|
|
{
|
|
switch( dwVar )
|
|
{
|
|
case SSI_VAR_DOCUMENT_NAME:
|
|
fEchoSuccess = pRequest->DoEchoDocumentName( &_pssiFile->GetFilename() );
|
|
break;
|
|
case SSI_VAR_DOCUMENT_URI:
|
|
fEchoSuccess = pRequest->DoEchoDocumentURI( &_strURL );
|
|
break;
|
|
case SSI_VAR_QUERY_STRING_UNESCAPED:
|
|
fEchoSuccess = pRequest->DoEchoQueryStringUnescaped();
|
|
break;
|
|
case SSI_VAR_DATE_LOCAL:
|
|
fEchoSuccess = pRequest->DoEchoDateLocal( &strTimeFmt );
|
|
break;
|
|
case SSI_VAR_DATE_GMT:
|
|
fEchoSuccess = pRequest->DoEchoDateGMT( &strTimeFmt );
|
|
break;
|
|
case SSI_VAR_LAST_MODIFIED:
|
|
fEchoSuccess = pRequest->DoEchoLastModified( &_pssiFile->GetFilename(),
|
|
&strTimeFmt );
|
|
break;
|
|
default:
|
|
apszParms[ 0 ] = pSEI->QueryTagValue()->QueryStr();
|
|
dwID = SSINCMSG_CANT_FIND_VARIABLE;
|
|
}
|
|
if ( !fEchoSuccess )
|
|
{
|
|
apszParms[ 0 ] = pSEI->QueryTagValue()->QueryStr();
|
|
dwID = SSINCMSG_CANT_EVALUATE_VARIABLE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
|
|
if ( dwID )
|
|
{
|
|
pRequest->SSISendError( dwID,
|
|
apszParms );
|
|
}
|
|
break;
|
|
case SSI_CMD_EXEC:
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_CMD:
|
|
if ( !pRequest->DoProcessGateway( pSEI->QueryTagValue(),
|
|
FALSE ) )
|
|
{
|
|
_ultoa( GetLastError(), achNumberBuffer, 10 );
|
|
apszParms[ 0 ] = pSEI->QueryTagValue()->QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_CANT_EXEC_CMD;
|
|
}
|
|
break;
|
|
case SSI_TAG_CGI:
|
|
if ( !pRequest->DoProcessGateway( pSEI->QueryTagValue(),
|
|
TRUE ) )
|
|
{
|
|
_ultoa( GetLastError(), achNumberBuffer, 10 );
|
|
apszParms[ 0 ] = pSEI->QueryTagValue()->QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_CANT_EXEC_CGI;
|
|
}
|
|
break;
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
if ( dwID )
|
|
{
|
|
pRequest->SSISendError( dwID, apszParms );
|
|
}
|
|
break;
|
|
default:
|
|
pRequest->SSISendError( SSINCMSG_NOT_SUPPORTED,
|
|
NULL );
|
|
break;
|
|
}
|
|
}
|
|
|
|
UnMap();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
SSI_ELEMENT_LIST::GetFullPath(
|
|
IN SSI_REQUEST * pRequest,
|
|
IN SSI_ELEMENT_ITEM * pSEI,
|
|
OUT STR * pstrPath,
|
|
IN DWORD dwPermission
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Used to resolve FILE= and VIRTUAL= references. Fills in the physical
|
|
path of such file references and optionally checks the permissions
|
|
of the virtual directory.
|
|
|
|
Arguments:
|
|
|
|
pRequest - SSI_REQUEST
|
|
pSEI - Element item ( either FILE or VIRTUAL )
|
|
pstrPath - Filled in with physical path of file
|
|
dwPermission - Contains permissions that the virtual
|
|
path must satisfy. For example VROOT_MASK_READ.
|
|
If 0, then no permissions are checked
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
CHAR * pszValue;
|
|
STR * pstrValue;
|
|
DWORD dwMask;
|
|
DWORD cbBufLen;
|
|
CHAR achPath[ SSI_MAX_PATH + 1 ];
|
|
|
|
//
|
|
// We recalc the virtual root each time in case the root
|
|
// to directory mapping has changed
|
|
//
|
|
|
|
pstrValue = pSEI->QueryTagValue();
|
|
pszValue = pstrValue->QueryStr();
|
|
|
|
if ( *pszValue == '/' )
|
|
strcpy( achPath, pszValue );
|
|
else if ( (int)pSEI->QueryTag() == (int)SSI_TAG_FILE )
|
|
{
|
|
strcpy( achPath, _strURL.QueryStr() );
|
|
LPSTR pL = achPath + lstrlen( achPath );
|
|
while ( pL > achPath && pL[-1] != '/' )
|
|
--pL;
|
|
if ( pL == achPath )
|
|
*pL++ = '/';
|
|
strcpy( pL, pszValue );
|
|
}
|
|
else
|
|
{
|
|
achPath[0] = '/';
|
|
strcpy( achPath+1, pszValue );
|
|
}
|
|
|
|
//
|
|
// Map to a physical directory
|
|
//
|
|
|
|
if ( !pRequest->LookupVirtualRoot( achPath,
|
|
pstrPath,
|
|
dwPermission ) )
|
|
{
|
|
if ( GetLastError() == ERROR_ACCESS_DENIED )
|
|
{
|
|
LPCTSTR apszParms[ 1 ];
|
|
apszParms[ 0 ] = achPath;
|
|
|
|
pRequest->SSISendError( SSINCMSG_ACCESS_DENIED,
|
|
apszParms );
|
|
}
|
|
else
|
|
{
|
|
LPCTSTR apszParms[ 1 ];
|
|
apszParms[ 0 ] = achPath;
|
|
|
|
pRequest->SSISendError( SSINCMSG_CANT_RESOLVE_PATH,
|
|
apszParms );
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
SSI_ELEMENT_LIST::FindInternalVariable(
|
|
IN STR * pstrVariable,
|
|
OUT PDWORD pdwID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Lookup internal list of SSI variables that aren't supported by ISAPI.
|
|
These include "DOCUMENT_NAME", "DATE_LOCAL", etc.
|
|
|
|
Arguments:
|
|
|
|
pstrVariable - Variable to check
|
|
pdwID - Variable ID (or SSI_VAR_UNKNOWN if not found)
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
DWORD dwCounter = 0;
|
|
|
|
while ( ( SSIVarMap[ dwCounter ].pszMap != NULL ) &&
|
|
_strnicmp( SSIVarMap[ dwCounter ].pszMap,
|
|
pstrVariable->QueryStr(),
|
|
SSIVarMap[ dwCounter ].cchMap ) )
|
|
{
|
|
dwCounter++;
|
|
}
|
|
if ( SSIVarMap[ dwCounter ].pszMap != NULL )
|
|
{
|
|
*pdwID = SSIVarMap[ dwCounter ].ssiMap;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pdwID = SSI_VAR_UNKNOWN;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// SSI_REQUEST methods
|
|
//
|
|
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoFLastMod(
|
|
IN STR * pstrFilename,
|
|
IN STR * pstrTimeFmt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send the LastModTime of file to HTML stream
|
|
|
|
Arguments:
|
|
|
|
pstrFilename - Filename
|
|
pstrTimeFmt - Format of time -> follows strftime() convention
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
FILETIME ftTime;
|
|
SYSTEMTIME sysTime;
|
|
SYSTEMTIME sysLocal;
|
|
|
|
SSI_FILE ssiFile( pstrFilename, _hUser );
|
|
|
|
if ( !ssiFile.IsValid() ||
|
|
(!ssiFile.SSIGetLastModTime( &ftTime ) ) ||
|
|
(!FileTimeToSystemTime( &ftTime, &sysTime ) ) ||
|
|
(!SystemTimeToTzSpecificLocalTime( NULL,
|
|
&sysTime,
|
|
&sysLocal ) ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return SendDate( &sysLocal,
|
|
pstrTimeFmt );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::SendDate(
|
|
IN SYSTEMTIME * psysTime,
|
|
IN STR * pstrTimeFmt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a SYSTEMTIME in appropriate format to HTML stream
|
|
|
|
Arguments:
|
|
|
|
psysTime - SYSTEMTIME containing time to send
|
|
pstrTimeFmt - Format of time (follows strftime() convention)
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
struct tm tm;
|
|
CHAR achBuffer[ SSI_MAX_TIME_SIZE + 1 ];
|
|
DWORD cbBufLen;
|
|
|
|
// Convert SYSTEMTIME to 'struct tm'
|
|
|
|
tm.tm_sec = psysTime->wSecond;
|
|
tm.tm_min = psysTime->wMinute;
|
|
tm.tm_hour = psysTime->wHour;
|
|
tm.tm_mday = psysTime->wDay;
|
|
tm.tm_mon = psysTime->wMonth - 1;
|
|
tm.tm_year = psysTime->wYear - 1900;
|
|
tm.tm_wday = psysTime->wDayOfWeek;
|
|
tm.tm_yday = g_MonthToDayCount[tm.tm_mon] + tm.tm_mday - 1;
|
|
|
|
//
|
|
// Adjust for leap year - note that we do not handle 2100
|
|
//
|
|
|
|
if ( (tm.tm_mon) > 1 && !(psysTime->wYear&3) )
|
|
{
|
|
++tm.tm_yday;
|
|
}
|
|
|
|
cbBufLen = strftime( achBuffer,
|
|
SSI_MAX_TIME_SIZE + 1,
|
|
pstrTimeFmt->QueryStr(),
|
|
&tm );
|
|
|
|
if ( cbBufLen == 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return WriteToClient( achBuffer,
|
|
cbBufLen,
|
|
&cbBufLen );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoEchoISAPIVariable(
|
|
IN STR * pstrVariable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get ISAPI variable and if successful, send it to HTML stream
|
|
|
|
Arguments:
|
|
|
|
pstrVariable - Variable
|
|
|
|
Return Value:
|
|
|
|
TRUE on variable found and sent success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
STR strVar;
|
|
DWORD cbBufLen;
|
|
|
|
if ( !GetVariable( pstrVariable->QueryStr(),
|
|
&strVar ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return WriteToClient( strVar.QueryStrA(),
|
|
strVar.QueryCB(),
|
|
&cbBufLen );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoEchoDateLocal(
|
|
IN STR * pstrTimeFmt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends local time (#ECHO VAR="DATE_LOCAL")
|
|
|
|
Arguments:
|
|
|
|
pstrTimefmt - Format of time (follows strftime() convention)
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME sysTime;
|
|
|
|
::GetLocalTime( &sysTime );
|
|
return SendDate( &sysTime,
|
|
pstrTimeFmt );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoEchoDateGMT(
|
|
IN STR * pstrTimeFmt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends GMT time (#ECHO VAR="DATE_GMT")
|
|
|
|
Arguments:
|
|
|
|
pstrTimefmt - Format of time (follows strftime() convention)
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME sysTime;
|
|
|
|
::GetSystemTime( &sysTime );
|
|
return SendDate( &sysTime,
|
|
pstrTimeFmt );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoEchoDocumentName(
|
|
IN STR * pstrFilename
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends filename of current SSI document (#ECHO VAR="DOCUMENT_NAME")
|
|
|
|
Arguments:
|
|
|
|
pstrFilename - filename to print
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
DWORD cbBufLen;
|
|
|
|
return WriteToClient( pstrFilename->QueryStr(),
|
|
pstrFilename->QueryCB(),
|
|
&cbBufLen );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoEchoDocumentURI(
|
|
IN STR * pstrURL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends URL of current SSI document (#ECHO VAR="DOCUMENT_URI")
|
|
|
|
Arguments:
|
|
|
|
pstrURL - URL to print
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
DWORD cbBufLen;
|
|
|
|
return WriteToClient( pstrURL->QueryStr(),
|
|
pstrURL->QueryCB(),
|
|
&cbBufLen );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoEchoQueryStringUnescaped( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends unescaped querystring to HTML stream
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
DWORD cbBufLen;
|
|
STR strVar;
|
|
|
|
if ( !strVar.Copy( _pECB->lpszQueryString ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !strVar.Unescape() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return WriteToClient( strVar.QueryStr(),
|
|
strVar.QueryCB(),
|
|
&cbBufLen );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoEchoLastModified(
|
|
IN STR * pstrFilename,
|
|
IN STR * pstrTimeFmt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends LastModTime of current document (#ECHO VAR="LAST_MODIFIED")
|
|
|
|
Arguments:
|
|
|
|
pstrFilename - Filename of current SSI document
|
|
pstrTimeFmt - Time format (follows strftime() convention)
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
return DoFLastMod( pstrFilename,
|
|
pstrTimeFmt );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoFSize(
|
|
IN STR * pstrFilename,
|
|
IN BOOL bSizeFmtBytes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends file size of file to HTML stream
|
|
|
|
Arguments:
|
|
|
|
pstrfilename - Filename
|
|
bSizeFmtBytes - TRUE if count is in Bytes, FALSE if in KBytes
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
BOOL bRet;
|
|
DWORD cbSizeLow;
|
|
DWORD cbSizeHigh;
|
|
CHAR achInputNumber[ SSI_MAX_NUMBER_STRING + 1 ];
|
|
CHAR achOutputNumber[ SSI_MAX_NUMBER_STRING + 1 ];
|
|
NUMBERFMT nfNumberFormat;
|
|
int iOutputSize;
|
|
DWORD dwActualLen;
|
|
|
|
SSI_FILE ssiFile( pstrFilename, _hUser );
|
|
|
|
if ( !ssiFile.IsValid() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
bRet = ssiFile.SSIGetFileSize( &cbSizeLow,
|
|
&cbSizeHigh );
|
|
if ( !bRet )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( cbSizeHigh )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !bSizeFmtBytes )
|
|
{
|
|
// express in terms of KB
|
|
cbSizeLow /= 1000;
|
|
}
|
|
|
|
nfNumberFormat.NumDigits = 0;
|
|
nfNumberFormat.LeadingZero = 0;
|
|
nfNumberFormat.Grouping = 3;
|
|
nfNumberFormat.lpThousandSep = ",";
|
|
nfNumberFormat.lpDecimalSep = ".";
|
|
nfNumberFormat.NegativeOrder = 2;
|
|
|
|
_snprintf( achInputNumber,
|
|
SSI_MAX_NUMBER_STRING + 1,
|
|
"%ld",
|
|
cbSizeLow );
|
|
|
|
iOutputSize = GetNumberFormat( LOCALE_SYSTEM_DEFAULT,
|
|
0,
|
|
achInputNumber,
|
|
&nfNumberFormat,
|
|
achOutputNumber,
|
|
SSI_MAX_NUMBER_STRING + 1 );
|
|
if ( !iOutputSize )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
iOutputSize--;
|
|
|
|
return WriteToClient( achOutputNumber,
|
|
iOutputSize,
|
|
&dwActualLen );
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::DoProcessGateway(
|
|
STR * pstrPath,
|
|
BOOL fCGI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles #EXEC CMD=,CGI=
|
|
|
|
Arguments:
|
|
|
|
pstrPath - Command specified in #EXEC call
|
|
fCGI - TRUE if CGI/ISA/MAP, FALSE if CMD
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
BOOL fRet = TRUE;
|
|
STR strURLParams;
|
|
STR strWorkingDir;
|
|
STR strPhysical;
|
|
STR strURL;
|
|
STR strPathInfo;
|
|
STR strGatewayImage;
|
|
CHAR * pch;
|
|
GATEWAY_TYPE GatewayType = GATEWAY_NONE;
|
|
DWORD cchExt;
|
|
BOOL fImageInURL;
|
|
DWORD cchToEnd;
|
|
CHAR * pchStart;
|
|
CHAR * pchTmp;
|
|
DWORD cbDirSize;
|
|
|
|
if ( !fCGI )
|
|
{
|
|
if ( IsCmdExe( pstrPath->QueryStr() ) )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
return ProcessCGI( this,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
pstrPath,
|
|
NULL );
|
|
}
|
|
|
|
//
|
|
// Need to separate any params specified in URL
|
|
//
|
|
|
|
if ( !strURL.Copy( pstrPath->QueryStr() ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
pch = strchr( strURL.QueryStr(), '?' );
|
|
if ( pch != NULL )
|
|
{
|
|
*pch = '\0';
|
|
}
|
|
|
|
if ( pch != NULL )
|
|
{
|
|
if ( !strURLParams.Copy( pch + 1 ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if not params specified in #EXEC statement,
|
|
// then use params specified with .STM document
|
|
|
|
if ( !strURLParams.Copy( _pECB->lpszQueryString ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Is this a map
|
|
//
|
|
|
|
pchStart = strURL.QueryStr();
|
|
pchTmp = pchStart;
|
|
|
|
while ( *pchTmp != '\0' )
|
|
{
|
|
pchTmp = strchr( pchTmp + 1, '.' );
|
|
if ( pchTmp == NULL )
|
|
{
|
|
break;
|
|
}
|
|
if ( !LookupExtMap( pchTmp,
|
|
&strGatewayImage,
|
|
&GatewayType,
|
|
&cchExt,
|
|
&fImageInURL ) )
|
|
{
|
|
SetLastError( ERROR_FILE_NOT_FOUND );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( GatewayType != GATEWAY_UNKNOWN )
|
|
{
|
|
cchToEnd = (pchTmp+cchExt) - pchStart;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ( GatewayType != GATEWAY_BGI ) && ( GatewayType != GATEWAY_CGI ) )
|
|
{
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !strPathInfo.Copy( ( fImageInURL || *(pchStart + cchToEnd) ?
|
|
pchStart + cchToEnd :
|
|
pchStart ) ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
*( strURL.QueryStr() + cchToEnd ) = '\0';
|
|
|
|
if ( strPathInfo.IsEmpty() )
|
|
{
|
|
if ( !strPathInfo.Copy( _pECB->lpszPathInfo ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( !LookupVirtualRoot( strURL.QueryStr(),
|
|
&strPhysical,
|
|
VROOT_MASK_EXECUTE,
|
|
&cbDirSize ) )
|
|
{
|
|
DWORD dwErrCode;
|
|
LPCTSTR apszParms[ 1 ];
|
|
|
|
if ( GetLastError() == ERROR_ACCESS_DENIED )
|
|
{
|
|
dwErrCode = SSINCMSG_NO_EXECUTE_PERMISSION;
|
|
}
|
|
else
|
|
{
|
|
dwErrCode = SSINCMSG_CANT_RESOLVE_PATH;
|
|
}
|
|
apszParms[ 0 ] = strURL.QueryStr();
|
|
|
|
SSISendError( dwErrCode,
|
|
apszParms );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if ( GatewayType == GATEWAY_CGI )
|
|
{
|
|
if ( !strWorkingDir.Copy( strPhysical.QueryStr() ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
*( strWorkingDir.QueryStr() + cbDirSize ) = '\0';
|
|
}
|
|
|
|
if ( GatewayType == GATEWAY_BGI )
|
|
{
|
|
fRet = ProcessBGI( this,
|
|
strGatewayImage.IsEmpty() ? &strPhysical : &strGatewayImage,
|
|
&strURLParams,
|
|
&strPathInfo );
|
|
}
|
|
else
|
|
{
|
|
if ( !strGatewayImage.IsEmpty() )
|
|
{
|
|
STR strDecodedParams;
|
|
STR strCmdLine;
|
|
|
|
if ( !SetupCmdLine( &strDecodedParams,
|
|
strURLParams ) ||
|
|
!strCmdLine.Resize( strGatewayImage.QueryCB() +
|
|
strPhysical.QueryCB() +
|
|
strDecodedParams.QueryCB()))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( IsCmdExe( strGatewayImage.QueryStr() ))
|
|
{
|
|
//
|
|
// Make sure the path to the file exists if we're running
|
|
// the command interpreter
|
|
//
|
|
|
|
if ( GetFileAttributes( strPhysical.QueryStr() ) ==
|
|
0xffffffff )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
wsprintf( strCmdLine.QueryStr(),
|
|
strGatewayImage.QueryStr(),
|
|
strPhysical.QueryStr(),
|
|
strDecodedParams.QueryStr() );
|
|
|
|
fRet = ProcessCGI( this,
|
|
NULL,
|
|
&strURLParams,
|
|
&strWorkingDir,
|
|
&strCmdLine,
|
|
&strPathInfo );
|
|
}
|
|
else
|
|
{
|
|
fRet = ProcessCGI( this,
|
|
&strPhysical,
|
|
&strURLParams,
|
|
&strWorkingDir,
|
|
NULL,
|
|
&strPathInfo );
|
|
}
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
HANDLE
|
|
SSI_REQUEST::QueryPrimaryToken( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a primary token for use with CreateProcessAsUser()
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Primary token handle, or NULL if failed
|
|
|
|
--*/
|
|
{
|
|
if ( _hPrimary != NULL )
|
|
{
|
|
}
|
|
else if ( !_pHTTPRequest->QueryVrootImpersonateHandle() )
|
|
{
|
|
return _pHTTPRequest->QueryAuthenticationObj()->QueryPrimaryToken();
|
|
}
|
|
else
|
|
{
|
|
if ( !DuplicateTokenEx( _pHTTPRequest->QueryVrootImpersonateHandle(),
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenPrimary,
|
|
&_hPrimary ))
|
|
{
|
|
TCP_PRINT(( DBG_CONTEXT,
|
|
"[QueryPrimaryToken] DuplicateToken failed, error %lx\n",
|
|
GetLastError() ));
|
|
_hPrimary = NULL;
|
|
}
|
|
}
|
|
return _hPrimary;
|
|
}
|
|
|
|
BOOL
|
|
SSI_REQUEST::ProcessSSI( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the top level routine for retrieving a server side include
|
|
file.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
DWORD pcLevel = 0;
|
|
STR strTemp;
|
|
|
|
|
|
TCP_PRINT(( DBG_CONTEXT,
|
|
"[ProcessSSI] about to process %s\n",
|
|
_strFilename.QueryStr() ));
|
|
|
|
return SSISend( this, &_strFilename, &_strURL, &pcLevel );
|
|
}
|
|
|
|
// Standalone functions
|
|
|
|
BOOL
|
|
SSISend(
|
|
IN SSI_REQUEST * pRequest,
|
|
IN STR * pstrFile,
|
|
IN STR * pstrURL,
|
|
IN OUT DWORD * pcLevel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method builds the Server Side Include Element List the first time
|
|
a .stm file is sent. Subsequently, the element list is checked out from
|
|
the associated cache blob.
|
|
|
|
Arguments:
|
|
|
|
pRequest - SSI Request
|
|
pstrFile - File to send
|
|
pstrURL - URL (from root) of this file
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
SSI_ELEMENT_LIST * pSEL;
|
|
BOOL fRet = TRUE;
|
|
BOOL fAccessDenied = FALSE;
|
|
|
|
if ( *pcLevel > SSI_MAX_NESTED_INCLUDES )
|
|
{
|
|
pRequest->SSISendError( SSINCMSG_MAX_NESTED_INCLUDES,
|
|
NULL );
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef DO_CACHE
|
|
SSI_ELEMENT_LIST ** ppSELBlob;
|
|
DWORD cbBlob;
|
|
BOOL fCached = TRUE;
|
|
BOOL fMustFree = FALSE;
|
|
|
|
//
|
|
// Check if we've already processed the file and its in cache
|
|
//
|
|
if ( pRequest->IsAnonymous() &&
|
|
TsCheckOutCachedBlob( *g_ptsvcCache,
|
|
pstrFile->QueryStr(),
|
|
SSI_DEMUX,
|
|
(VOID**) &ppSELBlob,
|
|
&cbBlob ) )
|
|
{
|
|
//
|
|
// found it! Send a response header if processing base file
|
|
//
|
|
|
|
if ( pRequest->IsBaseFile() )
|
|
{
|
|
pRequest->SetNotBaseFile();
|
|
pRequest->SendResponseHeader( NULL, SSI_HEADER );
|
|
}
|
|
|
|
pSEL = *ppSELBlob;
|
|
|
|
goto SendSSI;
|
|
}
|
|
#endif
|
|
//
|
|
// This file hasn't been processed yet so go process it
|
|
//
|
|
|
|
pSEL = SSIParse( pRequest, pstrFile, pstrURL, &fAccessDenied );
|
|
|
|
if ( pRequest->IsBaseFile() )
|
|
{
|
|
pRequest->SetNotBaseFile();
|
|
if ( pSEL == NULL && fAccessDenied )
|
|
{
|
|
pRequest->SendResponseHeader( SSI_ACCESS_DENIED,
|
|
NULL );
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Send the response header now even though we do not know for sure
|
|
// whether all of the included files exist. If we find a file that
|
|
// doesn't exist then we'll just include an error message in the document
|
|
//
|
|
|
|
pRequest->SendResponseHeader( NULL,
|
|
SSI_HEADER );
|
|
}
|
|
}
|
|
if ( pSEL == NULL )
|
|
{
|
|
LPCTSTR apszParms[ 2 ];
|
|
CHAR pszNumBuf[ SSI_MAX_NUMBER_STRING ];
|
|
_ultoa( GetLastError(), pszNumBuf, 10 );
|
|
apszParms[ 0 ] = pstrFile->QueryStr();
|
|
apszParms[ 1 ] = pszNumBuf;
|
|
|
|
pRequest->SSISendError( SSINCMSG_ERROR_HANDLING_FILE,
|
|
apszParms );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef DO_CACHE
|
|
|
|
if ( !pRequest->IsAnonymous() )
|
|
{
|
|
fCached = FALSE;
|
|
goto SendSSI;
|
|
}
|
|
|
|
|
|
ppSELBlob = &pSEL;
|
|
|
|
//
|
|
// In case allocation/caching fails, initialize ppSELBlob
|
|
//
|
|
|
|
if ( !TsAllocateEx( *g_ptsvcCache,
|
|
sizeof( PVOID ),
|
|
(VOID**) &ppSELBlob,
|
|
(PUSER_FREE_ROUTINE) FreeSELBlob ) )
|
|
{
|
|
fCached = FALSE;
|
|
goto SendSSI;
|
|
}
|
|
|
|
*ppSELBlob = pSEL;
|
|
|
|
if ( !TsCacheDirectoryBlob( *g_ptsvcCache,
|
|
pstrFile->QueryStr(),
|
|
SSI_DEMUX,
|
|
ppSELBlob,
|
|
sizeof( PVOID ),
|
|
TRUE ) )
|
|
{
|
|
// remember to free the blob
|
|
fMustFree= TRUE;
|
|
fCached = FALSE;
|
|
goto SendSSI;
|
|
}
|
|
|
|
#endif
|
|
goto SendSSI;
|
|
|
|
SendSSI:
|
|
|
|
TCP_ASSERT( pSEL->CheckSignature() );
|
|
|
|
if ( !pSEL->Send( pRequest, pcLevel ) )
|
|
{
|
|
//
|
|
// Send a failure message
|
|
//
|
|
|
|
LPCTSTR apszParms[ 2 ];
|
|
CHAR pszNumBuf[ SSI_MAX_NUMBER_STRING ];
|
|
_ultoa( GetLastError(), pszNumBuf, 10 );
|
|
apszParms[ 0 ] = pstrFile->QueryStr();
|
|
apszParms[ 1 ] = pszNumBuf;
|
|
|
|
pRequest->SSISendError( SSINCMSG_ERROR_HANDLING_FILE,
|
|
apszParms );
|
|
fRet = FALSE;
|
|
}
|
|
|
|
#ifdef DO_CACHE
|
|
if ( fCached )
|
|
{
|
|
TCP_REQUIRE( TsCheckInCachedBlob( *g_ptsvcCache,
|
|
ppSELBlob ) );
|
|
}
|
|
else
|
|
{
|
|
if ( fMustFree )
|
|
{
|
|
TCP_REQUIRE( TsFree( *g_ptsvcCache,
|
|
ppSELBlob ) );
|
|
}
|
|
else
|
|
{
|
|
delete *ppSELBlob;
|
|
}
|
|
}
|
|
#else
|
|
delete pSEL;
|
|
#endif
|
|
return fRet;
|
|
}
|
|
|
|
SSI_ELEMENT_LIST *
|
|
SSIParse(
|
|
IN SSI_REQUEST * pRequest,
|
|
IN STR * pstrFile,
|
|
IN STR * pstrURL,
|
|
OUT BOOL * pfAccessDenied
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method opens and parses the specified server side include file.
|
|
|
|
Note: The HTTP headers have already been sent at this point so for any
|
|
subsequent non-catastrophic errors, we have to insert them into the output
|
|
stream.
|
|
|
|
We keep the file open but that's ok because if a change dir notification
|
|
occurs, the cache blob will get decached at which point we will close
|
|
all of our open handles.
|
|
|
|
Arguments:
|
|
|
|
pRequest - Request context
|
|
pstrFile - File to open and parse
|
|
pstrURL - The URL path of this file
|
|
pfAccessDenied - Was .STM file access denied?
|
|
|
|
Return Value:
|
|
|
|
Created Server Side Include File on success, NULL on failure.
|
|
|
|
--*/
|
|
{
|
|
SSI_FILE * pssiFile = NULL;
|
|
SSI_ELEMENT_LIST * pSEL = NULL;
|
|
CHAR * pchBeginRange = NULL;
|
|
CHAR * pchFilePos = NULL;
|
|
CHAR * pchBeginFile = NULL;
|
|
CHAR * pchEOF = NULL;
|
|
DWORD cbSizeLow, cbSizeHigh;
|
|
|
|
//
|
|
// Create the element list
|
|
//
|
|
|
|
pSEL = new SSI_ELEMENT_LIST;
|
|
|
|
if ( pSEL == NULL )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Set the URL (to be used in calculating FILE="xxx" paths
|
|
//
|
|
|
|
if ( !pSEL->SetURL( pstrURL ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
|
|
pssiFile = new SSI_FILE( pstrFile, pRequest->GetUser() );
|
|
|
|
if ( !pssiFile || !pssiFile->IsValid() )
|
|
{
|
|
*pfAccessDenied = ( GetLastError() == ERROR_ACCESS_DENIED );
|
|
goto ErrorExit;
|
|
}
|
|
else
|
|
{
|
|
*pfAccessDenied = FALSE;
|
|
}
|
|
|
|
pSEL->SetFile( pssiFile );
|
|
|
|
//
|
|
// Make sure a parent doesn't try and include a directory
|
|
//
|
|
|
|
if ( pssiFile->SSIGetFileAttributes() & FILE_ATTRIBUTE_DIRECTORY )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ( !pssiFile->SSIGetFileSize( &cbSizeLow, &cbSizeHigh ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ( cbSizeHigh )
|
|
{
|
|
SetLastError( ERROR_NOT_SUPPORTED );
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Create a file mapping, we shouldn't need to impersonate as we already
|
|
// have the file open
|
|
//
|
|
|
|
if ( !pSEL->Map() )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pchFilePos = pchBeginFile = pchBeginRange = pSEL->QueryData();
|
|
pchEOF = pchFilePos + cbSizeLow;
|
|
|
|
//
|
|
// Scan for "<!--" or "<%"
|
|
//
|
|
|
|
while ( TRUE )
|
|
{
|
|
while ( pchFilePos < pchEOF && *pchFilePos != '<' )
|
|
{
|
|
pchFilePos++;
|
|
}
|
|
|
|
if ( pchFilePos >= pchEOF )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Is this one of our tags?
|
|
//
|
|
|
|
if ( pchFilePos[1] == '%' ||
|
|
!strncmp( pchFilePos, "<!--", 4 ))
|
|
{
|
|
CHAR * pchBeginTag = pchFilePos;
|
|
SSI_COMMANDS CommandType;
|
|
SSI_TAGS TagType;
|
|
CHAR achTagString[SSI_MAX_PATH + 1];
|
|
BOOL fValidTag;
|
|
|
|
//
|
|
// Get the tag info. The file position will be advanced to the
|
|
// first character after the tag
|
|
//
|
|
|
|
if ( !ParseSSITag( &pchFilePos,
|
|
pchEOF,
|
|
&fValidTag,
|
|
&CommandType,
|
|
&TagType,
|
|
achTagString ))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If it's a tag we don't recognize then ignore it
|
|
//
|
|
|
|
if ( !fValidTag )
|
|
{
|
|
pchFilePos++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add the data up to the tag as a byte range
|
|
//
|
|
|
|
if ( pchBeginRange != pchBeginTag )
|
|
{
|
|
if ( !pSEL->AppendByteRange( pchBeginRange - pchBeginFile,
|
|
pchBeginTag - pchBeginRange ))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pchBeginRange = pchFilePos;
|
|
}
|
|
|
|
//
|
|
// Add the tag
|
|
//
|
|
|
|
if ( !pSEL->AppendCommand( CommandType,
|
|
TagType,
|
|
achTagString ))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not one of our tags, skip the openning angle bracket
|
|
//
|
|
|
|
pchFilePos++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tack on the last byte range
|
|
//
|
|
|
|
if ( pchFilePos > pchBeginRange )
|
|
{
|
|
if ( !pSEL->AppendByteRange( pchBeginRange - pchBeginFile,
|
|
pchFilePos - pchBeginRange ))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
pSEL->UnMap();
|
|
|
|
return pSEL;
|
|
|
|
ErrorExit:
|
|
|
|
if ( pSEL != NULL )
|
|
{
|
|
delete pSEL;
|
|
}
|
|
else if ( pssiFile != NULL )
|
|
{
|
|
delete pssiFile;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL
|
|
ParseSSITag(
|
|
IN OUT CHAR * * ppchFilePos,
|
|
IN CHAR * pchEOF,
|
|
OUT BOOL * pfValidTag,
|
|
OUT SSI_COMMANDS * pCommandType,
|
|
OUT SSI_TAGS * pTagType,
|
|
OUT CHAR * pszTagString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function picks apart an NCSA style server side include expression
|
|
|
|
The general form of a server side include directive is:
|
|
|
|
<[!-- or %]#[command] [tag]="[value]"[-- or %]>
|
|
|
|
For example:
|
|
|
|
<!--#include file="myfile.txt"-->
|
|
<%#echo var="HTTP_USER_AGENT"%>
|
|
<!--#fsize virtual="/dir/bar.htm"-->
|
|
|
|
For valid commands and tags see \iis\specs\ssi.doc
|
|
|
|
Arguments:
|
|
|
|
ppchFilePos - Pointer to first character of tag on way in, pointer
|
|
to first character after tag on way out if the tag is valid
|
|
pchEOF - Points to first byte beyond the end of the file
|
|
pfValidTag - Set to TRUE if this is a tag we support and all of the
|
|
parameters have been supplied
|
|
pCommandType - Receives SSI command
|
|
pTagType - Receives SSI tag
|
|
pszTagString - Receives value of pTagType. Must be > SSI_MAX_PATH.
|
|
|
|
Return Value:
|
|
|
|
TRUE if no errors occurred.
|
|
|
|
--*/
|
|
{
|
|
CHAR * pchFilePos = *ppchFilePos;
|
|
CHAR * pchEOT;
|
|
CHAR * pchEndQuote;
|
|
DWORD i;
|
|
DWORD cbToCopy;
|
|
DWORD cbJumpLen = 0;
|
|
BOOL fNewStyle; // <% format
|
|
|
|
TCP_ASSERT( *pchFilePos == '<' );
|
|
|
|
//
|
|
// Assume this is bad tag
|
|
//
|
|
|
|
*pfValidTag = FALSE;
|
|
|
|
if ( !strncmp( pchFilePos, "<!--", 4 ) )
|
|
{
|
|
fNewStyle = FALSE;
|
|
}
|
|
else if ( !strncmp( pchFilePos, "<%", 2 ) )
|
|
{
|
|
fNewStyle = TRUE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Find the closing comment token (either --> or %>). The reason
|
|
// why we shouldn't simply look for a > is because we want to allow
|
|
// the user to embed HTML <tags> in the directive
|
|
// (ex. <!--#CONFIG ERRMSG="<B>ERROR!!!</B>-->)
|
|
//
|
|
|
|
pchEOT = strstr( pchFilePos, fNewStyle ? "%>" : "-->" );
|
|
if ( !pchEOT )
|
|
{
|
|
return FALSE;
|
|
}
|
|
cbJumpLen = fNewStyle ? 2 : 3;
|
|
|
|
//
|
|
// Find the '#' that prefixes the command
|
|
//
|
|
|
|
pchFilePos = SSISkipTo( pchFilePos, '#', pchEOT );
|
|
|
|
if ( !pchFilePos )
|
|
{
|
|
//
|
|
// No command, bail for this tag
|
|
//
|
|
// CODEWORK - Check for if expression here
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Lookup the command
|
|
//
|
|
|
|
i = 0;
|
|
while ( SSICmdMap[i].pszCommand )
|
|
{
|
|
if ( *SSICmdMap[i].pszCommand == tolower( *pchFilePos ) &&
|
|
!_strnicmp( SSICmdMap[i].pszCommand,
|
|
pchFilePos,
|
|
SSICmdMap[i].cchCommand ))
|
|
{
|
|
*pCommandType = SSICmdMap[i].ssiCmd;
|
|
|
|
//
|
|
// Note the space after the command is included in cchCommand
|
|
//
|
|
|
|
pchFilePos += SSICmdMap[i].cchCommand;
|
|
goto FoundCommand;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
//
|
|
// Unrecognized command, bail
|
|
//
|
|
|
|
return TRUE;
|
|
|
|
FoundCommand:
|
|
|
|
//
|
|
// Next, find the tag name
|
|
//
|
|
|
|
pchFilePos = SSISkipWhite( pchFilePos, pchEOT );
|
|
|
|
if ( !pchFilePos )
|
|
return TRUE;
|
|
|
|
i = 0;
|
|
while ( SSITagMap[i].pszTag )
|
|
{
|
|
if ( *SSITagMap[i].pszTag == tolower( *pchFilePos ) &&
|
|
!_strnicmp( SSITagMap[i].pszTag,
|
|
pchFilePos,
|
|
SSITagMap[i].cchTag ))
|
|
{
|
|
*pTagType = SSITagMap[i].ssiTag;
|
|
pchFilePos += SSITagMap[i].cchTag;
|
|
goto FoundTag;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
//
|
|
// Tag not found, bail
|
|
//
|
|
|
|
return TRUE;
|
|
|
|
FoundTag:
|
|
|
|
//
|
|
// Skip to the quoted tag value, then find the close quote
|
|
//
|
|
|
|
pchFilePos = SSISkipTo( pchFilePos, '"', pchEOT );
|
|
|
|
if ( !pchFilePos )
|
|
return TRUE;
|
|
|
|
pchEndQuote = SSISkipTo( ++pchFilePos, '"', pchEOT );
|
|
|
|
if ( !pchEndQuote )
|
|
return TRUE;
|
|
|
|
cbToCopy = min( (pchEndQuote - pchFilePos), SSI_MAX_PATH );
|
|
|
|
memcpy( pszTagString,
|
|
pchFilePos,
|
|
cbToCopy );
|
|
|
|
pszTagString[cbToCopy] = '\0';
|
|
|
|
*pfValidTag = TRUE;
|
|
|
|
*ppchFilePos = pchEOT + cbJumpLen;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
FreeSELBlob(
|
|
VOID * pvCacheBlob
|
|
)
|
|
{
|
|
if ( pvCacheBlob )
|
|
{
|
|
TCP_ASSERT( (*((SSI_ELEMENT_LIST **)pvCacheBlob))->CheckSignature() );
|
|
delete *((SSI_ELEMENT_LIST **)pvCacheBlob);
|
|
}
|
|
}
|
|
|
|
CHAR *
|
|
SSISkipTo(
|
|
IN CHAR * pchFilePos,
|
|
IN CHAR ch,
|
|
IN CHAR * pchEOF
|
|
)
|
|
{
|
|
return (CHAR*) memchr( pchFilePos, ch, pchEOF - pchFilePos );
|
|
}
|
|
|
|
CHAR *
|
|
SSISkipWhite(
|
|
IN CHAR * pchFilePos,
|
|
IN CHAR * pchEOF
|
|
)
|
|
{
|
|
while ( pchFilePos < pchEOF )
|
|
{
|
|
if ( !isspace( *pchFilePos ) )
|
|
return pchFilePos;
|
|
|
|
pchFilePos++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// ISAPI DLL Required Entry Points
|
|
//
|
|
|
|
DWORD
|
|
WINAPI
|
|
HttpExtensionProc(
|
|
EXTENSION_CONTROL_BLOCK * pecb
|
|
)
|
|
{
|
|
BOOL bRet;
|
|
|
|
TCP_PRINT(( DBG_CONTEXT,
|
|
"HttpExtensionProc() entry point called\n" ));
|
|
|
|
SSI_REQUEST ssiReq = pecb;
|
|
if ( !ssiReq.IsValid() || !ssiReq.ProcessSSI() )
|
|
{
|
|
LPCTSTR apsz[ 1 ];
|
|
STR strLogMessage;
|
|
|
|
apsz[ 0 ] = pecb->lpszPathInfo;
|
|
strLogMessage.FormatString( SSINCMSG_LOG_ERROR,
|
|
apsz,
|
|
SSI_DLL_NAME );
|
|
|
|
strncpy( pecb->lpszLogData,
|
|
strLogMessage.QueryStr(),
|
|
HSE_LOG_BUFFER_LEN );
|
|
|
|
return HSE_STATUS_ERROR;
|
|
}
|
|
else
|
|
{
|
|
return HSE_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
GetExtensionVersion(
|
|
HSE_VERSION_INFO * pver
|
|
)
|
|
{
|
|
pver->dwExtensionVersion = MAKELONG( 0, 2 );
|
|
strcpy( pver->lpszExtensionDesc,
|
|
"Server Side Include Extension DLL" );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
TerminateExtension(
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
DLLEntry(
|
|
HINSTANCE hDll,
|
|
DWORD dwReason,
|
|
LPVOID lpvReserved
|
|
)
|
|
{
|
|
switch ( dwReason )
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
CREATE_DEBUG_PRINT_OBJECT( SSI_DLL_NAME );
|
|
SET_DEBUG_FLAGS( 0 );
|
|
|
|
if ( InitializeCGI() != NO_ERROR )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ( InitializeBGI() != NO_ERROR )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ( ReadExtMap() != NO_ERROR )
|
|
{
|
|
return FALSE;
|
|
}
|
|
#ifdef DO_CACHE
|
|
g_ptsvcCache = new TSVC_CACHE( SSINC_SVC_ID );
|
|
if ( g_ptsvcCache == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
DisableThreadLibraryCalls( hDll );
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
TerminateCGI();
|
|
TerminateExtMap();
|
|
#ifdef DO_CACHE
|
|
TsCacheFlush( SSINC_SVC_ID );
|
|
delete g_ptsvcCache;
|
|
#endif
|
|
DELETE_DEBUG_PRINT_OBJECT();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|