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.
651 lines
16 KiB
651 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 2000 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.
|
|
|
|
Implementation of the ssinc ISAPI entrypoints.
|
|
implementation of the custom error handling
|
|
|
|
Author:
|
|
|
|
Ming Lu (MingLu) 10-Apr-2000
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
//
|
|
// Prototypes
|
|
//
|
|
|
|
extern "C" {
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain(
|
|
HINSTANCE hDll,
|
|
DWORD dwReason,
|
|
LPVOID lpvReserved
|
|
);
|
|
}
|
|
|
|
//
|
|
// Global Data
|
|
//
|
|
|
|
DECLARE_DEBUG_PRINTS_OBJECT();
|
|
DECLARE_DEBUG_VARIABLE();
|
|
|
|
|
|
//
|
|
// utility functions
|
|
//
|
|
|
|
DWORD
|
|
SsiFormatMessageA(
|
|
IN DWORD dwMessageId,
|
|
IN LPSTR apszParms[],
|
|
OUT LPSTR * ppchErrorBuff
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
FormatMessage wrapper
|
|
|
|
Arguments:
|
|
|
|
dwMessageId - message Id to format
|
|
apszParms[] - parameters for message
|
|
ppchErrorBuff - formatted message text (caller must free it calling LocalFree)
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
DWORD cch;
|
|
|
|
|
|
//
|
|
// FormatMessage uses SEH to determine when to resize and
|
|
// this will cause debuggers to break on the 1st change exception
|
|
// All the parameters must be limited in max output size using format string
|
|
// in the message eg %1!.30s!would limit output of the parameter to max size
|
|
// of 30 characters
|
|
|
|
cch = ::FormatMessageA( FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_HMODULE,
|
|
GetModuleHandle( IIS_RESOURCE_DLL_NAME ),
|
|
dwMessageId,
|
|
0,
|
|
( LPSTR ) ppchErrorBuff, //see FormatMessage doc
|
|
0,
|
|
( va_list *) apszParms );
|
|
|
|
return cch;
|
|
}
|
|
|
|
//
|
|
// class that handles asynchronous sends of custom errors from
|
|
// the master level
|
|
//
|
|
|
|
class SSI_CUSTOM_ERROR
|
|
{
|
|
public:
|
|
SSI_CUSTOM_ERROR();
|
|
|
|
HRESULT
|
|
SSI_CUSTOM_ERROR::SendCustomError(
|
|
EXTENSION_CONTROL_BLOCK * pecb,
|
|
HRESULT hrErrorToReport
|
|
);
|
|
|
|
private:
|
|
~SSI_CUSTOM_ERROR();
|
|
|
|
VOID
|
|
SSI_CUSTOM_ERROR::SetVectorResponseData(
|
|
IN PCHAR pszStatus,
|
|
IN PCHAR pszHeaders,
|
|
IN PCHAR pszResponse
|
|
);
|
|
|
|
static
|
|
VOID WINAPI
|
|
HseCustomErrorCompletion(
|
|
IN EXTENSION_CONTROL_BLOCK * pECB,
|
|
IN PVOID pContext,
|
|
IN DWORD cbIO,
|
|
IN DWORD dwError
|
|
);
|
|
|
|
HSE_CUSTOM_ERROR_INFO _CustomErrorInfo;
|
|
HSE_RESPONSE_VECTOR _ResponseVector;
|
|
HSE_VECTOR_ELEMENT _VectorElement;
|
|
PCHAR _pszFormattedMessage;
|
|
BOOL _fHeadRequest;
|
|
};
|
|
|
|
SSI_CUSTOM_ERROR::SSI_CUSTOM_ERROR()
|
|
{
|
|
ZeroMemory( &_ResponseVector, sizeof( _ResponseVector ) );
|
|
ZeroMemory( &_VectorElement, sizeof( _VectorElement ) );
|
|
_ResponseVector.lpElementArray = &_VectorElement;
|
|
_ResponseVector.dwFlags = HSE_IO_ASYNC |
|
|
HSE_IO_SEND_HEADERS |
|
|
HSE_IO_DISCONNECT_AFTER_SEND;
|
|
_ResponseVector.pszStatus = "";
|
|
_ResponseVector.pszHeaders = "";
|
|
_ResponseVector.nElementCount = 0;
|
|
|
|
_pszFormattedMessage = NULL;
|
|
_fHeadRequest = FALSE;
|
|
}
|
|
|
|
SSI_CUSTOM_ERROR::~SSI_CUSTOM_ERROR()
|
|
{
|
|
if ( _pszFormattedMessage != NULL )
|
|
{
|
|
LocalFree( _pszFormattedMessage );
|
|
_pszFormattedMessage = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SSI_CUSTOM_ERROR::SetVectorResponseData(
|
|
IN PCHAR pszStatus,
|
|
IN PCHAR pszHeaders,
|
|
IN PCHAR pszResponse )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
if custom error couldn't be used then Vector send is used
|
|
This function is used internally to setup VectorSend structures
|
|
|
|
Arguments:
|
|
|
|
pszStatus -
|
|
pszHeaders -
|
|
pszResponse - response body
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
|
|
{
|
|
_ResponseVector.pszStatus = pszStatus;
|
|
_ResponseVector.pszHeaders = pszHeaders;
|
|
if ( !_fHeadRequest )
|
|
{
|
|
_VectorElement.ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
|
|
_VectorElement.pvContext = pszResponse;
|
|
_VectorElement.cbSize = strlen( pszResponse );
|
|
_ResponseVector.nElementCount = 1;
|
|
}
|
|
else
|
|
{
|
|
_ResponseVector.nElementCount = 0;
|
|
}
|
|
}
|
|
|
|
//static
|
|
VOID WINAPI
|
|
SSI_CUSTOM_ERROR::HseCustomErrorCompletion(
|
|
IN EXTENSION_CONTROL_BLOCK * pECB,
|
|
IN PVOID pContext,
|
|
IN DWORD /*cbIO*/,
|
|
IN DWORD /*dwError*/
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
completion routine (for async custom error send or vector send
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DBG_ASSERT( pContext != NULL );
|
|
|
|
SSI_CUSTOM_ERROR * pSsiCustomError =
|
|
reinterpret_cast<SSI_CUSTOM_ERROR *> (pContext);
|
|
delete pSsiCustomError;
|
|
pSsiCustomError = NULL;
|
|
|
|
//
|
|
// Notify IIS that we are done with processing
|
|
//
|
|
|
|
pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SSI_CUSTOM_ERROR::SendCustomError(
|
|
EXTENSION_CONTROL_BLOCK * pecb,
|
|
HRESULT hrErrorToReport
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
reports error hrErrorToReport either as custom error if possible or
|
|
builds error response that gets send asynchronously using VectorSend
|
|
|
|
Arguments:
|
|
pecb
|
|
hrErrorToReport - error to be reported
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if ( !pecb->ServerSupportFunction( pecb->ConnID,
|
|
HSE_REQ_IO_COMPLETION,
|
|
SSI_CUSTOM_ERROR::HseCustomErrorCompletion,
|
|
0,
|
|
(LPDWORD ) this ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto failed;
|
|
}
|
|
|
|
if ( _stricmp( pecb->lpszMethod, "HEAD" ) == 0 )
|
|
{
|
|
_fHeadRequest = TRUE;
|
|
}
|
|
|
|
DWORD dwError = WIN32_FROM_HRESULT( hrErrorToReport );
|
|
|
|
_CustomErrorInfo.pszStatus = NULL;
|
|
|
|
switch( dwError )
|
|
{
|
|
case ERROR_ACCESS_DENIED:
|
|
|
|
_CustomErrorInfo.pszStatus = "401 Access Denied";
|
|
_CustomErrorInfo.uHttpSubError = MD_ERROR_SUB401_LOGON_ACL;
|
|
_CustomErrorInfo.fAsync = TRUE;
|
|
|
|
break;
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
case ERROR_INVALID_NAME:
|
|
|
|
_CustomErrorInfo.pszStatus = "404 Object Not Found";
|
|
_CustomErrorInfo.uHttpSubError = 0;
|
|
_CustomErrorInfo.fAsync = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
if ( _CustomErrorInfo.pszStatus != NULL )
|
|
{
|
|
BOOL fRet = pecb->ServerSupportFunction( pecb->ConnID,
|
|
HSE_REQ_SEND_CUSTOM_ERROR,
|
|
&_CustomErrorInfo,
|
|
NULL,
|
|
NULL );
|
|
if ( !fRet )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sending custom error failed, send our own error
|
|
//
|
|
|
|
if ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
|
|
{
|
|
|
|
switch( dwError )
|
|
{
|
|
case ERROR_ACCESS_DENIED:
|
|
SetVectorResponseData(
|
|
SSI_ACCESS_DENIED,
|
|
SSI_HEADER,
|
|
"<body><h1>"
|
|
SSI_ACCESS_DENIED
|
|
"</h1></body>" );
|
|
break;
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
case ERROR_INVALID_NAME:
|
|
SetVectorResponseData(
|
|
SSI_OBJECT_NOT_FOUND,
|
|
SSI_HEADER,
|
|
"<body><h1>"
|
|
SSI_OBJECT_NOT_FOUND
|
|
"</h1></body>" );
|
|
break;
|
|
default:
|
|
{
|
|
STACK_BUFFER( buffTemp, ( SSI_DEFAULT_URL_SIZE + 1 ) * sizeof(WCHAR) );
|
|
STACK_STRA( strURL, SSI_DEFAULT_URL_SIZE );
|
|
DWORD cbSize = buffTemp.QuerySize();
|
|
CHAR pszNumBuf[ SSI_MAX_NUMBER_STRING ];
|
|
LPSTR apszParms[ 2 ] = { NULL, NULL };
|
|
|
|
//
|
|
// get the URL
|
|
//
|
|
if ( !pecb->GetServerVariable( pecb->ConnID,
|
|
"UNICODE_URL",
|
|
buffTemp.QueryPtr(),
|
|
&cbSize ) )
|
|
{
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
|
|
!buffTemp.Resize(cbSize))
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto failed;
|
|
}
|
|
|
|
//
|
|
// Now, we should have enough buffer, try again
|
|
//
|
|
if ( !pecb->GetServerVariable( pecb->ConnID,
|
|
"UNICODE_URL",
|
|
buffTemp.QueryPtr(),
|
|
&cbSize ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto failed;
|
|
}
|
|
}
|
|
if (FAILED( hr = strURL.CopyW((LPWSTR)buffTemp.QueryPtr())))
|
|
{
|
|
break;
|
|
}
|
|
|
|
_ultoa( dwError, pszNumBuf, 10 );
|
|
|
|
apszParms[ 0 ] = strURL.QueryStr();
|
|
apszParms[ 1 ] = pszNumBuf;
|
|
|
|
DWORD cch = SsiFormatMessageA (
|
|
SSINCMSG_REQUEST_ERROR,
|
|
apszParms,
|
|
&_pszFormattedMessage
|
|
);
|
|
|
|
if ( cch == 0 )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
goto failed;
|
|
}
|
|
|
|
SetVectorResponseData( "", // means 200 OK
|
|
SSI_HEADER,
|
|
_pszFormattedMessage );
|
|
} //end of default block
|
|
}
|
|
//
|
|
// perform the Vector send
|
|
//
|
|
if ( !pecb->ServerSupportFunction(
|
|
pecb->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&_ResponseVector,
|
|
NULL,
|
|
NULL) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto failed;
|
|
}
|
|
|
|
}
|
|
else if ( FAILED( hr ) )
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
failed:
|
|
DBG_ASSERT( FAILED( hr ) );
|
|
return hr;
|
|
}
|
|
//
|
|
// ISAPI DLL Required Entry Points
|
|
//
|
|
|
|
DWORD
|
|
WINAPI
|
|
HttpExtensionProc(
|
|
EXTENSION_CONTROL_BLOCK * pecb
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HRESULT hrToReport = E_FAIL;
|
|
BOOL fAsyncPending = FALSE;
|
|
SSI_REQUEST * pssiReq = NULL;
|
|
|
|
hrToReport = SSI_REQUEST::CreateInstance(
|
|
pecb,
|
|
&pssiReq );
|
|
|
|
if( FAILED( hrToReport ) )
|
|
{
|
|
SSI_CUSTOM_ERROR * pSsiCustomError = new SSI_CUSTOM_ERROR;
|
|
if ( pSsiCustomError == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
|
|
goto failed;
|
|
}
|
|
//
|
|
// in the case of success asynchronous custom error
|
|
// send will be executed
|
|
//
|
|
hr = pSsiCustomError->SendCustomError(
|
|
pecb,
|
|
hrToReport );
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
goto failed;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No Cleanup, there is pending IO request.
|
|
// completion routine is responsible perform proper cleanup
|
|
//
|
|
return HSE_STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT( pssiReq != NULL );
|
|
|
|
//
|
|
// Register completion callback since we will execute asynchronously
|
|
//
|
|
|
|
if ( !pecb->ServerSupportFunction( pecb->ConnID,
|
|
HSE_REQ_IO_COMPLETION,
|
|
SSI_REQUEST::HseIoCompletion,
|
|
0,
|
|
(LPDWORD ) pssiReq )
|
|
)
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
//
|
|
// Continue processing SSI file
|
|
//
|
|
|
|
hr = pssiReq->DoWork( 0,
|
|
&fAsyncPending );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
if( fAsyncPending )
|
|
{
|
|
//
|
|
// No Cleanup, there is pending IO request.
|
|
// completion routine is responsible perform proper cleanup
|
|
//
|
|
return HSE_STATUS_PENDING;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This request is completed. Do Cleanup before returning
|
|
//
|
|
delete pssiReq;
|
|
pssiReq = NULL;
|
|
return HSE_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
failed:
|
|
{
|
|
LPCSTR apsz[ 1 ];
|
|
DWORD cch;
|
|
LPSTR pchBuff;
|
|
|
|
apsz[ 0 ] = pecb->lpszPathInfo;
|
|
|
|
cch = SsiFormatMessageA( SSINCMSG_LOG_ERROR,
|
|
( va_list *) apsz,
|
|
&pchBuff );
|
|
|
|
if( cch > 0 )
|
|
{
|
|
strncpy( pecb->lpszLogData,
|
|
pchBuff,
|
|
HSE_LOG_BUFFER_LEN );
|
|
pecb->lpszLogData[ HSE_LOG_BUFFER_LEN - 1 ] = 0;
|
|
::LocalFree( ( VOID * )pchBuff );
|
|
}
|
|
|
|
//
|
|
// This request is completed. Do Cleanup before returning
|
|
//
|
|
if ( pssiReq != NULL )
|
|
{
|
|
delete pssiReq;
|
|
pssiReq = NULL;
|
|
}
|
|
return HSE_STATUS_ERROR;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
GetExtensionVersion(
|
|
HSE_VERSION_INFO * pver
|
|
)
|
|
{
|
|
pver->dwExtensionVersion = MAKELONG( 0, 1 );
|
|
strncpy( pver->lpszExtensionDesc,
|
|
"Server Side Include Extension DLL",
|
|
HSE_MAX_EXT_DLL_NAME_LEN );
|
|
pver->lpszExtensionDesc[ HSE_MAX_EXT_DLL_NAME_LEN - 1 ] = '\0';
|
|
|
|
if ( FAILED( SSI_REQUEST::InitializeGlobals() ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ( FAILED( SSI_INCLUDE_FILE::InitializeGlobals() ) )
|
|
{
|
|
SSI_REQUEST::TerminateGlobals();
|
|
return FALSE;
|
|
}
|
|
|
|
if ( FAILED( SSI_FILE::InitializeGlobals() ) )
|
|
{
|
|
SSI_INCLUDE_FILE::TerminateGlobals();
|
|
SSI_REQUEST::TerminateGlobals();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
TerminateExtension(
|
|
DWORD /* dwFlags */
|
|
)
|
|
{
|
|
SSI_FILE::TerminateGlobals();
|
|
SSI_INCLUDE_FILE::TerminateGlobals();
|
|
SSI_REQUEST::TerminateGlobals();
|
|
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain(
|
|
HINSTANCE hDll,
|
|
DWORD dwReason,
|
|
LPVOID /*lpvReserved*/
|
|
)
|
|
{
|
|
switch ( dwReason )
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
CREATE_DEBUG_PRINT_OBJECT( "ssinc" );
|
|
|
|
DisableThreadLibraryCalls( hDll );
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
DELETE_DEBUG_PRINT_OBJECT();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|