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.
1833 lines
52 KiB
1833 lines
52 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ssi_include_file.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\gateways\ssinc\ssinc.cxx.
|
|
|
|
A STM file may include other files. Each of those files is represented
|
|
by SSI_INCLUDE_FILE class instance
|
|
Most of the output generation is happening in this file
|
|
|
|
|
|
Author:
|
|
|
|
Ming Lu (MingLu) 5-Apr-2000
|
|
|
|
Revision history
|
|
Jaroslad Dec-2000
|
|
- modified to execute asynchronously
|
|
|
|
Jaroslad Apr-2001
|
|
- added VectorSend support, keepalive, split to multiple source files
|
|
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.hxx"
|
|
|
|
//
|
|
// 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
|
|
};
|
|
|
|
//
|
|
// SSI_INCLUDE_FILE methods implementation
|
|
//
|
|
|
|
//static
|
|
ALLOC_CACHE_HANDLER * SSI_INCLUDE_FILE::sm_pachSsiIncludeFiles = NULL;
|
|
|
|
|
|
SSI_INCLUDE_FILE::~SSI_INCLUDE_FILE( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor
|
|
|
|
--*/
|
|
|
|
{
|
|
if ( _pSsiFile != NULL )
|
|
{
|
|
_pSsiFile->DereferenceSsiFile();
|
|
_pSsiFile = NULL;
|
|
}
|
|
}
|
|
|
|
// static
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::CreateInstance(
|
|
IN STRU & strFilename,
|
|
IN STRU & strURL,
|
|
IN SSI_REQUEST * pRequest,
|
|
IN SSI_INCLUDE_FILE * pParent,
|
|
OUT SSI_INCLUDE_FILE ** ppSsiIncludeFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Instantiate SSI_INCLUDE_FILE
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
SSI_INCLUDE_FILE * pSsiIncludeFile = NULL;
|
|
|
|
DBG_ASSERT( pRequest != NULL );
|
|
|
|
pSsiIncludeFile = new SSI_INCLUDE_FILE( pRequest,
|
|
pParent );
|
|
if ( pSsiIncludeFile == NULL )
|
|
{
|
|
return HRESULT( ERROR_OUTOFMEMORY );
|
|
}
|
|
|
|
hr = pSsiIncludeFile->Initialize( strFilename,
|
|
strURL );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
//
|
|
// pass created SSI_INCLUDE_FILE through OUT parameter
|
|
//
|
|
*ppSsiIncludeFile = pSsiIncludeFile;
|
|
return S_OK;
|
|
|
|
failed:
|
|
DBG_ASSERT( FAILED( hr ) );
|
|
|
|
if ( pSsiIncludeFile != NULL )
|
|
{
|
|
delete pSsiIncludeFile;
|
|
pSsiIncludeFile = NULL;
|
|
}
|
|
*ppSsiIncludeFile = NULL;
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::Initialize(
|
|
IN STRU & strFilename,
|
|
IN STRU & strURL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Private initialization rouinte for CreateInstance
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if ( FAILED( hr = _strFilename.Copy( strFilename ) ) )
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
if ( FAILED( hr =_strURL.Copy( strURL ) ) )
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
if ( FAILED( hr =_strTimeFmt.Copy( SSI_DEF_TIMEFMT ) ) )
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
//
|
|
// get the SSI_FILE (it enables acces to file data and
|
|
// also contains SSI_ELEMENT_LIST
|
|
//
|
|
|
|
hr = SSI_FILE::GetReferencedInstance(
|
|
_strFilename,
|
|
_pRequest->GetUserToken(),
|
|
&_pSsiFile );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
SetState( SIF_STATE_READY );
|
|
|
|
return S_OK;
|
|
|
|
failed:
|
|
DBG_ASSERT( FAILED( hr ) );
|
|
|
|
//
|
|
// In the case of error set state to Processed
|
|
// Note: Don't set state to SIF_STATE_COMPLETED because that would cause
|
|
// buffered data from the response not be sent
|
|
// (including the last error message)
|
|
//
|
|
SetState( SIF_STATE_PROCESSED );
|
|
|
|
//
|
|
// leftover data structures will be cleaned up in the destructor
|
|
//
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoWork(
|
|
IN HRESULT hrLastOp,
|
|
OUT BOOL *pfAsyncPending
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method walks the element list sending the appropriate chunks of
|
|
data
|
|
|
|
Arguments:
|
|
|
|
hrLastOp - error of the last asynchronous operation
|
|
pfAsyncPending - TRUE if there is pending async operation
|
|
Return Value:
|
|
|
|
HRESULT
|
|
--*/
|
|
{
|
|
HSE_EXEC_URL_STATUS ExecUrlStatus;
|
|
SSI_ELEMENT_ITEM * pSEI;
|
|
DWORD dwID = 0;
|
|
LPSTR apszParms[ 2 ] = { NULL, NULL };
|
|
CHAR achNumberBuffer[ SSI_MAX_NUMBER_STRING ];
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DBG_ASSERT( _pRequest != NULL );
|
|
DBG_ASSERT( pfAsyncPending != NULL );
|
|
|
|
*pfAsyncPending = FALSE;
|
|
|
|
if ( FAILED( hrLastOp ) )
|
|
{
|
|
//
|
|
// Last asynchronous operation failed
|
|
// only in the state: SIF_STATE_EXEC_URL_PENDING we will proceed if error occured
|
|
//
|
|
|
|
if ( _State != SIF_STATE_EXEC_URL_PENDING )
|
|
{
|
|
//
|
|
// Completion error means that we are forced to finish processing
|
|
//
|
|
SetState( SIF_STATE_COMPLETED );
|
|
hr = hrLastOp;
|
|
}
|
|
}
|
|
|
|
while( _State != SIF_STATE_COMPLETED )
|
|
{
|
|
|
|
switch( _State )
|
|
{
|
|
case SIF_STATE_READY:
|
|
//
|
|
// All the necessary data structures are ready
|
|
//
|
|
// Set the Response Headers and switch to PROCESSING state
|
|
//
|
|
|
|
//
|
|
// Set response headers if we are in the top level INCLUDE file
|
|
//
|
|
SetState( SIF_STATE_PROCESSING );
|
|
|
|
if ( IsBaseFile() )
|
|
{
|
|
if ( _pSsiFile->QueryHasDirectives() )
|
|
{
|
|
//
|
|
// there are SSI directives to be processesed
|
|
// send only SSI_HEADER
|
|
//
|
|
|
|
return _pRequest->GetVectorBuffer().AddVectorHeaders(
|
|
SSI_HEADER );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There are no SSI directives, we will be able to send
|
|
// the whole response at once
|
|
//
|
|
CHAR * pszResponseHeaders = NULL;
|
|
BOOL fIncludesContentLength = FALSE;
|
|
DWORD cbFileSize = 0;
|
|
|
|
hr = _pSsiFile->GetResponseHeaders( &pszResponseHeaders,
|
|
&fIncludesContentLength );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
DBG_ASSERT( pszResponseHeaders != NULL );
|
|
|
|
hr = _pRequest->GetVectorBuffer().AddVectorHeaders(
|
|
pszResponseHeaders,
|
|
fIncludesContentLength );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// Optimization:
|
|
// If there are no directives in the file then AddToVector
|
|
// and set state to complete.
|
|
//
|
|
hr = _pSsiFile->SSIGetFileSize( &cbFileSize );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if ( _pSsiFile->SSIGetFileData() != NULL )
|
|
{
|
|
//
|
|
// file cached in memory
|
|
//
|
|
hr = _pRequest->GetVectorBuffer().AddToVector(
|
|
(PCHAR)_pSsiFile->SSIGetFileData(),
|
|
cbFileSize );
|
|
}
|
|
else
|
|
{
|
|
hr = _pRequest->GetVectorBuffer().AddFileChunkToVector(
|
|
0,
|
|
cbFileSize,
|
|
_pSsiFile->SSIGetFileHandle() );
|
|
}
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return( hr );
|
|
}
|
|
|
|
SetState( SIF_STATE_PROCESSED );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SIF_STATE_PROCESSING:
|
|
//
|
|
// There are few cases when ProcessElements() will return
|
|
// a) request completed
|
|
// b) pending operation
|
|
// c) child include file to be processed
|
|
//
|
|
// in any case return back to caller
|
|
//
|
|
|
|
hr = ProcessSsiElements( pfAsyncPending );
|
|
if ( SUCCEEDED( hr ) &&
|
|
!*pfAsyncPending )
|
|
{
|
|
if (_State == SIF_STATE_INCLUDE_CHILD_PENDING)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
case SIF_STATE_INCLUDE_CHILD_PENDING:
|
|
//
|
|
// Child include completed. Restore processing of current include file
|
|
//
|
|
SetState( SIF_STATE_PROCESSING );
|
|
break;
|
|
|
|
case SIF_STATE_EXEC_URL_PENDING:
|
|
|
|
//
|
|
// We were able to spawn child request. Get the status
|
|
//
|
|
pSEI = CONTAINING_RECORD( _pCurrentEntry, SSI_ELEMENT_ITEM, _ListEntry );
|
|
if( _pRequest->GetECB()->ServerSupportFunction(
|
|
_pRequest->GetECB()->ConnID,
|
|
HSE_REQ_GET_EXEC_URL_STATUS,
|
|
&ExecUrlStatus,
|
|
NULL,
|
|
NULL
|
|
) )
|
|
{
|
|
if ( ExecUrlStatus.uHttpStatusCode >= 400 )
|
|
{
|
|
apszParms[ 0 ] = ( CHAR* )pSEI->QueryTagValue()->QueryStr();
|
|
if ( ExecUrlStatus.uHttpStatusCode == 403 )
|
|
{
|
|
dwID = SSINCMSG_NO_EXECUTE_PERMISSION;
|
|
}
|
|
else if ( ExecUrlStatus.dwWin32Error != 0 )
|
|
{
|
|
//
|
|
// If there was a Win32 error return that rather than
|
|
// HTTP error
|
|
//
|
|
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_CMD:
|
|
dwID = SSINCMSG_CANT_EXEC_CMD;
|
|
break;
|
|
case SSI_TAG_CGI:
|
|
dwID = SSINCMSG_CANT_EXEC_CGI;
|
|
break;
|
|
case SSI_TAG_ISA:
|
|
dwID = SSINCMSG_CANT_EXEC_ISA;
|
|
break;
|
|
default:
|
|
DBG_ASSERT( FALSE );
|
|
break;
|
|
|
|
}
|
|
_itoa( ExecUrlStatus.dwWin32Error, achNumberBuffer, 10 );
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
}
|
|
else
|
|
{
|
|
dwID = SSINCMSG_CANT_EXEC_CGI_REPORT_HTTP_STATUS;
|
|
_itoa( ExecUrlStatus.uHttpStatusCode, achNumberBuffer, 10 );
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
dwID = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_ultoa( GetLastError(), achNumberBuffer, 10 );
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_CANT_EXEC_CGI;
|
|
}
|
|
|
|
//
|
|
// EXEC_URL completed. Adjust State back to PROCESSING
|
|
//
|
|
|
|
SetState( SIF_STATE_PROCESSING );
|
|
|
|
if ( dwID != 0 )
|
|
{
|
|
hr = _pRequest->SSISendError( dwID, apszParms );
|
|
if ( FAILED (hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
break;
|
|
case SIF_STATE_VECTOR_SEND_PENDING:
|
|
|
|
//
|
|
// Vector was sent. It's time to reset Vector data and continue processing
|
|
//
|
|
if ( FAILED( hr = _pRequest->GetVectorBuffer().Reset() ) )
|
|
{
|
|
return hr;
|
|
}
|
|
SetState( SIF_STATE_PROCESSING );
|
|
break;
|
|
case SIF_STATE_PROCESSED:
|
|
//
|
|
// All the data was processed
|
|
// Make the VectorSend only for the Base file
|
|
//
|
|
//
|
|
if ( IsBaseFile() )
|
|
{
|
|
SetState( SIF_STATE_COMPLETE_PENDING );
|
|
hr = _pRequest->GetVectorBuffer().VectorSend(
|
|
pfAsyncPending,
|
|
TRUE /*FinalSend*/ );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// not a base file
|
|
// SSI_INCLUDE_FILE processing is completed
|
|
// however sending the response will happen later
|
|
// (either at the very end for the Base file
|
|
// of whenever ExecuteUrl is called)
|
|
//
|
|
SetState( SIF_STATE_COMPLETED );
|
|
break;
|
|
}
|
|
|
|
if( FAILED(hr) || !*pfAsyncPending )
|
|
{
|
|
SetState( SIF_STATE_COMPLETED );
|
|
_pRequest->GetVectorBuffer().Reset();
|
|
}
|
|
else
|
|
{
|
|
// wait for the completion of VectorSend() or bail out on error
|
|
return hr;
|
|
}
|
|
break;
|
|
|
|
case SIF_STATE_COMPLETE_PENDING:
|
|
//
|
|
// last VectorSend for SSI_INCLUDE_FILE of Sending Custom Error
|
|
// completed
|
|
//
|
|
if ( FAILED( hr = _pRequest->GetVectorBuffer().Reset() ) )
|
|
{
|
|
return hr;
|
|
}
|
|
SetState( SIF_STATE_COMPLETED );
|
|
hr = S_OK;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Unexpected State
|
|
//
|
|
DBG_ASSERT( _State > SIF_STATE_UNINITIALIZED && _State < SIF_STATE_UNKNOWN );
|
|
return E_FAIL;
|
|
} // switch( _State )
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::ProcessSsiElements(
|
|
OUT BOOL * pfAsyncPending
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method walks the element list sending the appropriate chunks of
|
|
data
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
--*/
|
|
{
|
|
|
|
STACK_STRU( strPath, SSI_DEFAULT_PATH_SIZE + 1 );
|
|
SSI_ELEMENT_ITEM * pSEI;
|
|
DWORD dwID;
|
|
LPSTR apszParms[ 2 ];
|
|
CHAR achNumberBuffer[ SSI_MAX_NUMBER_STRING ];
|
|
HRESULT hr = E_FAIL;
|
|
SSI_ELEMENT_LIST * pSEL = NULL;
|
|
SSI_EXEC_TYPE ssiExecType;
|
|
|
|
DBG_ASSERT( _pRequest != NULL );
|
|
DBG_ASSERT( _pSsiFile != NULL );
|
|
|
|
pSEL = _pSsiFile->GetSsiElementList();
|
|
|
|
if ( pSEL == NULL )
|
|
{
|
|
//
|
|
// empty list means empty file
|
|
//
|
|
SetState( SIF_STATE_PROCESSED );
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DBG_ASSERT( pSEL != NULL );
|
|
|
|
|
|
if( _pCurrentEntry == NULL )
|
|
{
|
|
_pCurrentEntry = pSEL->QueryListHead();
|
|
}
|
|
|
|
//
|
|
// Move CurrentEntry pointer to next element
|
|
//
|
|
_pCurrentEntry = _pCurrentEntry->Flink;
|
|
|
|
//
|
|
// Loop through each element and take the appropriate action
|
|
//
|
|
|
|
while( _pCurrentEntry != pSEL->QueryListHead() )
|
|
{
|
|
|
|
DBG_ASSERT( _State == SIF_STATE_PROCESSING );
|
|
|
|
pSEI = CONTAINING_RECORD( _pCurrentEntry,
|
|
SSI_ELEMENT_ITEM,
|
|
_ListEntry );
|
|
|
|
DBG_ASSERT( pSEI->CheckSignature() );
|
|
|
|
//
|
|
// Initialize error structures
|
|
//
|
|
|
|
dwID = 0;
|
|
apszParms[ 0 ] = NULL;
|
|
apszParms[ 1 ] = NULL;
|
|
|
|
switch ( pSEI->QueryCommand() )
|
|
{
|
|
case SSI_CMD_BYTERANGE:
|
|
// DBGPRINTF((DBG_CONTEXT, "SSI_CMD_BYTERANGE element, SSI_FILE %S, offset %d\n", _strFilename.QueryStr(), pSEI->QueryBegin()));
|
|
//
|
|
// SSIGetFileData() may be NULL if file is too large to be cached
|
|
// in memory by w3core FILE CACHE
|
|
//
|
|
|
|
if ( _pSsiFile->SSIGetFileData() != NULL )
|
|
{
|
|
if ( FAILED( hr = _pRequest->GetVectorBuffer().AddToVector(
|
|
(PCHAR)_pSsiFile->SSIGetFileData() +
|
|
pSEI->QueryBegin(),
|
|
pSEI->QueryLength() ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// if file is not cached in memory then file handle must be available
|
|
//
|
|
DBG_ASSERT( _pSsiFile->SSIGetFileHandle() != NULL );
|
|
if ( FAILED( hr = _pRequest->GetVectorBuffer().AddFileChunkToVector(
|
|
pSEI->QueryBegin(),
|
|
pSEI->QueryLength(),
|
|
_pSsiFile->SSIGetFileHandle() ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SSI_CMD_INCLUDE:
|
|
// DBGPRINTF((DBG_CONTEXT, "SSI_CMD_INCLUDE element, SSI_FILE %S\n", _strFilename.QueryStr()));
|
|
|
|
switch ( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_FILE:
|
|
case SSI_TAG_VIRTUAL:
|
|
{
|
|
STACK_STRU( strFullURL, SSI_DEFAULT_URL_SIZE + 1 );
|
|
|
|
if ( FAILED ( hr = GetFullPath( pSEI,
|
|
&strPath,
|
|
HSE_URL_FLAGS_READ,
|
|
&_strURL,
|
|
&strFullURL ) ) )
|
|
{
|
|
_ultoa( WIN32_FROM_HRESULT( hr ),
|
|
achNumberBuffer,
|
|
10 );
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
|
|
QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_ERROR_HANDLING_FILE;
|
|
break;
|
|
}
|
|
|
|
if ( IsRecursiveInclude( strPath ) )
|
|
{
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
|
|
QueryStr();
|
|
apszParms[ 1 ] = NULL;
|
|
dwID = SSINCMSG_ERROR_RECURSIVE_INCLUDE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Nested STM include
|
|
//
|
|
|
|
SSI_INCLUDE_FILE * pChild = NULL;
|
|
|
|
hr = SSI_INCLUDE_FILE::CreateInstance(
|
|
strPath,
|
|
strFullURL,
|
|
_pRequest,
|
|
this, /*Parent*/
|
|
&pChild );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
_ultoa( WIN32_FROM_HRESULT( hr ), achNumberBuffer, 10 );
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
|
|
QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_ERROR_HANDLING_FILE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// _pRequest is taking over the ownership of the pChild
|
|
// it will control it's lifetime
|
|
//
|
|
|
|
_pRequest->SetCurrentIncludeFile( pChild );
|
|
|
|
SetState( SIF_STATE_INCLUDE_CHILD_PENDING );
|
|
|
|
//
|
|
// Return back to SSI_REQUEST (_pRequest)
|
|
// SSI_REQUEST will start executing it just added SSI_INCLUDE_FILE (pChild)
|
|
//
|
|
// This way recursive function calls that were used in the previous
|
|
// implementation can be avoided (to makes possible to implement
|
|
// asynchronous processing)
|
|
//
|
|
|
|
return NO_ERROR;
|
|
}
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
break;
|
|
|
|
case SSI_CMD_FLASTMOD:
|
|
// DBGPRINTF((DBG_CONTEXT, "SSI_CMD_FLASTMOD element, SSI_FILE %S\n", _strFilename.QueryStr()));
|
|
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_FILE:
|
|
case SSI_TAG_VIRTUAL:
|
|
if ( FAILED( hr = GetFullPath( pSEI,
|
|
&strPath,
|
|
0,
|
|
&_strURL ) ) ||
|
|
FAILED( hr = DoFLastMod( &strPath ) ) )
|
|
{
|
|
|
|
_ultoa( WIN32_FROM_HRESULT( hr ),
|
|
achNumberBuffer,
|
|
10 );
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
|
|
QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_CANT_DO_FLASTMOD;
|
|
}
|
|
break;
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
break;
|
|
|
|
case SSI_CMD_CONFIG:
|
|
// DBGPRINTF((DBG_CONTEXT, "SSI_CMD_CONFIG element, SSI_FILE %S\n", _strFilename.QueryStr()));
|
|
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_ERRMSG:
|
|
if ( !_pRequest->SetUserErrorMessage(
|
|
pSEI->QueryTagValue() ) )
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
break;
|
|
case SSI_TAG_TIMEFMT:
|
|
if ( pSEI->QueryTagValue()->IsEmpty() ||
|
|
FAILED( _strTimeFmt.Resize( pSEI->QueryTagValue()->QueryCCH() ) ) )
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
|
|
if ( FAILED( hr = _strTimeFmt.Copy( pSEI->QueryTagValue()->QueryStr() ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
break;
|
|
case SSI_TAG_SIZEFMT:
|
|
if ( _strnicmp( SSI_DEF_BYTES,
|
|
( CHAR * )pSEI->QueryTagValue()->QueryStr(),
|
|
SSI_DEF_BYTES_LEN ) == 0 )
|
|
{
|
|
_fSizeFmtBytes = TRUE;
|
|
}
|
|
else if ( _strnicmp( SSI_DEF_ABBREV,
|
|
( CHAR * )pSEI->QueryTagValue()->QueryStr(),
|
|
SSI_DEF_ABBREV_LEN ) == 0 )
|
|
{
|
|
_fSizeFmtBytes = FALSE;
|
|
}
|
|
else
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
break;
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
break;
|
|
|
|
case SSI_CMD_FSIZE:
|
|
// DBGPRINTF((DBG_CONTEXT, "SSI_CMD_FSIZE element, SSI_FILE %S,\n", _strFilename.QueryStr()));
|
|
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_FILE:
|
|
case SSI_TAG_VIRTUAL:
|
|
if ( FAILED ( hr = GetFullPath( pSEI,
|
|
&strPath,
|
|
0,
|
|
&_strURL ) ) ||
|
|
FAILED ( hr = DoFSize( &strPath ) ) )
|
|
{
|
|
_ultoa( WIN32_FROM_HRESULT( hr ),
|
|
achNumberBuffer,
|
|
10 );
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
|
|
QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_CANT_DO_FSIZE;
|
|
|
|
}
|
|
break;
|
|
default:
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
|
|
break;
|
|
|
|
case SSI_CMD_ECHO:
|
|
// DBGPRINTF((DBG_CONTEXT, "SSI_CMD_ECHO element, SSI_FILE %S\n", _strFilename.QueryStr()));
|
|
|
|
if ( pSEI->QueryTag() == SSI_TAG_VAR )
|
|
{
|
|
// First let ISAPI try to evaluate variable.
|
|
hr = DoEchoISAPIVariable( pSEI->QueryTagValue() );
|
|
if ( SUCCEEDED (hr ) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwVar;
|
|
HRESULT hrEcho = E_FAIL;
|
|
|
|
// if ISAPI couldn't resolve var, try internal list
|
|
if ( !FindInternalVariable( pSEI->QueryTagValue(),
|
|
&dwVar ) )
|
|
{
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
|
|
QueryStr();
|
|
apszParms[ 1 ] = NULL;
|
|
dwID = SSINCMSG_CANT_FIND_VARIABLE;
|
|
}
|
|
else
|
|
{
|
|
switch( dwVar )
|
|
{
|
|
case SSI_VAR_DOCUMENT_NAME:
|
|
hrEcho = DoEchoDocumentName();
|
|
break;
|
|
case SSI_VAR_DOCUMENT_URI:
|
|
hrEcho = DoEchoDocumentURI();
|
|
break;
|
|
case SSI_VAR_QUERY_STRING_UNESCAPED:
|
|
hrEcho = DoEchoQueryStringUnescaped();
|
|
break;
|
|
case SSI_VAR_DATE_LOCAL:
|
|
hrEcho = DoEchoDateLocal();
|
|
break;
|
|
case SSI_VAR_DATE_GMT:
|
|
hrEcho = DoEchoDateGMT();
|
|
break;
|
|
case SSI_VAR_LAST_MODIFIED:
|
|
hrEcho = DoEchoLastModified();
|
|
break;
|
|
default:
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->
|
|
QueryTagValue()->QueryStr();
|
|
apszParms[ 1 ] = NULL;
|
|
dwID = SSINCMSG_CANT_FIND_VARIABLE;
|
|
}
|
|
|
|
if ( FAILED ( hrEcho ) )
|
|
{
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->
|
|
QueryTagValue()->QueryStr();
|
|
apszParms[ 1 ] = NULL;
|
|
dwID = SSINCMSG_CANT_EVALUATE_VARIABLE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
|
|
break;
|
|
|
|
case SSI_CMD_EXEC:
|
|
// DBGPRINTF((DBG_CONTEXT, "SSI_CMD_EXEC element, SSI_FILE %S\n", _strFilename.QueryStr()));
|
|
|
|
ssiExecType = SSI_EXEC_UNKNOWN;
|
|
|
|
if ( _pRequest->IsExecDisabled() )
|
|
{
|
|
dwID = SSINCMSG_EXEC_DISABLED;
|
|
}
|
|
else if ( pSEI->QueryTag() == SSI_TAG_CMD )
|
|
{
|
|
if ( !_pRequest->IsCmdDirectiveEnabled() )
|
|
{
|
|
dwID = SSINCMSG_CMD_NOT_ENABLED;
|
|
}
|
|
else
|
|
{
|
|
ssiExecType = SSI_EXEC_CMD;
|
|
}
|
|
}
|
|
else if ( pSEI->QueryTag() == SSI_TAG_CGI )
|
|
{
|
|
ssiExecType = SSI_EXEC_CGI;
|
|
}
|
|
else if ( pSEI->QueryTag() == SSI_TAG_ISA )
|
|
{
|
|
ssiExecType = SSI_EXEC_ISA;
|
|
}
|
|
else
|
|
{
|
|
dwID = SSINCMSG_INVALID_TAG;
|
|
}
|
|
|
|
if ( ssiExecType != SSI_EXEC_UNKNOWN )
|
|
{
|
|
BOOL fOk = FALSE;
|
|
|
|
//
|
|
// We have to send all of the currently buffered Vector data
|
|
// before we start with Child Execute
|
|
//
|
|
// If there is data to be sent then VectorSend will execute
|
|
// asynchronously and we can proceed with EXEC_URL
|
|
// only after it is completed. That's why we have to move
|
|
// _pCurrentEntry back by one
|
|
// After VectorSend completes, then the next call to VectorSend
|
|
// will not have to send anything and it completes right away
|
|
// and we can continue with EXEC_URL
|
|
//
|
|
// _pCurrentEntry must be changed before VectorSend is called
|
|
// because VectorSend could complete on different thread
|
|
// before this thread completed with the request
|
|
//
|
|
SetState( SIF_STATE_VECTOR_SEND_PENDING );
|
|
|
|
_pCurrentEntry = _pCurrentEntry->Blink;
|
|
|
|
hr = _pRequest->GetVectorBuffer().VectorSend( pfAsyncPending );
|
|
|
|
if( SUCCEEDED( hr ) && !*pfAsyncPending )
|
|
{
|
|
SetState( SIF_STATE_PROCESSING );
|
|
_pRequest->GetVectorBuffer().Reset();
|
|
//
|
|
// Restore _pCurrentEntry
|
|
//
|
|
_pCurrentEntry = _pCurrentEntry->Flink;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Wait for the completion of VectorSend()
|
|
// or bail out on error
|
|
//
|
|
return hr;
|
|
}
|
|
|
|
ZeroMemory( &_ExecUrlInfo,
|
|
sizeof( _ExecUrlInfo ) );
|
|
|
|
//
|
|
// Make asynchronous Child Execute
|
|
//
|
|
_ExecUrlInfo.dwExecUrlFlags = HSE_EXEC_URL_NO_HEADERS |
|
|
HSE_EXEC_URL_DISABLE_CUSTOM_ERROR |
|
|
HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE;
|
|
|
|
if ( ssiExecType == SSI_EXEC_CMD )
|
|
{
|
|
_ExecUrlInfo.dwExecUrlFlags |= HSE_EXEC_URL_SSI_CMD;
|
|
}
|
|
|
|
_ExecUrlInfo.pszUrl = (LPSTR) pSEI->QueryTagValue()->QueryStr();
|
|
|
|
DBG_ASSERT( _ExecUrlInfo.pszUrl != NULL );
|
|
|
|
//
|
|
// Avoid execution of empty URL
|
|
//
|
|
|
|
|
|
if ( _ExecUrlInfo.pszUrl[0] == '\0' )
|
|
{
|
|
_ultoa( ERROR_INVALID_NAME, achNumberBuffer, 10 );
|
|
apszParms[ 0 ] = _ExecUrlInfo.pszUrl;
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
switch( pSEI->QueryTag() )
|
|
{
|
|
case SSI_TAG_CMD:
|
|
dwID = SSINCMSG_CANT_EXEC_CMD;
|
|
break;
|
|
case SSI_TAG_CGI:
|
|
dwID = SSINCMSG_CANT_EXEC_CGI;
|
|
break;
|
|
case SSI_TAG_ISA:
|
|
dwID = SSINCMSG_CANT_EXEC_ISA;
|
|
break;
|
|
default:
|
|
DBG_ASSERT( FALSE );
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
SetState( SIF_STATE_EXEC_URL_PENDING );
|
|
fOk = _pRequest->GetECB()->ServerSupportFunction(
|
|
_pRequest->GetECB()->ConnID,
|
|
HSE_REQ_EXEC_URL,
|
|
&_ExecUrlInfo,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if ( !fOk )
|
|
{
|
|
SetState( SIF_STATE_PROCESSING );
|
|
_ultoa( GetLastError(), achNumberBuffer, 10 );
|
|
apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->QueryStr();
|
|
apszParms[ 1 ] = achNumberBuffer;
|
|
dwID = SSINCMSG_CANT_EXEC_CGI;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
*pfAsyncPending = TRUE;
|
|
return hr;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// DBGPRINTF((DBG_CONTEXT, "unknown element, SSI_FILE %S\n", _strFilename.QueryStr()));
|
|
|
|
dwID = SSINCMSG_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
if ( dwID )
|
|
{
|
|
hr = _pRequest->SSISendError( dwID, apszParms );
|
|
if ( FAILED (hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Before moving to next element of SSI_ELEMENT_LIST
|
|
// check if there is a need to flush currently buffered VectorSend
|
|
//
|
|
|
|
|
|
if ( _pRequest->GetVectorBuffer().QueryCurrentNumberOfElements() > SSI_DEFAULT_NUM_VECTOR_ELEMENTS )
|
|
{
|
|
SetState( SIF_STATE_VECTOR_SEND_PENDING );
|
|
|
|
hr = _pRequest->GetVectorBuffer().VectorSend( pfAsyncPending );
|
|
|
|
if( SUCCEEDED( hr ) && !*pfAsyncPending )
|
|
{
|
|
SetState( SIF_STATE_PROCESSING );
|
|
_pRequest->GetVectorBuffer().Reset();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Wait for the completion of VectorSend()
|
|
// or bail out on error
|
|
//
|
|
return hr;
|
|
}
|
|
|
|
}
|
|
//
|
|
// Move to next element of SSI_ELEMENT_LIST
|
|
//
|
|
|
|
_pCurrentEntry = _pCurrentEntry->Flink;
|
|
}
|
|
//
|
|
// End of the list has been reached
|
|
// It means that processing of the current SSI_INCLUDE_FILE has completed
|
|
//
|
|
SetState( SIF_STATE_PROCESSED );
|
|
return NO_ERROR;
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::GetFullPath(
|
|
IN SSI_ELEMENT_ITEM * pSEI,
|
|
OUT STRU * pstrPath,
|
|
IN DWORD dwPermission,
|
|
IN STRU * pstrCurrentURL,
|
|
OUT STRU * pstrURL
|
|
)
|
|
/*++
|
|
|
|
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:
|
|
|
|
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 HSE_URL_FLAGS_READ.
|
|
If 0, then no permissions are checked
|
|
pstrCurrentURL - Current .STM URL being parsed
|
|
pstrURL - Full URL filled in here (may be NULL if only pstrPath is to be retrieved)
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
--*/
|
|
{
|
|
WCHAR * pszValue;
|
|
STACK_STRU( strValue, SSI_DEFAULT_TAG_SIZE + 1 );
|
|
STACK_STRA( straValue, SSI_DEFAULT_TAG_SIZE + 1 );
|
|
STACK_STRU( strPath, SSI_MAX_PATH + 1 );
|
|
HRESULT hr = E_FAIL;
|
|
|
|
//
|
|
// We recalc the virtual root each time in case the root
|
|
// to directory mapping has changed
|
|
//
|
|
|
|
if ( FAILED( hr = strValue.CopyA( pSEI->QueryTagValue()->QueryStr() ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
pszValue = strValue.QueryStr();
|
|
|
|
if ( *pszValue == L'/' )
|
|
{
|
|
if ( FAILED( hr = strPath.Copy( pszValue ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else if ( ( int )pSEI->QueryTag() == ( int )SSI_TAG_FILE )
|
|
{
|
|
if ( FAILED( hr = strPath.Copy( pstrCurrentURL->QueryStr() ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
LPWSTR pL = strPath.QueryStr() + strPath.QueryCCH();
|
|
while ( pL > strPath.QueryStr() && pL[ -1 ] != L'/' )
|
|
{
|
|
--pL;
|
|
}
|
|
|
|
if ( pL == strPath.QueryStr() )
|
|
{
|
|
if ( FAILED( hr = strPath.Copy( L"/" ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// truncate the path off the filename
|
|
// (SetLen takes number of characters)
|
|
//
|
|
strPath.SetLen( (DWORD)DIFF( pL - strPath.QueryStr() ) );
|
|
}
|
|
if ( FAILED( hr = strPath.Append( pszValue ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( FAILED( hr = strPath.Copy( L"/" ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
if ( FAILED( hr = strPath.Append( pszValue ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert the URL into MBCS in the server's default codepage
|
|
// for normalization.
|
|
//
|
|
// CODEWORK: This is a bit extraneous, since the URL begins
|
|
// in the SSI_ELEMENT_ITEM structure as ANSI. The only reason
|
|
// that it even needs to be handled as UNICODE above is that
|
|
// pstrCurrentUrl comes in as UNICODE...
|
|
//
|
|
|
|
hr = straValue.CopyW( strPath.QueryStr() );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
|
|
if( !_pRequest->GetECB()->ServerSupportFunction(
|
|
_pRequest->GetECB()->ConnID,
|
|
HSE_REQ_NORMALIZE_URL,
|
|
straValue.QueryStr(),
|
|
NULL,
|
|
NULL
|
|
) )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
hr = strPath.CopyA( straValue.QueryStr() );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Map to a physical directory
|
|
//
|
|
|
|
if ( FAILED( hr =_pRequest->LookupVirtualRoot(
|
|
strPath.QueryStr(),
|
|
pstrPath,
|
|
dwPermission ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if( pstrURL == NULL )
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
if( FAILED( hr = pstrURL->Copy( strPath ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//static
|
|
BOOL
|
|
SSI_INCLUDE_FILE::FindInternalVariable(
|
|
IN STRA * 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,
|
|
( CHAR * )pstrVariable->QueryStr(),
|
|
SSIVarMap[ dwCounter ].cchMap ) )
|
|
{
|
|
dwCounter++;
|
|
}
|
|
if ( SSIVarMap[ dwCounter ].pszMap != NULL )
|
|
{
|
|
*pdwID = SSIVarMap[ dwCounter ].ssiMap;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pdwID = SSI_VAR_UNKNOWN;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoEchoISAPIVariable(
|
|
IN STRA * pstrVariable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get ISAPI variable and if successful, send it to HTML stream
|
|
|
|
Arguments:
|
|
|
|
pstrVariable - Variable
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DWORD dwBufLen = 0;
|
|
PCHAR pszVectorBufferSpace = NULL;
|
|
BOOL fRet;
|
|
|
|
|
|
fRet = _pRequest->GetECB()->GetServerVariable(
|
|
_pRequest->GetECB()->ConnID,
|
|
pstrVariable->QueryStr(),
|
|
NULL,
|
|
&dwBufLen );
|
|
if ( !fRet )
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
if ( dwError == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
if ( FAILED( hr = _pRequest->GetVectorBuffer().AllocateSpace(
|
|
dwBufLen,
|
|
&pszVectorBufferSpace ) ) )
|
|
{
|
|
goto failed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( dwError );
|
|
goto failed;
|
|
}
|
|
|
|
fRet = _pRequest->GetECB()->GetServerVariable(
|
|
_pRequest->GetECB()->ConnID,
|
|
pstrVariable->QueryStr(),
|
|
pszVectorBufferSpace,
|
|
&dwBufLen );
|
|
if ( !fRet )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto failed;
|
|
}
|
|
}
|
|
//
|
|
// dwBufLen includes terminating 0
|
|
//
|
|
if ( dwBufLen > 1 )
|
|
{
|
|
return _pRequest->GetVectorBuffer().AddToVector(
|
|
pszVectorBufferSpace,
|
|
dwBufLen - sizeof('\0') );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't bother sending anything for empty variable
|
|
//
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
failed:
|
|
DBG_ASSERT( FAILED( hr ) );
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoEchoDateLocal(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends local time (#ECHO VAR="DATE_LOCAL")
|
|
|
|
uses _strFileFmt - Format of time (follows strftime() convention)
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME sysTime;
|
|
|
|
::GetLocalTime( &sysTime );
|
|
return _pRequest->SendDate( &sysTime,
|
|
&_strTimeFmt );
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoEchoDateGMT(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends GMT time (#ECHO VAR="DATE_GMT")
|
|
|
|
Arguments:
|
|
|
|
pstrTimefmt - Format of time (follows strftime() convention)
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME sysTime;
|
|
STACK_STRA( straGmtTimeFmt, SSI_DEFAULT_TIME_FMT + 1 );
|
|
CHAR * pszTimeFmt = _strTimeFmt.QueryStr();
|
|
HRESULT hr = E_FAIL;
|
|
CHAR szGMT[] = "GMT";
|
|
|
|
|
|
//
|
|
// for GMT time the time zone as displayes by strftime() will be incorrect
|
|
// lets replace all the occurences with time zone string (%z,%Z, %#z, %#Z)
|
|
// with hardcoded GMT value
|
|
//
|
|
|
|
while ( *pszTimeFmt != '\0' )
|
|
{
|
|
if ( *pszTimeFmt == '%' )
|
|
{
|
|
if ( *( pszTimeFmt + 1 ) == 'z' || *( pszTimeFmt + 1 ) == 'Z' )
|
|
{
|
|
pszTimeFmt += 2;
|
|
//
|
|
// replace %z or %Z with GMT
|
|
//
|
|
if ( FAILED( hr = straGmtTimeFmt.Append( szGMT ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
continue;
|
|
}
|
|
else if ( *( pszTimeFmt + 1 ) == '#' )
|
|
{
|
|
if ( *( pszTimeFmt + 2 ) == 'z' || *( pszTimeFmt + 2 ) == 'Z' )
|
|
{
|
|
pszTimeFmt +=3 ;
|
|
//
|
|
// replace %z or %Z with GMT
|
|
//
|
|
if ( FAILED( hr = straGmtTimeFmt.Append( szGMT ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if ( FAILED( hr = straGmtTimeFmt.Append( pszTimeFmt++, 1 ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
|
|
::GetSystemTime( &sysTime );
|
|
return _pRequest->SendDate( &sysTime,
|
|
&straGmtTimeFmt );
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoEchoDocumentName(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends filename of current SSI document (#ECHO VAR="DOCUMENT_NAME")
|
|
|
|
Arguments:
|
|
|
|
pstrFilename - filename to print
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
return _pRequest->GetVectorBuffer().CopyToVector( _strFilename );
|
|
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoEchoDocumentURI(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends URL of current SSI document (#ECHO VAR="DOCUMENT_URI")
|
|
|
|
Arguments:
|
|
|
|
pstrURL - URL to print
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
--*/
|
|
{
|
|
return _pRequest->GetVectorBuffer().CopyToVector( _strURL );
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoEchoQueryStringUnescaped(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends unescaped querystring to HTML stream (#ECHO VAR="QUERY_STRING_UNESCAPED")
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
STACK_STRA( straQueryString, SSI_DEFAULT_URL_SIZE + 1 );
|
|
|
|
if ( FAILED( hr = straQueryString.Copy( _pRequest->GetECB()->lpszQueryString ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if ( FAILED( hr = straQueryString.Unescape()) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return _pRequest->GetVectorBuffer().CopyToVector( straQueryString );
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoEchoLastModified(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
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:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
return DoFLastMod( &_strFilename );
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoFSize(
|
|
IN STRU * pstrFilename
|
|
)
|
|
/*++
|
|
|
|
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:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
DWORD cbFileSize;
|
|
CHAR achInputNumber[ SSI_MAX_NUMBER_STRING + 1 ];
|
|
NUMBERFMTA nfNumberFormat;
|
|
int iOutputSize;
|
|
HRESULT hr = E_FAIL;
|
|
CHAR * pszVectorBufferSpace = NULL;
|
|
|
|
if ( wcscmp( pstrFilename->QueryStr(),
|
|
_strFilename.QueryStr() ) )
|
|
{
|
|
//
|
|
// FSize requested for the file different from this on
|
|
//
|
|
|
|
SSI_FILE *pSsiFile = NULL;
|
|
hr = SSI_FILE::GetReferencedInstance(
|
|
*pstrFilename,
|
|
_pRequest->GetUserToken(),
|
|
&pSsiFile );
|
|
if (FAILED( hr ))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
|
|
hr = pSsiFile->SSIGetFileSize( &cbFileSize );
|
|
|
|
pSsiFile->DereferenceSsiFile();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// FSize requested for the current file
|
|
//
|
|
if ( FAILED( hr = _pSsiFile->SSIGetFileSize( &cbFileSize ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if ( !_fSizeFmtBytes )
|
|
{
|
|
//
|
|
// express in terms of KB
|
|
// most applications round up the size in KB (eg. 1B is shown as 1KB)
|
|
// Let's do the same
|
|
//
|
|
cbFileSize = cbFileSize / 1024 + (( cbFileSize % 1024 != 0 )?1:0);
|
|
}
|
|
|
|
nfNumberFormat.NumDigits = 0;
|
|
nfNumberFormat.LeadingZero = 0;
|
|
nfNumberFormat.Grouping = 3;
|
|
nfNumberFormat.lpThousandSep = ",";
|
|
nfNumberFormat.lpDecimalSep = ".";
|
|
nfNumberFormat.NegativeOrder = 2;
|
|
|
|
_snprintf( achInputNumber,
|
|
SSI_MAX_NUMBER_STRING,
|
|
"%ld",
|
|
cbFileSize );
|
|
achInputNumber[ SSI_MAX_NUMBER_STRING ] = '\0';
|
|
|
|
if ( FAILED( hr = _pRequest->GetVectorBuffer().AllocateSpace(
|
|
SSI_MAX_NUMBER_STRING + 1,
|
|
&pszVectorBufferSpace ) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
iOutputSize = GetNumberFormatA( LOCALE_SYSTEM_DEFAULT,
|
|
0,
|
|
achInputNumber,
|
|
&nfNumberFormat,
|
|
pszVectorBufferSpace,
|
|
SSI_MAX_NUMBER_STRING + 1 );
|
|
if ( !iOutputSize )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
//
|
|
// Do not count trailing '\0'
|
|
//
|
|
|
|
iOutputSize--;
|
|
|
|
return _pRequest->GetVectorBuffer().AddToVector(
|
|
pszVectorBufferSpace,
|
|
iOutputSize );
|
|
}
|
|
|
|
HRESULT
|
|
SSI_INCLUDE_FILE::DoFLastMod(
|
|
IN STRU * pstrFilename
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send the LastModTime of file to HTML stream
|
|
|
|
Arguments:
|
|
|
|
pstrFilename - Filename
|
|
pstrTimeFmt - Format of time -> follows strftime() convention
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
FILETIME ftTime;
|
|
FILETIME ftLocalTime;
|
|
SYSTEMTIME sysLocal;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if ( wcscmp( pstrFilename->QueryStr(),
|
|
_strFilename.QueryStr() ) )
|
|
{
|
|
//
|
|
// FLastMod requested for file different from the current one
|
|
//
|
|
|
|
|
|
SSI_FILE *pSsiFile = NULL;
|
|
hr = SSI_FILE::GetReferencedInstance(
|
|
*pstrFilename,
|
|
_pRequest->GetUserToken(),
|
|
&pSsiFile );
|
|
if (FAILED( hr ))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = pSsiFile->SSIGetLastModTime( &ftTime );
|
|
|
|
pSsiFile->DereferenceSsiFile();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// FLastMod requested for the current file
|
|
//
|
|
hr = _pSsiFile->SSIGetLastModTime( &ftTime );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if ( ( !FileTimeToLocalFileTime( &ftTime, &ftLocalTime ) ) ||
|
|
( !FileTimeToSystemTime( &ftLocalTime, &sysLocal ) ) )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
return _pRequest->SendDate( &sysLocal,
|
|
&_strTimeFmt );
|
|
}
|
|
|
|
|
|
|
|
|