//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 2000.
//
//  File:       datasrc.cxx
//
//  Contents:   Class factory description
//
//  History:    3-30-97     MohamedN   Created
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <datasrc.hxx>
#include <session.hxx>
#include <cidbprop.hxx>     // CDbProperties
#include <dbprpini.hxx>     // CGetDbInitProps

// Datasource object interfaces that support ISupportErrorInfo
static const GUID* apDataSrcErrInt[] =
{
    &IID_IDBCreateSession,
    &IID_IDBInitialize,
    &IID_IDBProperties,
    &IID_IPersist,
    &IID_IDBInfo,
};
static const ULONG cDataSrcErrInt  = NUMELEM( apDataSrcErrInt );

//+---------------------------------------------------------------------------
//
//  Method:     CDataSrc::CDataSrc
//
//  Synopsis:   ctor
//
//  Arguments:  pUnkOuter   - outer unknown
//
//  History:    3-30-97     mohamedn    Created
//             10-05-97     danleg      _ErrorInfo
//             10-30-97     danleg      Prop info for Initialize/Uninitialize
//  Notes:
//
//----------------------------------------------------------------------------

CDataSrc::CDataSrc( IUnknown *  pUnkOuter,
                    IUnknown ** ppUnkInner )
                                          : _cSessionCount(0),
                                            _fDSOInitialized(FALSE),
                                            _fGlobalViewsCreated(FALSE),
                                            _xIPVerify(new CImpIParserVerify()),
                                            _UtlProps(_xIPVerify.GetPointer()),
#pragma warning(disable : 4355)
                                            _impIUnknown(this),
                                            _ErrorInfo( * ((IUnknown *) (IDBInitialize *) this), _mtxDSO )
#pragma warning(default : 4355)
{
    _pUnkOuter = pUnkOuter ? pUnkOuter : (IUnknown *) &_impIUnknown;
    _ErrorInfo.SetInterfaceArray( cDataSrcErrInt, apDataSrcErrInt );

    // SQL Text Parser
    // @devnote: The following is allocated since its existence is controlled
    // by AddRef and Release.
    SCODE sc = MakeIParser(((IParser**)_xIParser.GetQIPointer()));
    if( FAILED(sc) )
        THROW( CException(sc) );

    _UtlPropInfo.ExposeMinimalSets();
    _UtlProps.ExposeMinimalSets();

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


//+---------------------------------------------------------------------------
//
//  Method:     CDataSrc::~CDataSrc
//
//  Synopsis:   d-ctor
//
//  Arguments:
//
//  History:    3-30-97     mohamedn    Created
//             10-30-97     danleg      Prop info for Initialize/Uninitialize
//----------------------------------------------------------------------------

CDataSrc::~CDataSrc()
{
    _UtlPropInfo.ExposeMaximalSets();
    _UtlProps.ExposeMaximalSets();
}


//+---------------------------------------------------------------------------
//
//  Member:     CDataSrc::RealQueryInterface
//
//  Synopsis:   Supports IID_IUnknown,
//                       IID_IDBInitialize,
//                       IID_IDBProperties,
//                       IID_IDBIPersist,
//                       IID_IDBCreateSession
//                       IID_IDBInfo
//
//  History:    03-30-97    mohamedn    created
//              09-05-97    danleg      added IDBInfo & ISupportErrorInfo
//              01-29-98    danleg      non delegating QI when not aggregated
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::RealQueryInterface(REFIID riid, void **ppvObj )
{

    SCODE sc = S_OK;

    if ( !ppvObj )
    return E_INVALIDARG;


    if ( riid == IID_IUnknown )
    {
        *ppvObj = (void *)  ( (IUnknown *) (IDBInitialize *) this );
    }
    else if ( riid == IID_IDBInitialize )
    {
        *ppvObj = (void *) (IDBInitialize *) this;
    }
    else if ( riid == IID_IDBProperties )
    {
        *ppvObj = (void *) (IDBProperties *) this;
    }
    else if ( riid == IID_IPersist )
    {
        *ppvObj = (void *) (IPersist *) this;
    }
    else if ( riid == IID_IDBCreateSession )
    {
        //
        // The following interfaces are supported only if DSO is initialized
        //

        // Make sure we don't get uninitialized
        if ( _fDSOInitialized )
        {
            *ppvObj = (void *) (IDBCreateSession *) this;
        }
        else
        {
            *ppvObj = 0;
            sc = E_UNEXPECTED;    // per OLE DB spec.
        }
    }
    else if ( riid == IID_IDBInfo )
    {
        *ppvObj = (void *) (IDBInfo *) this;
    }
    else if ( riid == IID_ISupportErrorInfo )
    {
        *ppvObj = (void *) ((IUnknown *) (ISupportErrorInfo *) &_ErrorInfo);
    }
    else
    {
        *ppvObj = 0;
        sc = E_NOINTERFACE;
    }

    return sc;

} // QueryInterface

//+---------------------------------------------------------------------------
//
//  Method:     CDataSrc::Initialize
//
//  Synopsis:   changes the DSO state to Initialized.
//
//  Arguments:
//
//  History:    3-30-97     mohamedn    Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::Initialize()
{

    SCODE sc = S_OK;

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

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        CLock lck( _mtxDSO );

        if ( !_fDSOInitialized )
        {
            // Expose non-init propsets
            sc = _UtlProps.ExposeMaximalSets();
            if ( SUCCEEDED(sc) )
            {
                // Expose propinfo for non-init propsets
                _UtlPropInfo.ExposeMaximalSets();

                // OK, now we're initialized.
                _fDSOInitialized = TRUE;
            }
            else
            {
                THROW( CException(sc) );
            }
        }
        else
        {
            THROW( CException(DB_E_ALREADYINITIALIZED) );
        }
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBInitialize );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}


//+---------------------------------------------------------------------------
//
//  Method:     CDataSrc::Uninitialize
//
//  Synopsis:   changes the DSO state to Uninitialized.
//
//  Arguments:
//
//  History:    3-30-97     mohamedn    Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::Uninitialize()
{
    SCODE sc = S_OK;

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

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        CLock lck( _mtxDSO );

        if ( 0 == _cSessionCount )
        {
            // Hide non-init propsets
            _UtlProps.ExposeMinimalSets();

            _UtlPropInfo.ExposeMinimalSets();

            // Mark DSO as uninitialized
            _fDSOInitialized = FALSE;
        }
        else
        {
            THROW( CException(DB_E_OBJECTOPEN) );
        }
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBInitialize );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}


//+-------------------------------------------------------------------------
//
//  Function:   CDataSrc::GetProperties
//
//  Synopsis:   gets IDBProperties
//
//  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:     3-30-97    mohamedn    Created
//              10-30-97    danleg      Chaned to use the Monarch prop code
//
//--------------------------------------------------------------------------

STDMETHODIMP CDataSrc::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( _mtxDSO );

        // Check Arguments
        _UtlProps.GetPropertiesArgChk( cPropertySets,
                                       rgPropertySets,
                                       pcProperties,
                                       prgProperties );

        // Note that CUtlProps knows about initialization,
        // so we don't have to here.
        sc = _UtlProps.GetProperties( cPropertySets,
                                      rgPropertySets,
                                      pcProperties,
                                      prgProperties );
        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBProperties );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   CDataSrc::SetProperties
//
//  Synopsis:   sets IDBProperties
//
//  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-30-97    danleg          Changed to use Monarch's prop code
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::SetProperties
    (
    ULONG cPropertySets,
    DBPROPSET rgPropertySets[]
    )
{
    // Clear previous Error Object for this thread
    _ErrorInfo.ClearErrorInfo();


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

    SCODE               sc = S_OK;

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        XArray<DBPROPSET>   xaDbPropSet;
        ULONG               iNewSet, iSet, iProp;

        CLock lck( _mtxDSO );

        _UtlProps.SetPropertiesArgChk( cPropertySets, rgPropertySets );

        // We need to handle the DBINIT properties specially after being initialized.
        // - they should be treated as NOTSETTABLE at this point.
        if( _fDSOInitialized )
        {
            Win4Assert( cPropertySets );

            bool fFoundDBINIT = false;

            // Allocate a DBPROPSET structure of equal size
            xaDbPropSet.Init( cPropertySets );

            for( iNewSet=0,iSet=0; iSet<cPropertySets; iSet++ )
            {
                // Remove any DBPROPSET_DBINIT values and mark them all
                // as not settable
                if( rgPropertySets[iSet].guidPropertySet == DBPROPSET_DBINIT )
                {
                    fFoundDBINIT = true;

                    for(iProp=0; iProp<rgPropertySets[iSet].cProperties; iProp++)
                    {
                        rgPropertySets[iSet].rgProperties[iProp].dwStatus = DBPROPSTATUS_NOTSETTABLE;
                    }
                }
                else
                {
                    // If not DBPROPSET_DBINIT then copy the DBPROPSET values
                    RtlCopyMemory( &(xaDbPropSet[iNewSet++]), &rgPropertySets[iSet], sizeof(DBPROPSET) );
                }
            }

            // If we have no propertyset to pass on to the property handler,
            // we can exit
            if( 0 == iNewSet )
            {
                sc = DB_E_ERRORSOCCURRED;
            }
            else
            {
                sc = _UtlProps.SetProperties( iNewSet, xaDbPropSet.GetPointer() );

                // If we have determined that one of the property sets was DBINIT, we may
                // need to fixup the returned hr value.
                if( fFoundDBINIT && (sc == S_OK) )
                    sc = DB_S_ERRORSOCCURRED;
            }
        }
        else
        {
            // Note that CUtlProps knows about initialization, so we don't
            // have to here. This sets members _bstrCatalog and _bstrMachine
            // in CMDSProps
            sc = _UtlProps.SetProperties( cPropertySets, rgPropertySets );
        }

        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBProperties );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
} // CDatasrc::SetProperties


//+---------------------------------------------------------------------------
//
//  Function:   CDataSrc::GetPropertyInfo
//
//  Synopsis:   sets IDBProperties
//
//  Arguments:  [cPropertyIDSets]       - number of property sets
//              [rgPropertyIDSets]      - array of property sets
//              [pcPropertyInfoSets]    - count of properties returned
//              [prgPropertyInfoSets]   - property information returned
//              [ppDescBuffer]          - buffer for returned descriptions
//
//  Returns:
//
//  History:    10-28-97    danleg              created from Monarch
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::GetPropertyInfo
    (
    ULONG             cPropertyIDSets,
    const DBPROPIDSET rgPropertyIDSets[],
    ULONG *           pcPropertyInfoSets,
    DBPROPINFOSET **  prgPropertyInfoSets,
    OLECHAR **        ppDescBuffer
    )
{
    ULONG       ul;
    ULONG       cSpecialPropertySets = 0;

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

    // Initialize
    if( pcPropertyInfoSets )
        *pcPropertyInfoSets     = 0;
    if( prgPropertyInfoSets )
        *prgPropertyInfoSets    = 0;
    if( ppDescBuffer )
        *ppDescBuffer           = 0;

    // Check Arguments, on failure post HRESULT to error queue
    if( ((cPropertyIDSets > 0) && !rgPropertyIDSets ) ||
        !pcPropertyInfoSets || !prgPropertyInfoSets )
        return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IDBProperties );

    // New argument check for > 1 cPropertyIDs and NULL pointer for
    // array of property ids.
    for(ul=0; ul<cPropertyIDSets; ul++)
    {
        if( rgPropertyIDSets[ul].cPropertyIDs &&
            !(rgPropertyIDSets[ul].rgPropertyIDs) )
            return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IDBProperties );
    }

    //check the count of special propertySets
    for(ul=0; ul<cPropertyIDSets; ul++)
    {
        if( (rgPropertyIDSets[ul].guidPropertySet ==DBPROPSET_DATASOURCEALL) ||
            (rgPropertyIDSets[ul].guidPropertySet ==DBPROPSET_DATASOURCEINFOALL) ||
            (rgPropertyIDSets[ul].guidPropertySet ==DBPROPSET_DBINITALL) ||
            (rgPropertyIDSets[ul].guidPropertySet ==DBPROPSET_ROWSETALL) ||
            (rgPropertyIDSets[ul].guidPropertySet ==DBPROPSET_SESSIONALL) )
            cSpecialPropertySets++;
    }

    // if used SpecialPropertySets with non-special Propertysets
    if ((cSpecialPropertySets > 0) && (cSpecialPropertySets < cPropertyIDSets))
    {
        return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IDBProperties );
    }

    SCODE sc = S_OK;

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        CLock lck( _mtxDSO );

        sc =  _UtlPropInfo.GetPropertyInfo( cPropertyIDSets,
                                            rgPropertyIDSets,
                                            pcPropertyInfoSets,
                                            prgPropertyInfoSets,
                                            ppDescBuffer );
        if ( FAILED(sc) )
            THROW( CException(sc) );

    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBProperties );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Method:     CDataSrc::CreateGlobalViews
//
//  Synopsis:   Creates global views (FILEINFO, WEBINFO etc.).  These views
//              are removed when the IParser object goes away.
//
//  Arguments:
//
//  History:    01-09-98    danleg      Created
//
//----------------------------------------------------------------------------

void CDataSrc::CreateGlobalViews( IParserSession * pIPSession )
{
    SCODE sc = S_OK;
extern const LPWSTR s_pwszPredefinedViews;

    DBCOMMANDTREE * pDBCOMMANDTREE = 0;
    XInterface<IParserTreeProperties> xIPTProperties;

    LCID lcid = GetDSPropsPtr()->GetValLong( CMDSProps::eid_DBPROPSET_DBINIT,
                                             CMDSProps::eid_INIT_LCID );

    sc = pIPSession->ToTree( lcid,
                             s_pwszPredefinedViews,
                             &pDBCOMMANDTREE,
                             xIPTProperties.GetPPointer() );

    if ( FAILED(sc) )
        THROW( CException(sc) );

    _fGlobalViewsCreated = TRUE;
}

//+---------------------------------------------------------------------------
//
//  Method:     CDataSrc::DupImpersonationToken, public
//
//  Synopsis:   Clients calling CreateSession can be impersonated.  One such
//              client, SQL Server's Distributed Query Processor, stays 
//              impersonated only for the duration of the call to CreateSession.
//              
//              This routine is called from CreateSession and caches the 
//              impersonation token.  This token is used to get back the security
//              context of the client during CreateCommand/OpenRowset.
//
//  Arguments:  
//
//  History:    09-01-98        danleg          Created
//
//  Notes:      Revisit if OLE DB defines "Integrated Security" differently in
//              the future, or defines a better security scheme.
//
//----------------------------------------------------------------------------

void CDataSrc::DupImpersonationToken
    (
    HANDLE & hToken
    )
{
    DWORD               dwLength = 0;
    NTSTATUS            status = STATUS_SUCCESS;
    TOKEN_STATISTICS    TokenInformation;
        HANDLE                          hTempToken;

    status = NtOpenThreadToken( GetCurrentThread(),
                                TOKEN_QUERY |
                                  TOKEN_DUPLICATE |
                                  TOKEN_IMPERSONATE,
                                TRUE,
                                &hTempToken );

    if ( !NT_SUCCESS(status) )
    {   
        if ( STATUS_NO_TOKEN == status )
        {
            status = NtOpenProcessToken( GetCurrentProcess(),
                                         TOKEN_QUERY |
                                            TOKEN_DUPLICATE |
                                            TOKEN_IMPERSONATE,
                                         &hTempToken );
        }
        
        if ( !NT_SUCCESS(status) )
        {
            vqDebugOut(( DEB_ERROR, 
                         "DupImpersonationToken failed to get token, %x\n",
                         status ));
            THROW( CException(status) );
        }
    }

        SHandle xHandle( hTempToken );

    HANDLE                      hNewToken = INVALID_HANDLE_VALUE;
    OBJECT_ATTRIBUTES           ObjAttr;
    SECURITY_QUALITY_OF_SERVICE qos;

    qos.Length              = sizeof( SECURITY_QUALITY_OF_SERVICE  );
    qos.ImpersonationLevel  = SecurityImpersonation;
    qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    qos.EffectiveOnly       = FALSE;

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

    status = NtDuplicateToken( hTempToken,
                               TOKEN_IMPERSONATE |
                                 TOKEN_QUERY |
                                 TOKEN_DUPLICATE,
                               &ObjAttr,
                               FALSE,
                               TokenImpersonation,
                               &hNewToken );

    if ( !NT_SUCCESS(status) )
    {
        vqDebugOut(( DEB_ERROR,
                     "DupImpersonationToken failed to duplicate token, %x\n",
                     status ));
        THROW( CException(HRESULT_FROM_WIN32(status)) );
    }

    hToken = hNewToken;
}

//+---------------------------------------------------------------------------
//
//  Method:     CDataSrc::CreateSession
//
//  Synopsis:   Associates a session with the DSO.
//
//  Arguments:  [pUnkOuter]     - controlling unknown
//              [riid]          - interface requested
//              [ppDBSession]   - contains returned interface pointer
//
//  History:    3-30-97     mohamedn    Created
//              1-10-98     danleg      Added global views
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::CreateSession( IUnknown *   pUnkOuter,
                                      REFIID       riid,
                                      IUnknown **  ppDBSession )
{
    _ErrorInfo.ClearErrorInfo();

    if ( !ppDBSession )
        return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IDBCreateSession );
    else
        *ppDBSession = 0;

    if ( !_fDSOInitialized )
        return _ErrorInfo.PostHResult( E_UNEXPECTED, IID_IDBCreateSession );

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

    SCODE       sc          = S_OK;
    HANDLE      hToken      = INVALID_HANDLE_VALUE;

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        XInterface<IColumnMapperCreator>  xColMapCreator;
        XInterface<IParserSession>        xIPSession;

        CLock lck( _mtxDSO );

        DupImpersonationToken( hToken );

        //
        // Service Components set DBPROP_RESETDATASOURCE to indicate that the DSO has been
        // pooled.  We need to reset the IParser object because it maintains state valid
        // across sessions (eg. views)
        //
        LONG lResetVal = _UtlProps.GetValLong( CMDSProps::eid_DBPROPSET_DATASOURCE,
                                               CMDSProps::eid_DBPROPVAL_RESETDATASOURCE);

        if ( DBPROPVAL_RD_RESETALL == lResetVal )
        {
            // make sure there aren't any outstanding sessions when doing this.

            if ( 0 != _cSessionCount )
                THROW( CException( E_INVALIDARG ) );

            _xIParser.Free();
            sc = MakeIParser( _xIParser.GetPPointer() );
            if ( FAILED(sc) )
                THROW( CException(sc) );

            _UtlProps.SetValLong( CMDSProps::eid_DBPROPSET_DATASOURCE,
                                  CMDSProps::eid_DBPROPVAL_RESETDATASOURCE,
                                  0L );
        }

        //
        // Create an IParserSession object to pass to the session
        //
        _xIPVerify->GetColMapCreator( xColMapCreator.GetPPointer() );

        sc = _xIParser->CreateSession( &DBGUID_MSSQLTEXT,
                                       GetDSPropsPtr()->GetValString(
                                              CMDSProps::eid_DBPROPSET_DBINIT,
                                              CMDSProps::eid_DBPROPVAL_INIT_LOCATION),
                                       _xIPVerify.GetPointer(),
                                       xColMapCreator.GetPointer(),
                                       xIPSession.GetPPointer() );
        if ( FAILED(sc) )
            THROW( CException(sc) );

        LPCWSTR pwszCatalog = 0;

        pwszCatalog = GetDSPropsPtr()->GetValString(
                                    CMDSProps::eid_DBPROPSET_DATASOURCE,
                                    CMDSProps::eid_DBPROPVAL_CURRENTCATALOG);

        sc = xIPSession->SetCatalog( pwszCatalog );
        if( FAILED(sc) )
            THROW( CException(sc) );

        //
        // Predefined views -- only once per DSO
        //
        if ( !_fGlobalViewsCreated )
            CreateGlobalViews( xIPSession.GetPointer() );

        //
        // Create the session object
        //
        XInterface<IUnknown>    xUnkInner;
        CDBSession *pDBSession = new CDBSession( *this,
                                                 pUnkOuter,
                                                 xUnkInner.GetPPointer(),
                                                 xIPSession.GetPointer(),
                                                 hToken );

        // NOTE: pDBSession is the same object as xUnkInner.
        sc = xUnkInner->QueryInterface( riid, (void **) ppDBSession );
        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH(CException, e)
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBCreateSession );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Method:     CDataSrc::GetClassID
//
//  Synopsis:   Return the CLSID for this server object
//
//  Arguments:  [pClassID]
//
//  History:    3-30-97     mohamedn    Created
//             10-28-97     danleg      added _ErrorInfo
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::GetClassID    ( CLSID *pClassID )
{
    _ErrorInfo.ClearErrorInfo();

    if (pClassID)
    {
        RtlCopyMemory( pClassID, &CLSID_CiFwDSO, sizeof(CLSID) );
        return S_OK;
    }
    else
        return _ErrorInfo.PostHResult( E_FAIL, IID_IPersist );
}

//============================================================================
//  LITERAL INFO Constants
//============================================================================
// The following are constants that define literals that don't change..  When the buffer
// to return literal information is allocated, it is initialized with this string and
// then when a particular literal is asked for; a pointer in these values is used.

static const LPWSTR LIT_BUFFER                   = L"\"\0.\0%\0_\0[]\0[]\0";
static const ULONG  LIT_QUOTE_VALID_OFFSET       = 0;
static const ULONG  LIT_CATALOG_SEP_VALID_OFFSET = LIT_QUOTE_VALID_OFFSET + NUMELEM(L"\"");
static const ULONG  LIT_PERCENT_VALID_OFFSET     = LIT_CATALOG_SEP_VALID_OFFSET + NUMELEM(L".");
static const ULONG  LIT_UNDERSCORE_VALID_OFFSET  = LIT_PERCENT_VALID_OFFSET + NUMELEM(L"%");
static const ULONG  LIT_ESCAPE_PERCENT_OFFSET    = LIT_UNDERSCORE_VALID_OFFSET + NUMELEM(L"_");
static const ULONG  LIT_ESCAPE_UNDERSCORE_OFFSET = LIT_ESCAPE_PERCENT_OFFSET + NUMELEM(L"[]");
static const ULONG  LIT_CCH_INITIAL_BUFFER       = LIT_ESCAPE_UNDERSCORE_OFFSET + NUMELEM(L"[]");
static const ULONG  LIT_CB_INITIAL_BUFFER        = LIT_CCH_INITIAL_BUFFER * sizeof(WCHAR);

// List of unique Keywords that OLE DB does not define.
static const WCHAR s_pwszKeyWords[] = {L"ARRAY,COERCE,CONTAINS,DEEP,DERIVATIONAL,"
                                       L"EXCLUDE,FORMSOF,FREETEXT,INFLECTIONAL,"
                                       L"ISABOUT,MATCHES,NEAR,PARAGRAPH,PASSTHROUGH,"
                                       L"PROPERTYNAME,PROPID,RANKMETHOD,SENTENCE,"
                                       L"SCOPE,SEARCH,SHALLOW,SOUNDEX,THESAURUS,"
                                       L"TRAVERSAL,TYPE,WEIGHT,WORD"};

//+---------------------------------------------------------------------------
//
//  Function:   CDataSrc::GetKeywords
//
//  Synopsis:   returns a list of provider specific keywords
//
//  Arguments:  [ppwszKeywords]   - string containing returned list of comma
//                                  separated keywords
//
//  Returns:    HRESULT indicating the status of the method
//              S_OK | Keyword list retrieved
//              E_FAIL | Provider specific error (ODBC call failed)
//              E_OUTOFMEMORY | Buffer could not be allocated for the keywords.
//              E_INVALIDARG | Arguments did not match specification
//
//  History:    09-05-97    danleg      created from Monarch project
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::GetKeywords(LPOLESTR* ppwszKeywords)
{
    SCODE sc = S_OK;

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


    TRANSLATE_EXCEPTIONS;
    TRY
    {
        CLock lck( _mtxDSO );

        // Check arguments
        if( ppwszKeywords )
        {
            *ppwszKeywords = 0;

            // Check that object is initialized
            if( _fDSOInitialized )
            {
                XArrayOLE<WCHAR>   xpwszKeyWords( NUMELEM(s_pwszKeyWords) );
                RtlCopyMemory( xpwszKeyWords.GetPointer(),
                               s_pwszKeyWords,
                               sizeof(s_pwszKeyWords) );

                *ppwszKeywords = xpwszKeyWords.Acquire();

            }
            else
            {
                vqDebugOut(( DEB_TRACE, "Initialization must occur before IDBInfo can be called\n" ));
                sc = E_UNEXPECTED;
            }
        }
        else
        {
            sc = E_INVALIDARG;
        }

        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBInfo );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   CDataSrc::GetLiteralInfo
//
//  Synopsis:   Retreives information about literals.  Their length, and special
//              characters and whether they are actually supported.
//
//  Arguments:  [cLiterals]         - Number of literals being asked about
//              [rgLiterals]        - Array of literals being asked about
//              [pcLiteralInfo]     - Contains returned number of literals
//              [prgLiteralInfo]    - Contains returned literal information
//              [ppCharBuffer]      - Buffer for returned string values
//
//  Returns:    HRESULT indicating the status of the method
//              S_OK            | Keyword list retrieved
//              E_FAIL          | Provider specific error (ODBC call failed)
//              E_OUTOFMEMORY   | Buffer could not be allocated for the keywords.
//              E_INVALIDARG    | Arguments did not match specification
//
//  History:    09-05-97    danleg      created from Monarch project
//
//----------------------------------------------------------------------------

STDMETHODIMP CDataSrc::GetLiteralInfo
    (
    ULONG cLiterals,
    const DBLITERAL rgLiterals[],
    ULONG* pcLiteralInfo,
    DBLITERALINFO** prgLiteralInfo,
    OLECHAR**   ppCharBuffer
    )
{
    SCODE           sc = S_OK;

const DWORD LITERAL_NORESTRICTIONS      = 0x00000001;
const DWORD LITERAL_FAILURE             = 0x00000002;
const DWORD LITERAL_SUCCESS             = 0x00000004;

const static DBLITERAL s_rgSupportedLiterals[] = {
        DBLITERAL_BINARY_LITERAL, DBLITERAL_CATALOG_NAME, DBLITERAL_CATALOG_SEPARATOR,
        DBLITERAL_CHAR_LITERAL, DBLITERAL_COLUMN_NAME, DBLITERAL_CORRELATION_NAME,
        DBLITERAL_ESCAPE_PERCENT, DBLITERAL_ESCAPE_UNDERSCORE,
        DBLITERAL_LIKE_PERCENT, DBLITERAL_LIKE_UNDERSCORE, DBLITERAL_TABLE_NAME,
        DBLITERAL_TEXT_COMMAND, DBLITERAL_VIEW_NAME, DBLITERAL_QUOTE_PREFIX,
                DBLITERAL_QUOTE_SUFFIX
        };

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

    // Initialize
    if( pcLiteralInfo )
        *pcLiteralInfo  = 0;
    if( prgLiteralInfo )
        *prgLiteralInfo = 0;
    if( ppCharBuffer )
        *ppCharBuffer   = 0;

    // Check Arguments
    if( ((cLiterals > 0) && !rgLiterals) ||
        !pcLiteralInfo ||
        !ppCharBuffer ||
        !prgLiteralInfo )
    {
        return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IDBInfo );
    }

    TRANSLATE_EXCEPTIONS;
    TRY
    {
        ULONG           ulDex,
                        ulNew;
        DWORD           dwStatus = 0;
        DBLITERALINFO*  pdbLitInfo;

        CLock lck( _mtxDSO );

        // We must be initialized
        if( !_fDSOInitialized )
        {
            vqDebugOut(( DEB_TRACE, "Initialization must occur before IDBInfo can be called\n" ));
            return _ErrorInfo.PostHResult( E_UNEXPECTED, IID_IDBInfo );
        }

        // Allocate Memory for literal information
        if( cLiterals == 0 )
        {
            dwStatus |= LITERAL_NORESTRICTIONS;
            cLiterals = NUMELEM( s_rgSupportedLiterals );
            rgLiterals = s_rgSupportedLiterals;
        }

        XArrayOLE<DBLITERALINFO>        xaLiteralInfo( cLiterals );
        XArrayOLE<WCHAR>                xaCharBuffer( LIT_CCH_INITIAL_BUFFER );

        // Initialize the first part of the buffer with our
        // static set of literal information
        RtlCopyMemory( xaCharBuffer.GetPointer(), LIT_BUFFER, LIT_CB_INITIAL_BUFFER );

        // Process each of the DBLITERAL values that are in the
        // restriction array or that we potentially could support
        for(ulDex=0, ulNew=0; ulDex<cLiterals; ulDex++)
        {
            pdbLitInfo                           = &(xaLiteralInfo[ulNew]);
            pdbLitInfo->lt                       = rgLiterals[ulDex];
            pdbLitInfo->fSupported               = TRUE;
            pdbLitInfo->pwszLiteralValue         = 0;
            pdbLitInfo->pwszInvalidChars         = 0;
            pdbLitInfo->pwszInvalidStartingChars = 0;

            switch( rgLiterals[ulDex] )
            {
                case DBLITERAL_TEXT_COMMAND:
                case DBLITERAL_CHAR_LITERAL:
                case DBLITERAL_BINARY_LITERAL:
                case DBLITERAL_TABLE_NAME:
                    pdbLitInfo->cchMaxLen = ~0;
                    break;

                case DBLITERAL_CATALOG_NAME:
                case DBLITERAL_COLUMN_NAME:
                case DBLITERAL_CORRELATION_NAME:
                case DBLITERAL_VIEW_NAME:
                    pdbLitInfo->cchMaxLen = 128;
                    break;


                case DBLITERAL_CATALOG_SEPARATOR:
                    pdbLitInfo->cchMaxLen = 1;      // L'.';
                    pdbLitInfo->pwszLiteralValue = xaCharBuffer.GetPointer() + LIT_CATALOG_SEP_VALID_OFFSET;
                    break;
                case DBLITERAL_ESCAPE_PERCENT:
                    pdbLitInfo->cchMaxLen = 2;      // L"[]";
                    pdbLitInfo->pwszLiteralValue = xaCharBuffer.GetPointer() + LIT_ESCAPE_PERCENT_OFFSET;
                    break;
                case DBLITERAL_ESCAPE_UNDERSCORE:
                    pdbLitInfo->cchMaxLen = 2;      // L"[]";
                    pdbLitInfo->pwszLiteralValue = xaCharBuffer.GetPointer() + LIT_ESCAPE_UNDERSCORE_OFFSET;
                    break;
                case DBLITERAL_LIKE_PERCENT:
                    pdbLitInfo->cchMaxLen = 1;      // L'%';
                    pdbLitInfo->pwszLiteralValue = xaCharBuffer.GetPointer() + LIT_PERCENT_VALID_OFFSET;
                    break;
                case DBLITERAL_LIKE_UNDERSCORE:
                    pdbLitInfo->cchMaxLen = 1;      // L'_';
                    pdbLitInfo->pwszLiteralValue = xaCharBuffer.GetPointer() + LIT_UNDERSCORE_VALID_OFFSET;
                    break;
                case DBLITERAL_QUOTE_PREFIX:
                                case DBLITERAL_QUOTE_SUFFIX:
                    pdbLitInfo->cchMaxLen = 1;      // L'"';
                    pdbLitInfo->pwszLiteralValue = xaCharBuffer.GetPointer() + LIT_QUOTE_VALID_OFFSET;
                    break;
                default:
                    pdbLitInfo->cchMaxLen = 0;
                    // If we are given a dbLiteral that we do not
                    // support, just set the fSupport flag false
                    // and continue on.
                    pdbLitInfo->fSupported = FALSE;
                    break;
            }

            // If we are returning all the supported literals, then
            // we need to drop any that are fSupported = FALSE;
            if( dwStatus & LITERAL_NORESTRICTIONS )
            {
                if( pdbLitInfo->fSupported == FALSE )
                    continue;
            }
            else
            {
                if( pdbLitInfo->fSupported == FALSE )
                    dwStatus |= LITERAL_FAILURE;
                else
                    dwStatus |= LITERAL_SUCCESS;
            }

            ulNew++;
        }

        sc = (dwStatus & LITERAL_FAILURE) ?
                ((dwStatus & LITERAL_SUCCESS) ? DB_S_ERRORSOCCURRED : DB_E_ERRORSOCCURRED) :
                S_OK;

        *pcLiteralInfo  = ulNew;

        // We only want to return the string buffer if it
        // is a success
        if ( SUCCEEDED(sc) )
            *ppCharBuffer   = xaCharBuffer.Acquire();

        // We want to return the LiteralInfo on success and on
        // a DB_E_ERRORSOCCURRED failure
        if ( SUCCEEDED(sc) || (sc == DB_E_ERRORSOCCURRED) )
            *prgLiteralInfo = xaLiteralInfo.Acquire();

        if ( FAILED(sc) )
            THROW( CException(sc) );
    }
    CATCH( CException, e )
    {
        sc = _ErrorInfo.PostHResult( e, IID_IDBInfo );
    }
    END_CATCH;
    UNTRANSLATE_EXCEPTIONS;

    return sc;
}