//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 2000.
//
//  File:       session.cxx
//
//  Contents:   TSession interfaces.
//
//  History:    3-30-97     MohamedN   Created
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#define DBINITCONSTANTS
#include <mparser.h>
#undef DBINITCONSTANTS

#include <session.hxx>
#include <stdqspec.hxx>     // CQuerySpec

// Constants -----------------------------------------------------------------
// Session object interfaces that support ISupportErrorInfo
static const GUID* apSessionErrInt[] = 
{
    &IID_IDBCreateCommand,
    &IID_IGetDataSource,
    &IID_IOpenRowset,
    &IID_ISessionProperties,
};
static const ULONG cSessionErrInt       = NUMELEM( apSessionErrInt );

//+---------------------------------------------------------------------------
//
//  Class:      CDBSession::CDBSession
//
//  Purpose:    ctor
//
//  History:    3-30-97     MohamedN   Created
//
//  Notes: 
//
//----------------------------------------------------------------------------

CDBSession::CDBSession( CDataSrc &  dataSrc, 
                        IUnknown *  pUnkOuter,
                        IUnknown ** ppUnkInner,
                        IParserSession * pIPSession,
                        HANDLE      hToken ) : 
        _dataSrc(dataSrc),
#pragma warning(disable : 4355) // 'this' in ctor
        _impIUnknown(this),
        _ErrorInfo( * ((IUnknown *) (IOpenRowset *) this), _mtxSess ),
#pragma warning(default : 4355) // 'this' in ctor.
        _xSessionToken( hToken )
{
    _pUnkOuter  = pUnkOuter ? pUnkOuter : (IUnknown *) &_impIUnknown;
  
    _ErrorInfo.SetInterfaceArray( cSessionErrInt, apSessionErrInt );    

    _dataSrc.AddRef();
    _dataSrc.IncSessionCount();

    //
    // SQL Text Parser
    //
    Win4Assert( pIPSession );
    _xIPSession.Set( pIPSession );
    _xIPSession->AddRef();

    *ppUnkInner = (IUnknown *) &_impIUnknown;
    (*ppUnkInner)->AddRef();
}


//+---------------------------------------------------------------------------
//
//  Class:      CDBSession::~CDBSession
//
//  Purpose:    dtor
//
//  History:    3-30-97     MohamedN   Created
//
//  Notes: 
//
//----------------------------------------------------------------------------

CDBSession::~CDBSession()
{
    _dataSrc.DecSessionCount();
    _dataSrc.Release();
}

//+---------------------------------------------------------------------------
//
//  Member:     CDBSession::RealQueryInterface
//
//  Synopsis:   Supports IID_IUnknown,
//                       IID_IGetDataSource,
//                       IID_IOpenRowset,
//                       IID_ISessionProperties,
//                       IID_IDBCreateCommand
//                       IID_ISupportErrorInfo
//
//  History:    03-30-97    mohamedn    created
//              10-18-97    danleg      added ISupportErrorInfo Support
//              01-29-98    danleg      non delegating QI when not aggregated
//----------------------------------------------------------------------------

STDMETHODIMP CDBSession::RealQueryInterface(REFIID riid, void **ppvObj )
{
    SCODE sc = S_OK;
    
    if ( !ppvObj )
        return E_INVALIDARG;

    *ppvObj = 0;

    if ( riid == IID_IUnknown )
    {
        *ppvObj = (void *) ( (IUnknown *) (IOpenRowset *) this );
    }
    else if ( riid == IID_IGetDataSource )
    {
        *ppvObj = (void *) (IGetDataSource *) this;
    }
    else if ( riid == IID_ISessionProperties )
    {
        *ppvObj = (void *) (ISessionProperties *) this;
    }
    else if ( riid == IID_IOpenRowset )
    {
        *ppvObj = (void *) (IOpenRowset *) this;
    }
    else if ( riid == IID_IDBCreateCommand )
    {
        *ppvObj = (void *) (IDBCreateCommand *) this;
    }
    else if ( riid == IID_ISupportErrorInfo )
    {
        *ppvObj = (void *) ((IUnknown *) (ISupportErrorInfo *) &_ErrorInfo);
    }
    else
    {
        *ppvObj = 0;
        sc = E_NOINTERFACE;
    }

    return sc;
} //RealQueryInterface

//+---------------------------------------------------------------------------
//
//  Method:     CDBSession::GetDataSource
//
//  Synopsis:   obtains the owning data source object
//
//  Arguments:  [riid]          - interface to bind
//              [ppDataSource]  - interface returned here
//
//  returns:    S_OK, E_NOINTERFACE
//
//  History:     3-30-97    mohamedn    Created
//              11-20-97    danleg      QI on OuterUnk 
//  Notes:      
//
//----------------------------------------------------------------------------

STDMETHODIMP CDBSession::GetDataSource( REFIID riid, IUnknown ** ppDataSource )
{

    SCODE sc = S_OK;

    // Clear previous Error Object for this thread
    _ErrorInfo.ClearErrorInfo();

    // Check Function Arguments
    if ( 0 == ppDataSource )
        return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IGetDataSource );
    
    TRANSLATE_EXCEPTIONS;
    TRY
    {
        CLock lck( _mtxSess );

        *ppDataSource = 0;

        sc = (_dataSrc.GetOuterUnk())->QueryInterface( riid, (void **)ppDataSource );
        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IGetDataSource );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Function:   CDBSession::GetProperties
//
//  Synopsis:   gets ISessionProperties
//
//  Arguments:  [cPropertyIDSets]  - number of desired property set IDs or 0
//              [pPropIDSets]      - array of desired property set IDs or NULL
//              [pcPropertySets]   - number of property sets returned
//              [prgPropertySets]  - array of returned property sets
//
//  Returns:    SCODE - result code indicating error return status.  One of
//                      S_OK, DB_S_ERRORSOCCURRED, E_INVALIDARG.
//
//  History:    03-30-97    mohamedn    Created
//              10-30-97    danleg      Changed to use common property code 
//--------------------------------------------------------------------------

STDMETHODIMP CDBSession::GetProperties(ULONG cPropertySets, const DBPROPIDSET rgPropertySets[],
                                       ULONG* pcProperties, DBPROPSET** prgProperties)
{
    SCODE sc = S_OK;

    // Clear previous Error Object for this thread
    _ErrorInfo.ClearErrorInfo();

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        CLock lck( _mtxSess );

        _UtlProps.GetPropertiesArgChk( cPropertySets,
                                       rgPropertySets,
                                       pcProperties,
                                       prgProperties );

        sc = _UtlProps.GetProperties( cPropertySets,
                                      rgPropertySets,
                                      pcProperties,
                                      prgProperties );
        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_ISessionProperties );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   CDBSession::SetProperties
//
//  Synopsis:   sets ISessionProperties
//
//  Arguments:  [cPropertySets]   - number of property sets
//              [rgPropertySets]  - array of property sets
//
//  Returns:    SCODE - result code indicating error return status.  One of
//                      S_OK, DB_S_ERRORSOCCURRED, E_INVALIDARG.
//
//  History:    03-30-97    mohamedn      created
//              10-28-97    danleg        Changed to use common property code
//----------------------------------------------------------------------------

STDMETHODIMP CDBSession::SetProperties(ULONG cPropertySets, DBPROPSET rgPropertySets[])
{
    SCODE sc = S_OK;

    // Win4Assert( _pCUtlProps );

    // Clear previous Error Object for this thread
    _ErrorInfo.ClearErrorInfo();

    // Quick return if the Count of Properties is 0
    if( cPropertySets == 0 )
        return S_OK;

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        CLock lck( _mtxSess );

        _UtlProps.SetPropertiesArgChk( cPropertySets, rgPropertySets );

        sc = _UtlProps.SetProperties( cPropertySets,
                                      rgPropertySets);
        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_ISessionProperties );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Method:     CDBSession::CreateCommand
//
//  Synopsis:   Creates an ICommand object
//
//  Arguments:  [pUnkOuter] -- 'Outer' IUnknown
//              [riid]      -- Interface to bind
//              [ppCommand] -- Interface returned here
//
//
//  returns:    SCODE of success or failure.
//
//  History:    03-30-97     mohamedn    Created
//              11-03-97     danleg      Aggregation support & error posting
//
//  Notes:      
//
//----------------------------------------------------------------------------

STDMETHODIMP CDBSession::CreateCommand
    (
    IUnknown *  pUnkOuter, 
    REFIID      riid, 
    IUnknown ** ppCommand
    )
{
    // Clear previous Error Object for this thread
    _ErrorInfo.ClearErrorInfo();

    // Check Function Arguments
    if ( 0 == ppCommand )
        return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IDBCreateCommand );

    if (0 != pUnkOuter && riid != IID_IUnknown)  
        return _ErrorInfo.PostHResult( DB_E_NOAGGREGATION, IID_IDBCreateCommand );  

    *ppCommand          = 0;
    SCODE        sc     = S_OK;
    CQuerySpec * pQuery = 0;

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        IUnknown * pUnkInner = 0;

        // Serialize access to this object.
        CLock lck( _mtxSess );

        pQuery = new CQuerySpec( pUnkOuter, &pUnkInner, this );

        XInterface<IUnknown> xUnkInner( pUnkInner );

        if ( IID_IUnknown == riid )
        {
            *ppCommand = pUnkInner;
        }
        else
        {
            sc = pUnkInner->QueryInterface( riid, (void **)ppCommand );
            if ( FAILED(sc) )
            {
                Win4Assert( 0 == *ppCommand );
                THROW( CException(sc) );
            }
        }
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBCreateCommand );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;
    
    return sc;
} //CreateCommand
 


//
// Constants used by OpenRowset
//
static const WCHAR BASE_SELECT[]    = L"SELECT Path, FileName, Size, Write,"\
                                      L"Attrib, DocTitle, DocAuthor, DocSubject,"\
                                      L"DocKeywords, Characterization FROM "\
                                      L"SCOPE('SHALLOW TRAVERSAL OF %s')";

//Approx. size (Includes space for NULL-TERMINATOR, do not subtract this out)
const ULONG APPROX_CCH_BASE_SELECT      = sizeof(BASE_SELECT) / sizeof(WCHAR);  

//+---------------------------------------------------------------------------
//
//  Method:     CDBSession::OpenRowset
//
//  Synopsis:   Opens and returns a rowset that includes all rows from 
//              a single base table
//
//  Arguments:  [pUnkOuter]      - Controlling unknown, if any
//              [pTableID]       - Table to open
//              [pIndexID]       - DBID of the index
//              [riid]           - Interface to return
//              [cPropertySets]  - Count of properties
//              [rgPropertySets] - Array of property values
//              [ppRowset]       - Where to return interface
//
//  Returns:    S_OK          - The method succeeded
//              E_INVALIDARG  - pTableID and pIndexId were NULL
//              E_FAIL        - Provider-specific error
//              DB_E_NOTABLE  - Specified table does not exist in current Data
//                              Data Source object
//              E_OUTOFMEMORY - Out of memory
//              E_NOINTERFACE - The requested interface was not available
//
//  History:    10-26-97    danelg     Created from Monarch
//
//----------------------------------------------------------------------------

STDMETHODIMP CDBSession::OpenRowset
    (
    IUnknown *  pUnkOuter,
    DBID     *  pTableID,
    DBID     *  pIndexID,
    REFIID      riid,
    ULONG       cPropertySets,
    DBPROPSET   rgPropertySets[],
    IUnknown ** ppRowset
    )
{
    ULONG                   ul, ul2;
    SCODE                   sc          = S_OK;
    SCODE                   scProp      = S_OK;

    // Clear previous Error Object for this thread
    _ErrorInfo.ClearErrorInfo();

    // Intialize Buffer
    if( ppRowset )
        *ppRowset = NULL;

    if ( 0 != pUnkOuter && IID_IUnknown != riid )
        return _ErrorInfo.PostHResult( DB_E_NOAGGREGATION, IID_IOpenRowset );
    
    // Check Arguments
    if( (!pTableID && !pIndexID) )
        return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IOpenRowset );

    // We only accept NULL for pIndexID
    if( pIndexID )
        return _ErrorInfo.PostHResult( DB_E_NOINDEX, IID_IOpenRowset );

    // If the eKind is not known to use, basically it
    // means we have no table identifier
    if( (!pTableID) ||
        (pTableID->eKind != DBKIND_NAME) ||
        ((pTableID->eKind == DBKIND_NAME) && (!(pTableID->uName.pwszName))) ||
        (wcslen(pTableID->uName.pwszName) == 0) )
        return _ErrorInfo.PostHResult( DB_E_NOTABLE, IID_IOpenRowset );

    // We do not allow the riid to be IID_NULL
    if( riid == IID_NULL )
        return _ErrorInfo.PostHResult( E_NOINTERFACE, IID_IOpenRowset );


    TRANSLATE_EXCEPTIONS;
    TRY
    {
        // Serialize access to this object.
        CLock lck( _mtxSess );

        XInterface<ICommandText>    xICmdText;
        IUnknown *                  pUnkInner;

        // Check Arguments for use by properties
        CUtlProps::SetPropertiesArgChk( cPropertySets, rgPropertySets );

        //
        // pUnkOuter is the outer unkown from the user on the Session 
        // object.  Don't use pUnkOuter for the Command object here.
        //
        XInterface<CQuerySpec>  xQuery(
            new CQuerySpec(0, &pUnkInner, this) );

        // Tell the command object to post errors as IOpenRowset
        xQuery->ImpersonateOpenRowset();

        // Construct and set Command.  Allocate buffer for SQL Statement
        XArray<WCHAR> xwszBuff( wcslen(pTableID->uName.pwszName) + APPROX_CCH_BASE_SELECT );

        //@devnote: swprintf not supported on win95?
        swprintf( xwszBuff.Get(), BASE_SELECT, pTableID->uName.pwszName );   

        sc = pUnkInner->QueryInterface( IID_ICommandText, 
                                        xICmdText.GetQIPointer() );
        if( SUCCEEDED(sc) )
        {
            Win4Assert( !xICmdText.IsNull() );

            sc = xICmdText->SetCommandText( DBGUID_SQL, xwszBuff.Get() );

            // Process properties
            if ( SUCCEEDED(sc) && cPropertySets > 0)
            {
                sc = SetOpenRowsetProperties(xICmdText.GetPointer(), 
                                            cPropertySets, rgPropertySets);
                scProp = sc;        // Save this retcode.
            }

            // Execute the SQL Statement if we were given a ppRowset
            if ( SUCCEEDED(sc) && ppRowset )
            {
                sc = xICmdText->Execute( pUnkOuter, riid, 0, 0, ppRowset );
                if ( DB_E_ERRORSOCCURRED == sc && (cPropertySets > 0) )
                {
                    sc = MarkOpenRowsetProperties((xICmdText.GetPointer()), 
                                            cPropertySets, rgPropertySets);
                }
            }
        }
    
        sc = (sc == S_OK) ? scProp : sc;
        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IOpenRowset );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:     CDBSession::SetOpenRowsetProperties, private
//
//  Synopsis:   Loop through the passed in properties andmark those in error.
//              Used by OpenRowset()
//
//  History:    10-31-97        briants      Created
//
//----------------------------------------------------------------------------

SCODE CDBSession::SetOpenRowsetProperties(
    ICommandText*   pICmdText,
    ULONG       cPropertySets,
    DBPROPSET   rgPropertySets[]
    )
{
    Win4Assert( pICmdText != NULL );
    XInterface<ICommandProperties>  xICmdProp;

    SCODE sc = pICmdText->QueryInterface( IID_ICommandProperties, 
                               (void **) xICmdProp.GetQIPointer() );
    if( SUCCEEDED(sc) )
    {
        Win4Assert( !xICmdProp.IsNull() );

        sc = xICmdProp->SetProperties( cPropertySets, rgPropertySets );
        if ( (DB_E_ERRORSOCCURRED == sc) || (DB_S_ERRORSOCCURRED == sc) )
        {
            // If all the properties set were OPTIONAL then we can set our status to 
            // DB_S_ERRORSOCCURED and continue
            for(ULONG ul=0;ul<cPropertySets; ul++)
            {
                for(ULONG ul2=0;ul2<rgPropertySets[ul].cProperties; ul2++)
                {
                    // Check for a required property that failed, if found, we must return
                    // DB_E_ERRORSOCCURRED
                    if( (rgPropertySets[ul].rgProperties[ul2].dwStatus != DBPROPSTATUS_OK) &&
                        (rgPropertySets[ul].rgProperties[ul2].dwOptions != DBPROPOPTIONS_OPTIONAL) )
                        return DB_E_ERRORSOCCURRED;
                }
            }
            sc = DB_S_ERRORSOCCURRED;
        }
    }

    return sc;
}


//+---------------------------------------------------------------------------
//
//  Member:     CDBSession::MarkOpenRowsetProperties, private
//
//  Synopsis:   Loop through the passed in properties andmark those in error.
//              Used by OpenRowset()
//
//  History:    10-31-97    briants     Created
//
//----------------------------------------------------------------------------

SCODE CDBSession::MarkOpenRowsetProperties(
    ICommandText* pICmdText,
    ULONG       cPropertySets,
    DBPROPSET   rgPropertySets[]
    )
{
    Win4Assert( pICmdText != NULL );
    XInterface<ICommandProperties>  xICmdProp;

    DBPROPSET *             pPropSets = 0;
    ULONG                   cPropSets = 0;
    DBPROPIDSET             dbPropIdSet[1];

    dbPropIdSet[0].guidPropertySet  = DBPROPSET_PROPERTIESINERROR;
    dbPropIdSet[0].cPropertyIDs     = 0;
    dbPropIdSet[0].rgPropertyIDs    = 0;

    SCODE sc = pICmdText->QueryInterface( IID_ICommandProperties, 
                               (void **) xICmdProp.GetQIPointer() );
    if( SUCCEEDED(sc) )
    {
        Win4Assert( !xICmdProp.IsNull() );
        sc = xICmdProp->GetProperties( 1, 
                                   dbPropIdSet, 
                                   &cPropSets, 
                                   &pPropSets );
        if( SUCCEEDED(sc) )
        {
            XArrayOLEInPlace<CDbPropSet>   xPropSets;
            xPropSets.Set( cPropSets, (CDbPropSet *)pPropSets );
            
            // Loop through all the properties in error and see if one 
            // of the passed in properties matches. If it matches, then 
            // transfer the in error status.
            for(ULONG iSet=0; iSet<cPropSets; iSet++)
            {
                if( 0 == xPropSets[iSet].rgProperties ||
                    0 == xPropSets[iSet].cProperties )
                    continue;

                for(ULONG iProp=0; iProp<xPropSets[iSet].cProperties; iProp++)
                {
                    MarkPropInError( cPropertySets, 
                                     rgPropertySets, 
                                     &(xPropSets[iSet].guidPropertySet),
                                     &(xPropSets[iSet].rgProperties[iProp]) );
            
                    // Clear variant value
                    VariantClear(&(xPropSets[iSet].rgProperties[iProp].vValue));
                }
                // Free the memory as we go through them
//              CoTaskMemFree(xPropSets[iSet].rgProperties);
            }
        }
    }

    return sc;
}           

//+---------------------------------------------------------------------------
//
//  Member:     CDBSession::MarkPropInError, private
//
//  Synopsis:   Loop through the passed in properties andmark those in error.
//              Used by OpenRowset()
//
//  History:    10-31-97        danleg      Created
//
//----------------------------------------------------------------------------

void CDBSession::MarkPropInError
    (
    ULONG       cPropertySets,
    DBPROPSET*  rgPropertySets,
    GUID*       pguidPropSet,
    DBPROP*     pProp
    )
{
    ULONG iSet, iProp;

    Win4Assert( rgPropertySets );

    for(iSet=0; iSet<cPropertySets; iSet++)
    {
        if( (rgPropertySets[iSet].guidPropertySet != *pguidPropSet) ||
            (0 == rgPropertySets[iSet].rgProperties) ||
            (0 == rgPropertySets[iSet].cProperties) )
            continue;

        for(iProp=0; iProp<rgPropertySets[iSet].cProperties; iProp++)
        {
            if( (rgPropertySets[iSet].rgProperties[iProp].dwPropertyID == pProp->dwPropertyID) &&
                (rgPropertySets[iSet].rgProperties[iProp].dwStatus == DBPROPSTATUS_OK) )
            {
                rgPropertySets[iSet].rgProperties[iProp].dwStatus = pProp->dwStatus;
            }
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CImpersonateSessionUser::CImpersonateSessionUser, public
//
//  Purpose:    ctor
//
//  History:    01-23-99    danleg      Created
//
//----------------------------------------------------------------------------

CImpersonateSessionUser::CImpersonateSessionUser( HANDLE hToken ) :
    _fImpersonated( FALSE ),
    _xSessionToken( INVALID_HANDLE_VALUE ),
    _xPrevToken( INVALID_HANDLE_VALUE )
{
    if ( INVALID_HANDLE_VALUE != hToken )
    {
        HANDLE hTempToken = DupToken( hToken );
        if ( INVALID_HANDLE_VALUE != hTempToken )
            _xSessionToken.Set( hTempToken );

        CachePrevToken();
        Impersonate();
    }
} //CImpersonateSessionUser

//+---------------------------------------------------------------------------
//
//  Member:     CImpersonateSessionUser::~CImpersonateSessionUser, public
//
//  Synopsis:   dtor
//
//  History:    01-23-99    danleg      Created
//
//----------------------------------------------------------------------------

CImpersonateSessionUser::~CImpersonateSessionUser()
{
    TRY
    {
        Revert();
    }
    CATCH( CException, e )
    {
        //
        // Ignore failures in unwind paths -- the query will fail.  If we
        // can't revert here the ole db client has to realize the thread
        // may be in a bad state after a query failure.
        //
    }
    END_CATCH

    BOOL fSuccess = TRUE;
} //~CImpersonateSessionUser

//+---------------------------------------------------------------------------
//
//  Member:     CImpersonateSessionUser::Revert, public
//
//  Synopsis:   Reverts the thread to the original state
//
//  History:    02-11-02    dlee      Created from ~, so we have a form that
//                                    can fail.
//
//----------------------------------------------------------------------------

void CImpersonateSessionUser::Revert()
{
    BOOL fSuccess = TRUE;

    if ( INVALID_HANDLE_VALUE == _xPrevToken.Get() )
    {
        //
        // There is no need to revert to self here if we didn't impersonate
        // in the first place -- if there was no token or there was no
        // session object.  If you revert here then IIS threads become
        // system.
        //

        if ( _fImpersonated )
        {
            fSuccess = RevertToSelf();

            if ( fSuccess )
                _fImpersonated = FALSE;
        }
    }
    else
    {
        fSuccess = ImpersonateLoggedOnUser( _xPrevToken.Get() );

        _xPrevToken.Free();
    }

    if ( !fSuccess )
    {
        DWORD dwError = GetLastError();

        vqDebugOut(( DEB_ERROR, 
                     "CImpersonateSessionUser::Revert: Impersonation failed with error %d\n",
                     dwError ));

        THROW( CException( HRESULT_FROM_WIN32( dwError ) ) );
    }
} //Revert

//+---------------------------------------------------------------------------
//
//  Member:     CImpersonateSessionUser:: DupToken, private
//
//  Synopsis:   Duplicate the session token for the current thread
//
//  History:    01-23-99    danleg      Created
//
//----------------------------------------------------------------------------

HANDLE CImpersonateSessionUser::DupToken( HANDLE hToken )
{
    SECURITY_QUALITY_OF_SERVICE qos;
    qos.Length              = sizeof( SECURITY_QUALITY_OF_SERVICE  );
    qos.ImpersonationLevel  = SecurityImpersonation;
    qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    qos.EffectiveOnly       = FALSE;

    OBJECT_ATTRIBUTES           ObjAttr;
    InitializeObjectAttributes( &ObjAttr,
                                NULL,
                                0,
                                NULL,
                                NULL );
    ObjAttr.SecurityQualityOfService = &qos;

    HANDLE hNewToken = INVALID_HANDLE_VALUE;
    NTSTATUS status = NtDuplicateToken( hToken,
                                        TOKEN_IMPERSONATE|TOKEN_QUERY,
                                        &ObjAttr,
                                        FALSE,
                                        TokenImpersonation,
                                        &hNewToken );

    if ( !NT_SUCCESS(status) )
    {
        vqDebugOut(( DEB_ERROR,
                     "DupToken failed to duplicate token, %x\n",
                     status ));

        THROW( CException( status ) );
    }

    return hNewToken;
} //DupToken

//+---------------------------------------------------------------------------
//
//  Member:     CImpersonateSessionUser:: CachePrevToken, private
//
//  Synopsis:   If the current thread is already impersonated, cache its
//              impersonation token so it can be restored later.
//
//  History:    01-23-99    danleg      Created
//
//----------------------------------------------------------------------------

void CImpersonateSessionUser::CachePrevToken()
{
    DWORD               dwLength;
    TOKEN_STATISTICS    TokenInformation;
    HANDLE              hToken = INVALID_HANDLE_VALUE;

    NTSTATUS status = NtOpenThreadToken( GetCurrentThread(),
                                         TOKEN_QUERY |
                                           TOKEN_DUPLICATE |
                                           TOKEN_IMPERSONATE,
                                         TRUE,
                                         &hToken);
    if ( NT_SUCCESS(status) )
    {
        SHandle xHandle( hToken );

        // 
        // If this thread is already impersonated, cache its impersonation
        // token and impersonate using the session (i.e. logon) token
        //
        status = NtQueryInformationToken ( hToken,
                                           TokenStatistics,
                                           (LPVOID)&TokenInformation,
                                           sizeof TokenInformation,
                                           &dwLength);

        if ( NT_SUCCESS(status) )
        {
            if ( TokenInformation.TokenType == TokenImpersonation )
            {
                HANDLE hTempToken = DupToken( hToken );

                if ( INVALID_HANDLE_VALUE != hTempToken )
                    _xPrevToken.Set( hTempToken );
            }
        }
        else // NtQueryInformation failed
        {
            vqDebugOut(( DEB_ERROR, 
                         "CImpersonateSessionUser failed to query token information, %x\n",
                         status ));

            THROW( CException( status ) );
        }
    }
    else // NtOpenThreadToken failed
    {
        //
        // If it's STATUS_NO_TOKEN then there isn't anything to capture and we
        // can ignore impersonation for this query.
        //

        if ( STATUS_NO_TOKEN != status )
        {
            vqDebugOut(( DEB_ERROR,
                         "CImpersonateSessionUser failed to open thread token, %x\n",
                         status ));

            THROW( CException( status ) );
        }
    }
} //CachePrevToken

//+---------------------------------------------------------------------------
//
//  Member:     CImpersonateSessionUser::Impersonate, private
//
//  Synopsis:   Impersonate the user who created the OLE DB session.
//
//  History:    01-23-99    danleg      Created
//
//----------------------------------------------------------------------------

void CImpersonateSessionUser::Impersonate()
{
    if ( INVALID_HANDLE_VALUE == _xSessionToken.Get() )
        return;

    BOOL fSuccess = ImpersonateLoggedOnUser( _xSessionToken.Get() );

    if ( fSuccess )
        _fImpersonated = TRUE;
    else
    {
        DWORD dwError = GetLastError();
        vqDebugOut(( DEB_ERROR, 
                     "CImpersonateSessionUser failed to impersonate, %d\n",
                     dwError ));

        THROW( CException( HRESULT_FROM_WIN32( dwError ) ) );
    }
} //Impersonate