|
|
/*++
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 ); }
|