#ifndef _W3CONTEXT_HXX_
#define _W3CONTEXT_HXX_

#define W3_PARAMETERS_KEY \
            L"System\\CurrentControlSet\\Services\\w3svc\\Parameters"

// Is it a UNC Path?
#define ISUNC(a) ((a)[0]==L'\\' && (a)[1]==L'\\')

#define W3_FLAG_ASYNC                   0x00000001
#define W3_FLAG_SYNC                    0x00000002
#define W3_FLAG_NO_CUSTOM_ERROR         0x00000004
#define W3_FLAG_MORE_DATA               0x00000008
#define W3_FLAG_PAST_END_OF_REQ         0x00000010
#define W3_FLAG_NO_HEADERS              0x00000020
#define W3_FLAG_NO_CONTENT_LENGTH       0x00000040
#define W3_FLAG_NO_ERROR_BODY           0x00000080

#define W3_FLAG_VALID               ( W3_FLAG_ASYNC             | \
                                      W3_FLAG_SYNC              | \
                                      W3_FLAG_NO_CUSTOM_ERROR   | \
                                      W3_FLAG_MORE_DATA         | \
                                      W3_FLAG_PAST_END_OF_REQ   | \
                                      W3_FLAG_NO_HEADERS        | \
                                      W3_FLAG_NO_ERROR_BODY     | \
                                      W3_FLAG_NO_CONTENT_LENGTH )

#define VALID_W3_FLAGS(x)           ( (x) == ((x) & (W3_FLAG_VALID)) )

class URL_CONTEXT;
class W3_HANDLER;
class COMPRESSION_CONTEXT;

//
// Access check state
//
// We potentially do two asynchronous operations in determining whether
// a given request is valid
// 1) Do an RDNS lookup if restrictions are configured in metabase
// 2) Request a client certificate if configured in metabase
//

enum W3_CONTEXT_ACCESS_STATE
{
    ACCESS_STATE_START,
    ACCESS_STATE_RDNS,
    ACCESS_STATE_CLIENT_CERT_PRELOAD_ENTITY,
    ACCESS_STATE_CLIENT_CERT,
    ACCESS_STATE_AUTHENTICATION,
    ACCESS_STATE_SENDING_ERROR,
    ACCESS_STATE_DONE
};

//
// W3_CONTEXT - Object representing an execution of the state machine to
//              handle a given request from UL
//

#define W3_CONTEXT_LIST_SPINS               200
//
// maximum number of nested child execute calls that are supported
//
#define W3_CONTEXT_MAX_RECURSION_LEVEL      100

#define W3_CONTEXT_SIGNATURE       ((DWORD) 'XC3W')
#define W3_CONTEXT_SIGNATURE_FREE  ((DWORD) 'xc3w')

class W3_CONTEXT
{
private:

    DWORD                   _dwSignature;

    //
    // Handler for this context
    //

    W3_HANDLER *            _pHandler;

    //
    // Error Status for request
    //

    HRESULT                 _errorStatus;

    //
    // Completion indication (for synchronous execution of handlers)
    //

    HANDLE                  _hCompletion;

    //
    // Custom error file to cleanup
    //

    W3_FILE_INFO *          _pCustomErrorFile;

    //
    // Maintain list of contexts
    //

    LIST_ENTRY              _listEntry;

    //
    // Where are we with checking access rights for this request
    //

    W3_CONTEXT_ACCESS_STATE _accessState;
    BOOL                    _fDNSRequiredForAccess;         // argh

    //
    // The status of last child request executed by this context
    //

    USHORT                  _childStatusCode;
    USHORT                  _childSubErrorCode; 
    HRESULT                 _childError;

    //
    // Is authentication access check required or not
    //

    BOOL                    _fAuthAccessCheckRequired;

    //
    // Are we suppressing the response entity (normally if it is a HEAD
    // request to something other than an ISAPI)
    //

    BOOL                    _fSuppressEntity;

    //
    // Recursion level for the ExecuteUrl() used to prevent infinite recursion
    // If recursion level reaches W3_CONTEXT_MAX_RECURSION_LEVEL value
    // then no more IsapiExecuteUrl() or ExecuteChildRequest() calls are allowed
    // 

    DWORD                   _dwRecursionLevel;

    //
    // Keep a reference to the VrToken so it won't get deleted somewhere else
    //
    
    TOKEN_CACHE_ENTRY *     _pctVrToken;

    //
    // Chunk buffer
    //
    
    CHUNK_BUFFER            _ChunkBuffer;

    CONTEXT_STATUS
    ExecuteCurrentHandler(
        VOID
    );

protected:

    //
    // The flags determining how to execute this request
    // (we let child contexts alter these flags if needed)
    //
    
    DWORD                   _dwExecFlags;

public:

    static CHAR                     sm_achRedirectMessage[ 512 ];
    static DWORD                    sm_cbRedirectMessage;
    static CHAR *                   sm_pszAccessDeniedMessage;
    
    W3_CONTEXT( 
        DWORD           dwExecFlags,
        DWORD           dwRecursionLevel
    );
    
    virtual ~W3_CONTEXT();
    
    virtual
    BOOL
    QueryResponseSent(
        VOID
    ) = 0;
    
    virtual
    BOOL
    QuerySendLocation(
        VOID
    )
    {
        return FALSE;
    }
   
    virtual
    BOOL
    QueryNeedFinalDone(
        VOID
    ) = 0;

    virtual
    VOID
    SetNeedFinalDone(
        VOID
    ) = 0;
    
    virtual
    W3_REQUEST *
    QueryRequest(
        VOID
    ) = 0;

    virtual
    W3_RESPONSE *
    QueryResponse(
        VOID
    ) = 0;    

    virtual
    W3_SITE *
    QuerySite(
        VOID
    ) = 0;

    virtual
    W3_CONTEXT *
    QueryParentContext(
        VOID
    ) = 0;
   
    virtual
    W3_MAIN_CONTEXT *
    QueryMainContext(
        VOID
    ) = 0;
    
    virtual
    URL_CONTEXT *
    QueryUrlContext(
        VOID
    ) = 0;
    
    virtual
    W3_USER_CONTEXT *
    QueryUserContext(
        VOID
    ) = 0;
    
    virtual
    W3_FILTER_CONTEXT *
    QueryFilterContext(
        BOOL                    fCreateIfNotFound = TRUE
    ) = 0;

    virtual
    ULATQ_CONTEXT
    QueryUlatqContext(
        VOID
    ) = 0;
    
    virtual
    BOOL
    QueryProviderHandled(
        VOID
    ) = 0;
    
    virtual
    BOOL
    NotifyFilters(
        DWORD               dwNotification,
        VOID *              pvFilterInfo,
        BOOL *              pfFinished
    ) = 0;

    virtual
    BOOL
    IsNotificationNeeded(
        DWORD               dwNotification
    ) = 0;
    
    virtual
    VOID
    SetDisconnect(
        BOOL                fDisconnect
    ) = 0;
    
    virtual
    BOOL
    QueryDisconnect(
        VOID
    ) = 0;

    virtual 
    VOID 
    SetDoneWithCompression(
        VOID
    ) = 0;

    virtual 
    BOOL 
    QueryDoneWithCompression(
        VOID
    ) = 0;

    virtual 
    VOID 
    SetCompressionContext(
        COMPRESSION_CONTEXT *   pCompressionContext
    ) = 0;

    virtual 
    COMPRESSION_CONTEXT *
    QueryCompressionContext(
        VOID
    ) = 0;

    virtual 
    HTTP_LOG_FIELDS_DATA *
    QueryUlLogData(
        VOID
    ) = 0;

    virtual 
    VOID 
    SetLastIOPending(
        LAST_IO_PENDING         ioPending
    ) = 0;

    virtual 
    VOID 
    IncrementBytesRecvd(
        DWORD                   dwRead
    ) = 0;

    virtual
    VOID
    IncrementBytesSent(
        DWORD                   dwSent
    ) = 0;
    
    virtual
    BOOL
    QueryIsUlCacheable(
        VOID
    ) = 0;
    
    virtual
    VOID
    DisableUlCache(
        VOID
    ) = 0;
    
    //
    // Other fixed W3_CONTEXT methods
    //
    
    VOID *
    ContextAlloc(
        DWORD           cbSize
    );

    W3_HANDLER *
    QueryHandler(
        VOID
    ) const
    {
        return _pHandler;
    }
    
    VOID
    SetChildStatusAndError(
        USHORT                  ChildStatusCode,
        USHORT                  ChildSubError,
        HRESULT                 ChildError
    )
    {
        _childStatusCode = ChildStatusCode;
        _childSubErrorCode = ChildSubError;
        _childError = ChildError;
    }
    
    VOID
    QueryChildStatusAndError(
        USHORT *                pChildStatusCode,
        USHORT *                pChildSubError,
        DWORD *                 pChildError
    ) const
    {
        DBG_ASSERT( pChildStatusCode != NULL );
        DBG_ASSERT( pChildSubError != NULL );
        DBG_ASSERT( pChildError != NULL );
        
        *pChildStatusCode = _childStatusCode;
        *pChildSubError = _childSubErrorCode;
        
        if ( FAILED( _childError ) )
        {
            *pChildError = WIN32_FROM_HRESULT( _childError );
        }
        else
        {
            *pChildError = ERROR_SUCCESS;
        }
    }
    
    BOOL
    QuerySendCustomError(
        VOID
    ) const
    {
        if ( _dwExecFlags & W3_FLAG_NO_CUSTOM_ERROR )
        {
            return FALSE;
        }
        else
        {
            return TRUE;
        }
    }
    
    BOOL
    QuerySendErrorBody(
        VOID
    ) const
    {
        if ( _dwExecFlags & W3_FLAG_NO_ERROR_BODY )
        {
            return FALSE;
        }
        else
        {
            return TRUE;
        }
    }
    
    BOOL
    QuerySendHeaders(
        VOID
    ) const
    {
        if ( _dwExecFlags & W3_FLAG_NO_HEADERS )
        {
            return FALSE;
        }
        else
        {
            return TRUE;
        }
    }
    
    VOID 
    SetAuthAccessCheckRequired(
        BOOL fAuthAccessCheckRequired
        )
    {
        _fAuthAccessCheckRequired = fAuthAccessCheckRequired;
    }

    BOOL 
    QueryAuthAccessCheckRequired(
        VOID
    ) const
    {
        return _fAuthAccessCheckRequired;
    }

    VOID
    SetSSICommandHandler(
        W3_HANDLER *            pHandler
    );

    BOOL 
    QueryDoUlLogging(
        VOID
    );

    BOOL 
    QueryDoCustomLogging(
        VOID
    );
    
    HRESULT
    SetupCustomErrorFileResponse(
        STRU &                  strErrorFile
    );

    HRESULT
    SendResponse(
        DWORD                   dwFlags
    );
    
    HRESULT
    GetCertificateInfoEx(
        IN  DWORD           cbAllocated,
        OUT DWORD *         pdwCertEncodingType,
        OUT unsigned char * pbCertEncoded,
        OUT DWORD *         pcbCertEncoded,
        OUT DWORD *         pdwCertificateFlags
    );
    
    HANDLE
    QueryImpersonationToken(
        BOOL *  pfIsVrToken = NULL
    );
    
    HANDLE
    QueryPrimaryToken(
        VOID
    );
    
    TOKEN_CACHE_ENTRY *
    QueryVrToken(
        VOID
    )
    {
        return _pctVrToken;
    }

    VOID
    QueryFileCacheUser(
        CACHE_USER *            pFileUser
    );
    
    CONTEXT_STATUS
    CheckAccess(
        BOOL                    fCompletion,
        DWORD                   cbCompletion,
        DWORD                   dwCompletionStatus,
        BOOL *                  pfAccessAllowed
    ); 
    
    CERTIFICATE_CONTEXT *
    QueryCertificateContext(
        VOID
    );
    
    CHUNK_BUFFER *
    QueryHeaderBuffer(
        VOID
    )
    {
        return &_ChunkBuffer;
    }
    
    BOOL
    CheckClientCertificateAccess(
        VOID
    );
    
    HRESULT
    SendEntity(
        DWORD                   dwFlags
    );

    HRESULT
    ReceiveEntity(
        DWORD                   dwFlags,
        VOID *                  pBuffer,
        DWORD                   cbBuffer,
        DWORD *                 pBytesReceived
    );
    
    DWORD
    QueryRemainingEntityFromUl(
        VOID
    );

    VOID SetRemainingEntityFromUl(
        DWORD cbRemaining
    );

    VOID
    QueryAlreadyAvailableEntity(
        VOID **                 ppvBuffer,
        DWORD *                 pcbBuffer
    );
    
    HRESULT
    QueryErrorStatus(
        VOID
    ) const
    {
        return _errorStatus;
    }

    VOID
    SetErrorStatus(
        HRESULT             errorStatus
    )
    {
        _errorStatus = errorStatus;
    }

    HRESULT
    CheckUrlRedirection(
        BOOL *pfRedirected,
        STRU *pstrDestination,
        HTTP_STATUS *pStatusCode
    );

    HRESULT
    SetupHttpRedirect(
        STRA &              strPath,
        BOOL                fIncludeParameters,
        HTTP_STATUS &       httpStatus
    );

    HRESULT
    SetupHttpRedirect(
        STRU &              strPath,
        BOOL                fIncludeParameters,
        HTTP_STATUS &       httpStatus
    );

    HRESULT
    SetupAllowHeader(
        VOID
    );
    
    BOOL
    CheckSignature(
        VOID
    ) const
    {
        return _dwSignature == W3_CONTEXT_SIGNATURE;
    }
    
    BOOL
    QueryAccessChecked(
        VOID
    ) const
    {
        return _accessState == ACCESS_STATE_DONE;
    }
    
    VOID
    ResetAccessCheck(
        VOID
    ) 
    {
        _accessState = ACCESS_STATE_START;  
    }
    
    HRESULT
    SetupCompletionEvent(
        VOID
    )
    {
        _hCompletion = IIS_CREATE_EVENT( "W3_CONTEXT::_hCompletion",
                                         this,
                                         FALSE,
                                         FALSE );
        if ( _hCompletion == NULL )
        {
            return HRESULT_FROM_WIN32( GetLastError() );
        }
        else
        {
            return NO_ERROR;
        }
    }
    
    VOID
    WaitForCompletion(
        VOID
    )
    {
        if ( _hCompletion )
        {
            WaitForSingleObject( _hCompletion, INFINITE );
        }
    }
    
    VOID
    IndicateCompletion(
        VOID
    )
    {
        if ( _hCompletion )
        {
            SetEvent( _hCompletion );
        }
    }
    
    BOOL
    QueryIsSynchronous(
        VOID
    ) const
    {
        return _hCompletion != NULL;
    }
    
    HRESULT
    IsapiExecuteUrl(
        EXEC_URL_INFO *         pExecUrlInfo
    );

    HRESULT
    CleanIsapiExecuteUrl(
        EXEC_URL_INFO *         pExecUrlInfo
    );
    
    HRESULT
    IsapiSendCustomError(
        HSE_CUSTOM_ERROR_INFO * pCustomErrorInfo
    );

    HRESULT
    CleanIsapiSendCustomError(
        HSE_CUSTOM_ERROR_INFO * pCustomErrorInfo
    );
    
    HRESULT
    ExecuteChildRequest(
        W3_REQUEST *            pNewRequest,
        BOOL                    fOwnRequest,
        DWORD                   dwFlags
    );

    HRESULT
    ExecuteHandler(
        DWORD                   dwFlags,
        BOOL *                  pfDidImmediateFinish = NULL
    );
    
    CONTEXT_STATUS
    ExecuteHandlerCompletion(
        DWORD                   cbCompletion,
        DWORD                   dwCompletionStatus
    );

    HRESULT
    CheckPathInfoExists(
        W3_HANDLER **               ppHandler
    );

    HRESULT
    InternalDetermineHandler(
        W3_HANDLER **           ppHandler,
        BOOL                    fDoExistenceCheck
    );
    
    HRESULT
    DetermineHandler(
        VOID
    )
    {
        return InternalDetermineHandler( &_pHandler, TRUE );
    }

    HRESULT
    ValidateAppPool(
        BOOL *                  pfAppPoolValid
    );

    virtual
    DWORD
    QueryCurrentStarScriptMapIndex(
        VOID
    ) = 0;

    static HRESULT
    Initialize(
        VOID
    );
    
    static VOID
    Terminate(
        VOID
    );
    
    static
    VOID
    OnCleanIsapiExecuteUrl(
        DWORD                   dwCompletionStatus,
        DWORD                   cbWritten,
        LPOVERLAPPED            lpo
    );
    
    static
    VOID
    OnCleanIsapiSendCustomError(
        DWORD                   dwCompletionStatus,
        DWORD                   cbWritten,
        LPOVERLAPPED            lpo
    );
};

//
// EXECUTE_CONTEXT Used to marshall an IsapiExecuteUrl() or 
// IsapiSendCustomError() on a clean (non-coinited) thread
//

#define EXECUTE_CONTEXT_SIGNATURE       ((DWORD) 'TCXE')
#define EXECUTE_CONTEXT_SIGNATURE_FREE  ((DWORD) 'xcxe')

class EXECUTE_CONTEXT
{
public:
    EXECUTE_CONTEXT( W3_CONTEXT * pW3Context )
    {
        DBG_ASSERT( pW3Context != NULL );
        _hEvent = NULL;
        
        _pW3Context = pW3Context;
        ZeroMemory( &_ExecUrlInfo, sizeof( _ExecUrlInfo ) );
        ZeroMemory( &_UserInfo, sizeof( _UserInfo ) );
        ZeroMemory( &_EntityInfo, sizeof( _EntityInfo ) );
        
        _dwSignature = EXECUTE_CONTEXT_SIGNATURE;
    }
    
    virtual ~EXECUTE_CONTEXT()
    {
        _dwSignature = EXECUTE_CONTEXT_SIGNATURE_FREE;
        
        if ( _hEvent != NULL )
        {
            SetEvent( _hEvent );
        } 
    }
    
    HRESULT
    InitializeFromExecUrlInfo(
        EXEC_URL_INFO *     pExecUrlInfo
    );
    
    EXEC_URL_INFO *
    QueryExecUrlInfo(
        VOID
    )
    {
        return &_ExecUrlInfo;
    }
    
    VOID
    SetCompleteEvent(
        HANDLE          hEvent
    )
    {
        _hEvent = hEvent;
    }
    
    BOOL
    CheckSignature(
        VOID
    ) const
    {
        return _dwSignature == EXECUTE_CONTEXT_SIGNATURE;
    }
    
    HANDLE
    QueryCompleteEvent(
        VOID
    ) const
    {
        return _hEvent;
    }
    
    W3_CONTEXT *
    QueryW3Context(
        VOID
    ) const
    {
        return _pW3Context;
    }
    
    VOID * 
    operator new( 
#if DBG
        size_t            size
#else
        size_t
#endif
    )
    {
        DBG_ASSERT( size == sizeof( EXECUTE_CONTEXT ) );
        DBG_ASSERT( sm_pachExecuteContexts != NULL );
        return sm_pachExecuteContexts->Alloc();
    }
    
    VOID
    operator delete(
        VOID *            pExecuteContext
    )
    {
        DBG_ASSERT( pExecuteContext != NULL );
        DBG_ASSERT( sm_pachExecuteContexts != NULL );
        
        DBG_REQUIRE( sm_pachExecuteContexts->Free( pExecuteContext ) );
    }
    
    static
    HRESULT
    Initialize(
        VOID
    );
    
    static
    VOID
    Terminate(
        VOID
    );

private:
    DWORD                       _dwSignature;
    W3_CONTEXT *                _pW3Context;
    EXEC_URL_INFO               _ExecUrlInfo;
    EXEC_URL_USER_INFO          _UserInfo;
    EXEC_URL_ENTITY_INFO        _EntityInfo;
    CHUNK_BUFFER                _HeaderBuffer;
    HANDLE                      _hEvent;
    
    static ALLOC_CACHE_HANDLER *    sm_pachExecuteContexts;
};

#endif