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.
1156 lines
30 KiB
1156 lines
30 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name :
|
|
urlinfo.cxx
|
|
|
|
Abstract:
|
|
Gets metadata for URL
|
|
|
|
Author:
|
|
Bilal Alam (balam) 8-Jan-2000
|
|
|
|
Environment:
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
ULW3.DLL
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#include <stringau.hxx>
|
|
|
|
//
|
|
// Utility to guard against ~ inconsistency
|
|
//
|
|
|
|
DWORD
|
|
CheckIfShortFileName(
|
|
IN WCHAR * pszPath,
|
|
IN HANDLE hImpersonation,
|
|
OUT BOOL * pfShort
|
|
);
|
|
|
|
W3_STATE_URLINFO::W3_STATE_URLINFO()
|
|
{
|
|
_hr = URL_CONTEXT::Initialize();
|
|
}
|
|
|
|
W3_STATE_URLINFO::~W3_STATE_URLINFO()
|
|
{
|
|
URL_CONTEXT::Terminate();
|
|
}
|
|
|
|
CONTEXT_STATUS
|
|
W3_STATE_URLINFO::OnCompletion(
|
|
W3_MAIN_CONTEXT * pMainContext,
|
|
DWORD cbCompletion,
|
|
DWORD dwCompletionStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle URLINFO completions. CheckAccess() is called in DoWork() and this
|
|
call is asynchronous.
|
|
|
|
Arguments:
|
|
|
|
pMainContext - W3_MAIN_CONTEXT representing execution of state machine
|
|
cbCompletion - Number of bytes in an async completion
|
|
dwCompletionStatus - Error status of a completion
|
|
|
|
Return Value:
|
|
|
|
CONTEXT_STATUS_CONTINUE - if we should continue in state machine
|
|
else stop executing the machine and free up the current thread
|
|
|
|
--*/
|
|
{
|
|
CONTEXT_STATUS contextStatus;
|
|
BOOL fAccessAllowed;
|
|
|
|
contextStatus = pMainContext->CheckAccess( TRUE, // this is a completion
|
|
cbCompletion,
|
|
dwCompletionStatus,
|
|
&fAccessAllowed );
|
|
|
|
if ( contextStatus == CONTEXT_STATUS_PENDING )
|
|
{
|
|
return CONTEXT_STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// If access is not allowed, then just finish state machine (
|
|
// response has already been sent)
|
|
//
|
|
|
|
if ( !fAccessAllowed )
|
|
{
|
|
pMainContext->SetFinishedResponse();
|
|
}
|
|
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
CONTEXT_STATUS
|
|
W3_STATE_URLINFO::DoWork(
|
|
W3_MAIN_CONTEXT * pMainContext,
|
|
DWORD cbCompletion,
|
|
DWORD dwCompletionStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle retrieving the metadata for this request
|
|
|
|
Arguments:
|
|
|
|
pMainContext - W3_MAIN_CONTEXT representing execution of state machine
|
|
cbCompletion - Number of bytes in an async completion
|
|
dwCompletionStatus - Error status of a completion
|
|
|
|
Return Value:
|
|
|
|
CONTEXT_STATUS_CONTINUE - if we should continue in state machine
|
|
else stop executing the machine and free up the current thread
|
|
|
|
--*/
|
|
{
|
|
URL_CONTEXT * pUrlContext = NULL;
|
|
BOOL fFinished = FALSE;
|
|
HRESULT hr = NO_ERROR;
|
|
W3_METADATA * pMetaData = NULL;
|
|
CONTEXT_STATUS contextStatus = CONTEXT_STATUS_CONTINUE;
|
|
W3_REQUEST * pHttpRequest = pMainContext->QueryRequest();
|
|
W3_RESPONSE * pResponse = pMainContext->QueryResponse();
|
|
BOOL fAccessAllowed = FALSE;
|
|
|
|
DBG_ASSERT( pHttpRequest != NULL );
|
|
DBG_ASSERT( pResponse != NULL );
|
|
|
|
//
|
|
// Set the context state. Note that passing TRUE as the final
|
|
// argument pushes the URL_CONTEXT into the main context. As
|
|
// a result, errors in this function should not attempt to
|
|
// clean it up.
|
|
//
|
|
|
|
hr = URL_CONTEXT::RetrieveUrlContext( pMainContext,
|
|
pMainContext->QueryRequest(),
|
|
&pUrlContext,
|
|
&fFinished,
|
|
TRUE );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
DBG_ASSERT( fFinished || ( pUrlContext != NULL ) );
|
|
|
|
//
|
|
// From now on, errors in this function should not cleanup the URL
|
|
// context since it is owned by the main context
|
|
//
|
|
|
|
pUrlContext = NULL;
|
|
|
|
//
|
|
// If filter wants out, leave
|
|
//
|
|
|
|
if ( fFinished )
|
|
{
|
|
pMainContext->SetDone();
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
//
|
|
// Check access now. That means checking for IP/SSL/Certs. We will
|
|
// avoid the authentication type check since the others (IP/SSL/Certs)
|
|
// take priority.
|
|
//
|
|
|
|
contextStatus = pMainContext->CheckAccess( FALSE, // not a completion
|
|
0, // cbCompletion,
|
|
NO_ERROR,
|
|
&fAccessAllowed );
|
|
if ( contextStatus == CONTEXT_STATUS_PENDING )
|
|
{
|
|
return CONTEXT_STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// If we don't have access, then the appropriate error response was
|
|
// already sent. Just finish the state machine
|
|
//
|
|
|
|
if ( !fAccessAllowed )
|
|
{
|
|
pMainContext->SetFinishedResponse();
|
|
}
|
|
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
|
|
Failure:
|
|
|
|
if ( pUrlContext != NULL )
|
|
{
|
|
delete pUrlContext;
|
|
}
|
|
|
|
if ( !pMainContext->QueryResponseSent() )
|
|
{
|
|
if ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
|
|
{
|
|
// For the non-8dot3 case
|
|
pMainContext->QueryResponse()->SetStatus( HttpStatusNotFound );
|
|
}
|
|
else if ( hr == HRESULT_FROM_WIN32( ERROR_INVALID_DATA ) )
|
|
{
|
|
pMainContext->QueryResponse()->SetStatus( HttpStatusServerError,
|
|
Http500BadMetadata );
|
|
}
|
|
else if ( hr == HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE ) )
|
|
{
|
|
pMainContext->QueryResponse()->SetStatus( HttpStatusServerError,
|
|
Http500UNCAccess );
|
|
}
|
|
else
|
|
{
|
|
pMainContext->QueryResponse()->SetStatus( HttpStatusServerError );
|
|
}
|
|
}
|
|
|
|
pMainContext->SetFinishedResponse();
|
|
pMainContext->SetErrorStatus( hr );
|
|
|
|
return CONTEXT_STATUS_CONTINUE;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
URL_CONTEXT::RetrieveUrlContext(
|
|
W3_CONTEXT * pW3Context,
|
|
W3_REQUEST * pRequest,
|
|
OUT URL_CONTEXT ** ppUrlContext,
|
|
BOOL * pfFinished,
|
|
BOOL fSetInW3Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For a given request, get a URL_CONTEXT which represents the
|
|
metadata and URI-specific info for that request
|
|
|
|
Arguments:
|
|
|
|
pW3Context - W3_CONTEXT for the request
|
|
pRequest - New request to lookup
|
|
ppUrlContext - Set to point to new URL_CONTEXT
|
|
pfFinished - Set to true if isapi filter said we're finished
|
|
fSetInW3Context - OPTIONAL default FALSE
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
STACK_STRU( strUrl, MAX_PATH );
|
|
W3_URL_INFO * pUrlInfo = NULL;
|
|
W3_METADATA * pMetaData = NULL;
|
|
TOKEN_CACHE_ENTRY * pTokenEntry = NULL;
|
|
URL_CONTEXT * pUrlContext = NULL;
|
|
HRESULT hr = NO_ERROR;
|
|
HANDLE hToken = NULL;
|
|
W3_TRACE_LOG * pTraceLog;
|
|
|
|
if ( pW3Context == NULL ||
|
|
pRequest == NULL ||
|
|
ppUrlContext == NULL ||
|
|
pfFinished == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
*ppUrlContext = NULL;
|
|
|
|
hr = pRequest->GetUrl( &strUrl );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Lookup the URI info for this request
|
|
//
|
|
|
|
DBG_ASSERT( g_pW3Server->QueryUrlInfoCache() != NULL );
|
|
|
|
hr = g_pW3Server->QueryUrlInfoCache()->GetUrlInfo(
|
|
pW3Context,
|
|
strUrl,
|
|
&pUrlInfo );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Now, create a URL_CONTEXT object which contains the W3_URL_INFO and
|
|
// W3_METADATA pointers as well as state information for use on cleanup
|
|
//
|
|
|
|
DBG_ASSERT( pUrlInfo != NULL );
|
|
|
|
pMetaData = (W3_METADATA*) pUrlInfo->QueryMetaData();
|
|
DBG_ASSERT( pMetaData != NULL );
|
|
|
|
pUrlContext = new (pW3Context) URL_CONTEXT( pMetaData, pUrlInfo );
|
|
if ( pUrlContext == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
|
goto Failure;
|
|
}
|
|
else if( fSetInW3Context )
|
|
{
|
|
//
|
|
// If specified, set the UrlContext in pW3Context. This is necessary
|
|
// since the below filter notification depends on having this context
|
|
// for APPL_MD_PATH and APPL_PHYSICAL_PATH. Once set the context owns
|
|
// cleaning it up.
|
|
//
|
|
((W3_MAIN_CONTEXT*)pW3Context)->SetUrlContext( pUrlContext );
|
|
}
|
|
|
|
//
|
|
// Now notify URL_MAP filters
|
|
//
|
|
|
|
if ( pW3Context->IsNotificationNeeded( SF_NOTIFY_URL_MAP ) )
|
|
{
|
|
STACK_STRA( straPhys, MAX_PATH + 1 );
|
|
STACK_STRA( straSavePhys, MAX_PATH + 1 );
|
|
STACK_STRA( straUrl, MAX_PATH + 1 );
|
|
STACK_STRA( straScriptMap, MAX_PATH + 1 );
|
|
BOOL fRet;
|
|
HTTP_FILTER_URL_MAP_EX filterMap;
|
|
STACK_STRU( strPhysicalPath, MAX_PATH );
|
|
|
|
hr = straPhys.CopyW( pUrlInfo->QueryPhysicalPath()->QueryStr() );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Save a copy for comparison after filter notification
|
|
//
|
|
hr = straSavePhys.Copy( straPhys );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
hr = straUrl.CopyW( strUrl.QueryStr() );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
filterMap.pszURL = straUrl.QueryStr();
|
|
filterMap.pszPhysicalPath = straPhys.QueryStr();
|
|
filterMap.cbPathBuff = straPhys.QuerySize();
|
|
filterMap.dwFlags = pMetaData->QueryAccessPerms();
|
|
filterMap.cchMatchingPath = pMetaData->QueryCBMatchingPathA();
|
|
filterMap.cchMatchingURL = pMetaData->QueryCBMatchingUrlA();
|
|
filterMap.pszScriptMapEntry = NULL;
|
|
|
|
if ( pUrlInfo->QueryScriptMapEntry() )
|
|
{
|
|
hr = straScriptMap.CopyW( pUrlInfo->QueryScriptMapEntry()->QueryExecutable()->QueryStr() );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
filterMap.pszScriptMapEntry = straScriptMap.QueryStr();
|
|
}
|
|
|
|
fRet = pW3Context->NotifyFilters( SF_NOTIFY_URL_MAP,
|
|
&filterMap,
|
|
pfFinished );
|
|
|
|
if ( !fRet )
|
|
{
|
|
W3_MAIN_CONTEXT * pMainContext = pW3Context->QueryMainContext();
|
|
DWORD dwError = GetLastError();
|
|
|
|
hr = HRESULT_FROM_WIN32( dwError );
|
|
|
|
pMainContext->SetErrorStatus( hr );
|
|
|
|
if ( dwError == ERROR_ACCESS_DENIED )
|
|
{
|
|
pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
|
|
Http401Filter );
|
|
}
|
|
else if ( dwError == ERROR_FILE_NOT_FOUND ||
|
|
dwError == ERROR_PATH_NOT_FOUND )
|
|
{
|
|
pMainContext->QueryResponse()->SetStatus( HttpStatusNotFound );
|
|
}
|
|
else
|
|
{
|
|
pMainContext->QueryResponse()->SetStatus( HttpStatusServerError );
|
|
}
|
|
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// If the filter is done, then we're done
|
|
//
|
|
|
|
if ( *pfFinished )
|
|
{
|
|
hr = NO_ERROR;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// If the physical path was changed, remember it here
|
|
//
|
|
|
|
if ( strcmp( straSavePhys.QueryStr(),
|
|
filterMap.pszPhysicalPath ) != 0 )
|
|
{
|
|
hr = strPhysicalPath.CopyA( (CHAR*) filterMap.pszPhysicalPath );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
hr = pUrlContext->SetPhysicalPath( strPhysicalPath );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We don't accept short filename since they can break metabase
|
|
// equivalency
|
|
//
|
|
|
|
if ( wcschr( pUrlContext->QueryPhysicalPath()->QueryStr(),
|
|
L'~' ) )
|
|
{
|
|
BOOL fShort = FALSE;
|
|
|
|
hr = pMetaData->GetAndRefVrAccessToken( &pTokenEntry );
|
|
if( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
if ( pTokenEntry != NULL )
|
|
{
|
|
hToken = pTokenEntry->QueryImpersonationToken();
|
|
}
|
|
else
|
|
{
|
|
hToken = NULL;
|
|
}
|
|
|
|
|
|
DWORD dwError = CheckIfShortFileName(
|
|
pUrlContext->QueryPhysicalPath()->QueryStr(),
|
|
hToken,
|
|
&fShort );
|
|
if ( dwError != ERROR_SUCCESS )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( dwError );
|
|
goto Failure;
|
|
}
|
|
|
|
if ( fShort )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
|
goto Failure;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether the UNC user was valid. If not, fail
|
|
//
|
|
|
|
if ( pMetaData->QueryUNCUserInvalid() )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE );
|
|
goto Failure;
|
|
}
|
|
|
|
pTraceLog = pW3Context->QueryMainContext()->QueryTraceLog();
|
|
if ( pTraceLog != NULL )
|
|
{
|
|
pTraceLog->Trace( L"%I64x: Successfully metadata for URL '%ws'\n",
|
|
pRequest->QueryRequestId(),
|
|
strUrl.QueryStr() );
|
|
}
|
|
|
|
*ppUrlContext = pUrlContext;
|
|
|
|
if( pTokenEntry != NULL )
|
|
{
|
|
pTokenEntry->DereferenceCacheEntry();
|
|
pTokenEntry = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
Failure:
|
|
|
|
pTraceLog = pW3Context->QueryMainContext()->QueryTraceLog();
|
|
if ( pTraceLog != NULL )
|
|
{
|
|
pTraceLog->Trace( L"%I64x: Failed to retrieve metadata for URL '%ws'. hr = %08X\n",
|
|
pRequest->QueryRequestId(),
|
|
strUrl.QueryStr(),
|
|
hr );
|
|
}
|
|
|
|
if ( pUrlContext != NULL )
|
|
{
|
|
if( !fSetInW3Context )
|
|
{
|
|
delete pUrlContext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pUrlInfo != NULL )
|
|
{
|
|
pUrlInfo->DereferenceCacheEntry();
|
|
}
|
|
}
|
|
|
|
if( pTokenEntry != NULL )
|
|
{
|
|
pTokenEntry->DereferenceCacheEntry();
|
|
pTokenEntry = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
W3_STATE_URLINFO::MapPath(
|
|
W3_CONTEXT * pW3Context,
|
|
STRU & strUrl,
|
|
STRU * pstrPhysicalPath,
|
|
BOOL fDoFiltering,
|
|
DWORD * pcchDirRoot,
|
|
DWORD * pcchVRoot,
|
|
DWORD * pcbAnsiDirRoot,
|
|
DWORD * pcbAnsiVRoot,
|
|
DWORD * pdwMask
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send a URL/Physical-Path pair to a filter for processing
|
|
|
|
Arguments:
|
|
|
|
pW3Context - W3_CONTEXT for the request
|
|
strUrl - The URL to be mapped
|
|
pstrPhysicalPath - Filled with the mapped path upon return. Set with
|
|
metadata physical path on entry
|
|
fDoFiltering - Enable filters
|
|
pcchDirRoot - Set to point to number of characters in found physical path
|
|
pcchVRoot - Set to point to number of characters in found virtual path
|
|
pcbAnsiDirRoot - Set to point to number of bytes in found ANSI physical path
|
|
pcbAnsiVRoot - Set to point to number of bytes in found ANSI virtual path
|
|
pdwMask - Set to point to the access perms mask of virtual path
|
|
|
|
Return Value:
|
|
|
|
SUCCEEDED()/FAILED()
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
W3_URL_INFO * pUrlInfo = NULL;
|
|
W3_METADATA * pMetaData = NULL;
|
|
|
|
DBG_ASSERT( pstrPhysicalPath );
|
|
|
|
//
|
|
// Get and keep the metadata and urlinfo for this path
|
|
//
|
|
|
|
DBG_ASSERT( g_pW3Server->QueryUrlInfoCache() != NULL );
|
|
|
|
hr = g_pW3Server->QueryUrlInfoCache()->GetUrlInfo(
|
|
pW3Context,
|
|
strUrl,
|
|
&pUrlInfo );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
DBG_ASSERT( pUrlInfo != NULL );
|
|
|
|
//
|
|
// Call the filters if we should do so
|
|
//
|
|
|
|
if ( fDoFiltering )
|
|
{
|
|
hr = FilterMapPath( pW3Context,
|
|
pUrlInfo,
|
|
pstrPhysicalPath );
|
|
}
|
|
else
|
|
{
|
|
hr = pstrPhysicalPath->Copy( *( pUrlInfo->QueryUrlTranslated() ) );
|
|
}
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pMetaData = pUrlInfo->QueryMetaData();
|
|
DBG_ASSERT( pMetaData != NULL );
|
|
|
|
//
|
|
// Return the other goodies
|
|
//
|
|
|
|
if ( pcchDirRoot != NULL )
|
|
{
|
|
*pcchDirRoot = pMetaData->QueryVrPath()->QueryCCH();
|
|
}
|
|
|
|
if ( pcchVRoot != NULL )
|
|
{
|
|
if (strUrl.QueryCCH())
|
|
{
|
|
*pcchVRoot = pMetaData->QueryVrLen();
|
|
}
|
|
else
|
|
{
|
|
*pcchVRoot = 0;
|
|
}
|
|
}
|
|
|
|
if ( pcbAnsiDirRoot != NULL )
|
|
{
|
|
*pcbAnsiDirRoot = pMetaData->QueryCBMatchingPathA();
|
|
}
|
|
|
|
if ( pcbAnsiVRoot != NULL )
|
|
{
|
|
if ( strUrl.QueryCCH() )
|
|
{
|
|
*pcbAnsiVRoot = pMetaData->QueryCBMatchingUrlA();
|
|
}
|
|
else
|
|
{
|
|
*pcbAnsiVRoot = 0;
|
|
}
|
|
}
|
|
|
|
if ( pdwMask != NULL )
|
|
{
|
|
*pdwMask = pMetaData->QueryAccessPerms();
|
|
}
|
|
|
|
Exit:
|
|
|
|
if ( pUrlInfo != NULL )
|
|
{
|
|
pUrlInfo->DereferenceCacheEntry();
|
|
pUrlInfo = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// static
|
|
HRESULT
|
|
W3_STATE_URLINFO::FilterMapPath(
|
|
W3_CONTEXT * pW3Context,
|
|
W3_URL_INFO * pUrlInfo,
|
|
STRU * pstrPhysicalPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Have URL_MAP filters do their thing
|
|
|
|
Arguments:
|
|
|
|
pW3Context - Context
|
|
pUrlInfo - Contains virtual/physical path
|
|
pstrPhysicalPath - Filled with physical path
|
|
|
|
Return Value:
|
|
|
|
SUCCEEDED()/FAILED()
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fFinished = FALSE;
|
|
W3_METADATA * pMetaData = NULL;
|
|
STACK_STRU( strFilterPath, MAX_PATH );
|
|
STRU * pstrFinalPhysical = NULL;
|
|
|
|
if ( pW3Context == NULL ||
|
|
pUrlInfo == NULL ||
|
|
pstrPhysicalPath == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
pMetaData = pUrlInfo->QueryMetaData();
|
|
DBG_ASSERT( pMetaData != NULL );
|
|
|
|
//
|
|
// We now have the metadata physical path. Let filters change it here
|
|
//
|
|
|
|
if ( pW3Context->IsNotificationNeeded( SF_NOTIFY_URL_MAP ) )
|
|
{
|
|
STACK_STRA( straPhys, MAX_PATH + 1 );
|
|
STACK_STRA( straSavePhys, MAX_PATH + 1 );
|
|
STACK_STRA( straUrl, MAX_PATH + 1 );
|
|
STACK_STRA( straScriptMap, MAX_PATH + 1 );
|
|
BOOL fRet;
|
|
HTTP_FILTER_URL_MAP_EX filterMap;
|
|
|
|
hr = straPhys.CopyW( pUrlInfo->QueryUrlTranslated()->QueryStr() );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Save a copy for comparison after filter notification
|
|
//
|
|
hr = straSavePhys.Copy( straPhys );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
hr = straUrl.CopyW( pUrlInfo->QueryUrl() );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
filterMap.pszURL = straUrl.QueryStr();
|
|
filterMap.pszPhysicalPath = straPhys.QueryStr();
|
|
filterMap.cbPathBuff = straPhys.QuerySize();
|
|
filterMap.dwFlags = pMetaData->QueryAccessPerms();
|
|
filterMap.cchMatchingPath = pMetaData->QueryCBMatchingPathA();
|
|
filterMap.cchMatchingURL = pMetaData->QueryCBMatchingUrlA();
|
|
filterMap.pszScriptMapEntry = NULL;
|
|
|
|
if ( pUrlInfo->QueryScriptMapEntry() )
|
|
{
|
|
hr = straScriptMap.CopyW( pUrlInfo->QueryScriptMapEntry()->QueryExecutable()->QueryStr() );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
filterMap.pszScriptMapEntry = straScriptMap.QueryStr();
|
|
}
|
|
|
|
fRet = pW3Context->NotifyFilters( SF_NOTIFY_URL_MAP,
|
|
&filterMap,
|
|
&fFinished );
|
|
|
|
//
|
|
// Ignore finished flag in this case since we really can't do much
|
|
// to advance to finish (since an ISAPI is calling this)
|
|
//
|
|
|
|
if ( !fRet )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Remember the mapped path
|
|
//
|
|
|
|
if ( strcmp( straSavePhys.QueryStr(),
|
|
filterMap.pszPhysicalPath ) != 0 )
|
|
{
|
|
hr = strFilterPath.CopyA( (CHAR*) filterMap.pszPhysicalPath );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pstrFinalPhysical = &strFilterPath;
|
|
}
|
|
else
|
|
{
|
|
pstrFinalPhysical = pUrlInfo->QueryUrlTranslated();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No filter is mapping, therefore just take the URL_INFO's physical
|
|
// path
|
|
//
|
|
|
|
pstrFinalPhysical = pUrlInfo->QueryUrlTranslated();
|
|
|
|
DBG_ASSERT( pstrFinalPhysical != NULL );
|
|
}
|
|
|
|
//
|
|
// We don't accept short filename since they can break metabase
|
|
// equivalency
|
|
//
|
|
|
|
if ( wcschr( pstrFinalPhysical->QueryStr(),
|
|
L'~' ) )
|
|
{
|
|
BOOL fShort = FALSE;
|
|
DWORD dwError = CheckIfShortFileName(
|
|
pstrFinalPhysical->QueryStr(),
|
|
pW3Context->QueryImpersonationToken(),
|
|
&fShort );
|
|
if ( dwError != ERROR_SUCCESS )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( dwError );
|
|
goto Exit;
|
|
}
|
|
|
|
if ( fShort )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the physical path is requested
|
|
//
|
|
|
|
hr = pstrPhysicalPath->Copy( *pstrFinalPhysical );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return hr;
|
|
}
|
|
|
|
DWORD
|
|
CheckIfShortFileName(
|
|
IN WCHAR * pszPath,
|
|
IN HANDLE hImpersonation,
|
|
OUT BOOL * pfShort
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
This function takes a suspected NT/Win95 short filename and checks if there's
|
|
an equivalent long filename. For example, c:\foobar\ABCDEF~1.ABC is the same
|
|
as c:\foobar\abcdefghijklmnop.abc.
|
|
|
|
NOTE: This function should be called unimpersonated - the FindFirstFile() must
|
|
be called in the system context since most systems have traverse checking turned
|
|
off - except for the UNC case where we must be impersonated to get network access.
|
|
|
|
Arguments:
|
|
|
|
pszPath - Path to check
|
|
hImpersonation - Impersonation handle if this is a UNC path - can be NULL if not UNC
|
|
pfShort - Set to TRUE if an equivalent long filename is found
|
|
|
|
Returns:
|
|
|
|
Win32 error on failure
|
|
--*/
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
WIN32_FIND_DATA FindData;
|
|
WCHAR * psz;
|
|
BOOL fUNC;
|
|
|
|
psz = wcschr( pszPath, L'~' );
|
|
*pfShort = FALSE;
|
|
fUNC = (*pszPath == L'\\');
|
|
|
|
//
|
|
// Loop for multiple tildas - watch for a # after the tilda
|
|
//
|
|
|
|
while ( psz++ )
|
|
{
|
|
if ( *psz >= L'0' && *psz <= L'9' )
|
|
{
|
|
WCHAR achTmp[MAX_PATH];
|
|
WCHAR * pchEndSeg;
|
|
WCHAR * pchBeginSeg;
|
|
HANDLE hFind;
|
|
|
|
//
|
|
// Isolate the path up to the segment with the
|
|
// '~' and do the FindFirst with that path
|
|
//
|
|
|
|
pchEndSeg = wcschr( psz, L'\\' );
|
|
if ( !pchEndSeg )
|
|
{
|
|
pchEndSeg = psz + wcslen( psz );
|
|
}
|
|
|
|
//
|
|
// If the string is beyond MAX_PATH then we allow it through
|
|
//
|
|
|
|
if ( ((INT) (pchEndSeg - pszPath)) >= MAX_PATH )
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
memcpy( achTmp,
|
|
pszPath,
|
|
(INT) (pchEndSeg - pszPath) * sizeof( WCHAR ) );
|
|
achTmp[pchEndSeg - pszPath] = L'\0';
|
|
|
|
if ( fUNC && hImpersonation )
|
|
{
|
|
if ( !SetThreadToken( NULL, hImpersonation ))
|
|
{
|
|
return GetLastError();
|
|
}
|
|
}
|
|
|
|
//
|
|
// IVANPASH: Although it looks simpler to use GetLongPathName
|
|
// instead of manually traversing the folders, we must use
|
|
// FindFirstFileW, because GetLongPathName requires ACLs for
|
|
// IIS_WPG from the root of the drive to work.
|
|
//
|
|
|
|
hFind = FindFirstFileW( achTmp, &FindData );
|
|
|
|
if ( fUNC && hImpersonation )
|
|
{
|
|
RevertToSelf();
|
|
}
|
|
|
|
if ( hFind == INVALID_HANDLE_VALUE )
|
|
{
|
|
err = GetLastError();
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"FindFirst failed!! - \"%s\", error %d\n",
|
|
achTmp,
|
|
GetLastError() ));
|
|
|
|
//
|
|
// If the FindFirstFile() fails to find the file then return
|
|
// success - the path doesn't appear to be a valid path which
|
|
// is ok.
|
|
//
|
|
|
|
if ( err == ERROR_FILE_NOT_FOUND ||
|
|
err == ERROR_PATH_NOT_FOUND ||
|
|
err == ERROR_INVALID_NAME )
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
DBG_REQUIRE( FindClose( hFind ));
|
|
|
|
//
|
|
// Isolate the last segment of the string which should be
|
|
// the potential short name equivalency
|
|
//
|
|
|
|
pchBeginSeg = wcsrchr( achTmp, L'\\' );
|
|
DBG_ASSERT( pchBeginSeg );
|
|
pchBeginSeg++;
|
|
|
|
//
|
|
// If the last segment doesn't match the long name then this is
|
|
// the short name version of the path
|
|
//
|
|
|
|
if ( _wcsicmp( FindData.cFileName, pchBeginSeg ))
|
|
{
|
|
*pfShort = TRUE;
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
psz = wcschr( psz, L'~' );
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
HRESULT
|
|
URL_CONTEXT::OpenFile(
|
|
CACHE_USER * pFileUser,
|
|
W3_FILE_INFO ** ppOpenFile,
|
|
FILE_CACHE_ASYNC_CONTEXT * pAsyncContext,
|
|
BOOL * pfHandledSync,
|
|
BOOL fAllowNoBuffering,
|
|
BOOL fCheckForExistenceOnly
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open the physical path for this request. If a map path filter did some
|
|
redirecting, we will use that path. Otherwise we will just use the
|
|
path determined by metadata and cached in the W3_URL_INFO
|
|
|
|
Arguments:
|
|
|
|
pFileUser - User to open file as
|
|
ppOpenFile - Set to file cache entry on success
|
|
pAsyncContext - In case an async read is desired, context with callback information
|
|
pfHandledSync - Did the open complete synchronously
|
|
fAllowNoBuffering - Allow the file to be opened with FILE_FLAG_NO_BUFFERING
|
|
fCheckForExistenceOnly - Only interested in existence of the file
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
BOOL fDoCache;
|
|
|
|
DBG_ASSERT( QueryMetaData() != NULL );
|
|
|
|
fDoCache = !QueryMetaData()->QueryNoCache();
|
|
|
|
//
|
|
// If an ISAPI filter changed the physical path, then we need to go
|
|
// directly to the file cache. Otherwise, we can go thru the
|
|
// W3_URL_INFO which may already have the cached file associated
|
|
//
|
|
|
|
if ( _strPhysicalPath.IsEmpty() )
|
|
{
|
|
//
|
|
// No filter. Fast path :-)
|
|
//
|
|
|
|
DBG_ASSERT( _pUrlInfo != NULL );
|
|
|
|
hr = _pUrlInfo->GetFileInfo( pFileUser,
|
|
fDoCache,
|
|
ppOpenFile,
|
|
pAsyncContext,
|
|
pfHandledSync,
|
|
fAllowNoBuffering,
|
|
fCheckForExistenceOnly );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Filter case. Must lookup in file cache :-(
|
|
//
|
|
|
|
DBG_ASSERT( g_pW3Server->QueryFileCache() != NULL );
|
|
|
|
hr = g_pW3Server->QueryFileCache()->GetFileInfo(
|
|
_strPhysicalPath,
|
|
QueryMetaData()->QueryDirmonConfig(),
|
|
pFileUser,
|
|
fDoCache,
|
|
ppOpenFile,
|
|
pAsyncContext,
|
|
pfHandledSync,
|
|
fAllowNoBuffering,
|
|
fCheckForExistenceOnly );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
URL_CONTEXT::Initialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize URL_CONTEXT lookaside
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//static
|
|
VOID
|
|
URL_CONTEXT::Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clean up URL_CONTEXT lookaside
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|