Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

507 lines
12 KiB

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// CUSTERR.CPP
//
// Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
//
#include "_davprs.h"
#include "custerr.h"
#include "content.h"
// ========================================================================
//
// CLASS IError
//
// Interface class for error response handler classes. An error response
// handler class implements one virtual method, DoResponse(), which
// handles the error response.
//
class IError : public CMTRefCounted
{
// NOT IMPLEMENTED
//
IError& operator=(const IError&);
IError( const IError& );
protected:
IError() {}
public:
// CREATORS
//
virtual ~IError() = 0;
// ACCESSORS
//
virtual void DoResponse( IResponse& response , const IEcb& ecb ) const = 0;
};
// ------------------------------------------------------------------------
//
// IError::~IError()
//
// Out of line virtual destructor necessary for proper deletion
// of objects of derived classes via this class
//
IError::~IError() {}
// ------------------------------------------------------------------------
//
// BOOL AddResponseBodyFromFile( IResponse& response, LPCSTR lpszFilePath )
//
// Utility function to add the file's contents to the response's body
//
static BOOL AddResponseBodyFromFile( IResponse& response, LPCWSTR pwszFilePath )
{
BOOL fReturn = FALSE;
auto_ref_handle hf;
//
// Add the file to the response body
//
if ( hf.FCreate(
CreateFileW( pwszFilePath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_SEQUENTIAL_SCAN |
FILE_FLAG_OVERLAPPED,
NULL )) )
{
response.AddBodyFile(hf);
// Set the response content type to an appropriate value based
// on the file's extension.
//
UINT cchContentType = 60;
CStackBuffer<WCHAR> pwszContentType(cchContentType * sizeof(WCHAR));
if (!pwszContentType.get())
return FALSE;
if ( !FGetContentTypeFromPath( *response.GetEcb(),
pwszFilePath,
pwszContentType.get(),
&cchContentType))
{
if (!pwszContentType.resize(cchContentType * sizeof(WCHAR)))
return FALSE;
if ( !FGetContentTypeFromPath( *response.GetEcb(),
pwszFilePath,
pwszContentType.get(),
&cchContentType))
{
//
// If we can't get a reasonable value from the mime map
// then use a reasonable default: application/octet-stream
//
Assert (pwszContentType.celems() >
CchConstString(gc_wszAppl_Octet_Stream));
wcscpy (pwszContentType.get(), gc_wszAppl_Octet_Stream);
}
}
response.SetHeader( gc_szContent_Type, pwszContentType.get() );
fReturn = TRUE;
}
return fReturn;
}
// ========================================================================
//
// CLASS CURLError
//
// URL error response handler class. Handles an error response by
// forwarding to another URL.
//
class CURLError : public IError
{
//
// The URL
//
LPCWSTR m_pwszURL;
// NOT IMPLEMENTED
//
CURLError& operator=(const CURLError&);
CURLError(const CURLError&);
public:
// CREATORS
//
CURLError( LPCWSTR pwszURL ) : m_pwszURL(pwszURL) {}
// ACCESSORS
//
void DoResponse( IResponse& response, const IEcb& ecb ) const;
};
// ------------------------------------------------------------------------
//
// CURLError::DoResponse()
//
// Handle the error response by forwarding to the configured URL.
//
void
CURLError::DoResponse( IResponse& response, const IEcb& ecb ) const
{
SCODE sc = S_OK;
// The first boolean flag is for keeping the query string
// and the second flag indicates that we are doing CustomError
// processing.
//
sc = response.ScForward( m_pwszURL, TRUE , TRUE );
if (FAILED(sc))
{
// The child execute failed - one reason is that the URL is a simple
// file URL. Try mapping the URL to file..
//
if ( HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) == sc )
{
HSE_UNICODE_URL_MAPEX_INFO mi;
// Obtain the file path and send the file in the body
//
sc = ecb.ScReqMapUrlToPathEx(m_pwszURL, &mi);
if (FAILED(sc))
{
// We ran into a case where the CE URL resource itself is not
// found. When we are ready to send response, this will mean
// an empty body. Appropriate body will be default generated there.
//
DebugTrace("CURLError::DoResponse() - IEcb::ScSSFReqMapUrlToPathEx() failed 0x%08lX\n", sc);
}
else
{
AddResponseBodyFromFile( response, mi.lpszPath );
}
}
}
return;
}
// ========================================================================
//
// CLASS CFileError
//
// File error response handler class. Handles an error response by
// adding a file containing response body content to the response body.
//
class CFileError : public IError
{
//
// The filename
//
LPCWSTR m_pwszFileName;
// NOT IMPLEMENTED
//
CFileError& operator=(const CFileError&);
CFileError(const CFileError&);
public:
// CREATORS
//
CFileError( LPCWSTR pwszFileName ) : m_pwszFileName(pwszFileName) {}
// ACCESSORS
//
void DoResponse( IResponse& response, const IEcb& ) const;
};
// ------------------------------------------------------------------------
//
// CFileError::DoResponse()
//
// Handle the error response by setting the response body content
// to the contents of the configured file.
//
void
CFileError::DoResponse( IResponse& response, const IEcb& ) const
{
AddResponseBodyFromFile( response, m_pwszFileName );
}
// ========================================================================
// class CEKey
// Key class for custom error keys that can be compared with ==.
//
#pragma warning(disable:4201) // Nameless struct/union
class CEKey
{
private:
union
{
DWORD m_dw;
struct
{
USHORT m_iStatusCode;
USHORT m_iSubError;
};
};
public:
CEKey (USHORT iStatusCode,
USHORT iSubError) :
m_iStatusCode(iStatusCode),
m_iSubError(iSubError)
{
}
DWORD Dw() const
{
return m_dw;
}
int CEKey::hash (const int rhs) const
{
return (m_dw % rhs);
}
bool CEKey::isequal (const CEKey& rhs) const
{
return (rhs.m_dw == m_dw);
}
};
#pragma warning(default:4201) // Nameless struct/union
// ========================================================================
//
// CLASS CCustomErrorMap
//
// Custom error list class. Each instance of this class encapsulates a
// set of custom error mappings. Each mapping maps from a status code
// and "suberror" (as defined by IIS) to an error response handling object.
//
// The list is configured, via FInit(), from a set of null-terminated
// string of the following form:
//
// "<error>,<suberror|*>,<"FILE"|"URL">,<filename|URL>"
//
// For example, the string "404,*,FILE,C:\WINNT\help\common\404b.htm" would
// translate to a mapping from "404,*" to a CFileError(C:\WINNT\htlp\common\404b.htm)
// object.
//
class CCustomErrorMap : public ICustomErrorMap
{
//
// A cache of status-code-plus-sub-error strings to
// error object mappings
//
CCache<CEKey, auto_ref_ptr<IError> > m_cache;
// NOT IMPLEMENTED
//
CCustomErrorMap& operator=(const CCustomErrorMap&);
CCustomErrorMap(const CCustomErrorMap&);
public:
// CREATORS
//
CCustomErrorMap()
{
// If this fails, our allocators will throw for us.
(void)m_cache.FInit();
//
//$COM refcounting
//
m_cRef = 1;
}
// MANIPULATORS
//
BOOL FInit( LPWSTR pwszCustomErrorMappings );
// ACCESSORS
//
BOOL FDoResponse( IResponse& response, const IEcb& ecb ) const;
};
// ------------------------------------------------------------------------
//
// CCustomErrorMap::FInit()
//
// Initialize a custom error map from a sequence of comma-delimited mapping
// strings.
//
// Disable warnings about conversion from INT to USHORT losing data for
// this function only. The conversion is for the status code and suberror
// which we Assert() are in the range of a USHORT.
//
BOOL
CCustomErrorMap::FInit( LPWSTR pwszCustomErrorMappings )
{
Assert( pwszCustomErrorMappings != NULL );
//
// Parse through the error list and build up the cache.
// (Code mostly copied from IIS' W3_METADATA::BuildCustomErrorTable())
//
// Each mapping is a string of the form:
//
// "<error>,<suberror|*>,<"FILE"|"URL">,<filename|URL>"
//
// Note that if any of the mappings is invalid we fail the whole call.
// This is consistent with IIS' behavior.
//
for ( LPWSTR pwszMapping = pwszCustomErrorMappings; *pwszMapping; )
{
enum {
ISZ_CE_STATCODE = 0,
ISZ_CE_SUBERROR,
ISZ_CE_TYPE,
ISZ_CE_PATH,
ISZ_CE_URL = ISZ_CE_PATH, // alias
CSZ_CE_FIELDS
};
LPWSTR rgpwsz[CSZ_CE_FIELDS];
INT iStatusCode;
INT iSubError = 0;
auto_ref_ptr<IError> pError;
UINT cchMapping;
Assert( !IsBadWritePtr(pwszMapping, wcslen(pwszMapping) * sizeof(WCHAR)) );
//
// Digest the metadata
//
if ( !FParseMDData( pwszMapping,
rgpwsz,
CSZ_CE_FIELDS,
&cchMapping ) )
return FALSE;
//
// Verify that the first field is a valid status code
//
iStatusCode = _wtoi(rgpwsz[ISZ_CE_STATCODE]);
if ( iStatusCode < 400 || iStatusCode > 599 )
return FALSE;
//
// Verify that the second field is a valid suberror. A valid
// suberror is either a "*" or an integer. Note: IIS'
// BuildCustomErrorTable() only checks whether the first
// character is a '*' so we do the same here.
//
if ( *rgpwsz[ISZ_CE_SUBERROR] != L'*' )
{
iSubError = _wtoi(rgpwsz[ISZ_CE_SUBERROR]);
if ( iSubError < 0 || iSubError > _UI16_MAX )
return FALSE;
}
//
// Verify that the third field is a valid type and
// create the appropriate (file or URL) error object.
//
if ( !_wcsicmp(rgpwsz[ISZ_CE_TYPE], L"FILE") )
{
pError = new CFileError(rgpwsz[ISZ_CE_PATH]);
}
else if ( !_wcsicmp(rgpwsz[ISZ_CE_TYPE], L"URL") )
{
pError = new CURLError(rgpwsz[ISZ_CE_URL]);
}
else
{
return FALSE;
}
//
// Add the error object to the cache, keyed by the error/suberror.
//
(void)m_cache.FSet( CEKey(static_cast<USHORT>(iStatusCode),
static_cast<USHORT>(iSubError)),
pError );
//
// Get the next mapping
//
pwszMapping += cchMapping;
}
return TRUE;
}
// ------------------------------------------------------------------------
//
// CCustomErrorMap::FDoResponse()
//
// Look for a custom error response mapping for the particular response
// error status and, if one exists, apply it to the response.
//
// Returns TRUE if an error mapping exists, FALSE if not.
//
BOOL
CCustomErrorMap::FDoResponse( IResponse& response, const IEcb& ecb ) const
{
auto_ref_ptr<IError> pError;
Assert( response.DwStatusCode() <= _UI16_MAX );
Assert( response.DwSubError() <= _UI16_MAX );
//
// Lookup the error/suberror pair in the cache
//
if ( m_cache.FFetch( CEKey(static_cast<USHORT>(response.DwStatusCode()),
static_cast<USHORT>(response.DwSubError())),
&pError ) )
{
pError->DoResponse( response, ecb );
return TRUE;
}
return FALSE;
}
// ========================================================================
//
// FREE FUNCTIONS
//
// ------------------------------------------------------------------------
//
// FSetCustomErrorResponse()
//
BOOL
FSetCustomErrorResponse( const IEcb& ecb,
IResponse& response )
{
const ICustomErrorMap * pCustomErrorMap;
pCustomErrorMap = ecb.MetaData().GetCustomErrorMap();
return pCustomErrorMap && pCustomErrorMap->FDoResponse(response, ecb);
}
// ------------------------------------------------------------------------
//
// NewCustomErrorMap()
//
ICustomErrorMap *
NewCustomErrorMap( LPWSTR pwszCustomErrorMappings )
{
auto_ref_ptr<CCustomErrorMap> pCustomErrorMap;
pCustomErrorMap.take_ownership(new CCustomErrorMap());
if ( pCustomErrorMap->FInit(pwszCustomErrorMappings) )
return pCustomErrorMap.relinquish();
return NULL;
}