//---------------------------------------------------------------------------

//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995
//
//  File:  csession.cxx
//
//  Contents:  Microsoft OleDB/OleDS Session object for ADSI
//
//
//  History:   08-01-96     shanksh    Created.
//
//----------------------------------------------------------------------------
#include "oleds.hxx"
#pragma hdrstop
#include "atl.h"
#include "row.hxx"

//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::GetDataSource
//
//  Synopsis: Retrieve an interface pointer on the session object
//
//  Arguments:
//              riid,               IID desired
//              ppDSO               ptr to interface
//
//
//  Returns:
//              S_OK                    Session Object Interface returned
//              E_INVALIDARG            ppDSO was NULL
//              E_NOINTERFACE           IID not supported
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSessionObject::GetDataSource (
        REFIID      riid,
    IUnknown ** ppDSO
    )
{
    //
    // Asserts
    //
    ADsAssert(_pDSO);

    if( ppDSO == NULL )
        RRETURN( E_INVALIDARG );

    RRETURN( _pDSO->QueryInterface(riid, (LPVOID*)ppDSO) );
}

//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::GetDataSource
//
//  Synopsis: Retrieve an interface pointer on the session object
//
//  Arguments:
//              riid,               IID desired
//              ppDSO               ptr to interface
//
//
//  Returns:
//              S_OK                    Session Object Interface returned
//              E_INVALIDARG            ppDSO was NULL
//              E_NOINTERFACE           IID not supported
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSessionObject::OpenRowset(
        IUnknown *  pUnkOuter,
        DBID *      pTableID,
        DBID *      pIndexID,
        REFIID      riid,
        ULONG       cPropertySets,
        DBPROPSET   rgPropertySets[],
        IUnknown ** ppRowset
        )
{
    // Don't pass any credentials (NULL)
    RRETURN( OpenRowsetWithCredentials(
                    pUnkOuter,
                    pTableID,
                    pIndexID,
                    riid,
                    cPropertySets,
                    rgPropertySets,
                    NULL,
                    ppRowset) );
}


//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::OpenRowsetWithCredentials
//
//  Synopsis: Opens a rowset. Similar to OpenRowset but takes extra argument
//            CCredentials. This function is used when consumer calls
//            IBindResource::Bind requesting a rowset.
//
//  Returns : HRESULT
//
//----------------------------------------------------------------------------
HRESULT
CSessionObject::OpenRowsetWithCredentials (
        IUnknown *     pUnkOuter,
        DBID *         pTableID,
        DBID *         pIndexID,
        REFIID         riid,
        ULONG          cPropertySets,
        DBPROPSET      rgPropertySets[],
        CCredentials * pCredentials,
        IUnknown **    ppRowset
        )
{
    BOOL         fWarning = FALSE;
    CRowProvider *pRowProvider = NULL;
    DWORD        cAttrs = 1;
    BOOL         *pbMultiValue = NULL; 
    LPWSTR       *pAttrs = NULL;

    //
    // Check in-params and NULL out-params in case of error
    //
    if( ppRowset )
        *ppRowset = NULL;

    if( !pTableID && !pIndexID )
        RRETURN( E_INVALIDARG );

    if( pIndexID )
        RRETURN( DB_E_NOINDEX );

    //
    // Check the PropertySets
    //
    if( cPropertySets > 0 && !rgPropertySets )
        RRETURN ( E_INVALIDARG );

    for(ULONG i=0; i<cPropertySets; i++) {
        if( rgPropertySets[i].cProperties && !rgPropertySets[i].rgProperties )
            RRETURN ( E_INVALIDARG );
    }

    //
    // pwszName field represents the ADsPath of the Directory we have to open;
    // Make sure pwszName is meaningful
    //
    if( !pTableID ||  pTableID->eKind != DBKIND_NAME ||
        !pTableID->uName.pwszName || !(*(pTableID->uName.pwszName)) )
        RRETURN( DB_E_NOTABLE );

    if( pUnkOuter )//&& !InlineIsEqualGUID(riid, IID_IUnknown) )
        RRETURN( DB_E_NOAGGREGATION );

    if( riid == IID_NULL )
        RRETURN( E_NOINTERFACE );

    //
    // By default, we use credentials stored in member variable _Credentials
    // for binding. Buf if caller passed any credentials through pCredentials,
    // these take precedence. This also means that we need to store these
    // credentials in the CRowProvider object for future use -
    // e.g. GetRowFromHRow will use these credentials.
    //
    CCredentials * pCreds = &_Credentials;
    if( pCredentials )
        pCreds = pCredentials;

    //
    // If integrated security is being used, impersonate the caller
    //
    BOOL fImpersonating;

    fImpersonating = FALSE;
    if(_pDSO->IsIntegratedSecurity())
    {
        HANDLE ThreadToken = _pDSO->GetThreadToken();

        ASSERT(ThreadToken != NULL);
        if (ThreadToken)
        {
            if (!ImpersonateLoggedOnUser(ThreadToken))
                RRETURN(E_FAIL);
            fImpersonating = TRUE;
        }
        else
            RRETURN(E_FAIL);
    }

    HRESULT hr = GetDSInterface(
                    pTableID->uName.pwszName,
                    *pCreds,
                    IID_IDirectorySearch,
                    (void **)&_pDSSearch);

    if (fImpersonating)
    {
        RevertToSelf();
        fImpersonating = FALSE;
    }

    if( FAILED(hr) )
        RRETURN( hr );

    //
    // Get ColumnsInfo based on the list of attributes that we want to be
    // returned.  GetDefaultColumnInfo cleansup memory on failure.
    //
    ULONG          cColumns      = 0;
    DBCOLUMNINFO * prgInfo       = NULL;
    WCHAR *        pStringBuffer = NULL;

    hr = GetDefaultColumnInfo(&cColumns, &prgInfo, &pStringBuffer);
    if( FAILED(hr) )
        RRETURN( hr );

    // Store the properties (which must be in the rowset property group) in
    // the property object. OpenRowset is different from methods like
    // ICOmmand::SetProperties and ISessionProperties::SetProperties in that
    // it returns DB_E_ERROSOCCURRED if any property which is REQUIRED could
    // not be set and DB_S_ERROROCCURRED if any property that is OPTIONAL
    // could not be set. ICommand::SetProperties returns DB_E_ERROSOCCURRED
    // if all properties could not be set and DB_S_ERROSOCCURRED if some
    // property could not be set i.e, DBPROPOPTIONS (REQUIRED or OPTIONAL) is
    // ignored.

    // Use PROPSET_COMMAND as the bitmask below since the properties that are
    // going to be set are in the rowset property group. These properties that
    // are stored in the property object cannot be retrieved by the client
    // since GetProperties on a session object will only return properties in
    // the session property group.

    hr = _pUtilProp->SetProperties(
            cPropertySets,
            rgPropertySets,
            PROPSET_COMMAND
            );
    if( (DB_E_ERRORSOCCURRED == hr) || (DB_S_ERRORSOCCURRED == hr) )
    // check if a required property could not be set
    {
        ULONG i, j;

        for(i = 0; i < cPropertySets; i++)
            for(j = 0; j < rgPropertySets[i].cProperties; j++)
                if( rgPropertySets[i].rgProperties[j].dwStatus != 
                               DBPROPSTATUS_OK ) 
                    if( rgPropertySets[i].rgProperties[j].dwOptions != 
                               DBPROPOPTIONS_OPTIONAL ) 
                    {
                        BAIL_ON_FAILURE( hr = DB_E_ERRORSOCCURRED );
                    }
                    else
                        fWarning = TRUE;

        // if we get here, then there was all required properties were set 
        // successfully. However, hr could still be DB_ERRORSOCCURRED if all
        // properties were optional and all of them could not be set. This
        // condition is not an error for OpenRowset as noted in the comment 
        // above. Hence reset hr to S_OK.

        hr = S_OK;
    }

    // we still need to catch other errors like E_INAVLIDARG
    BAIL_ON_FAILURE(hr);

    hr = SetSearchPrefs();
    BAIL_ON_FAILURE( hr );

    //
    // Create RowProvider object to pass to rowset code
    //
    pbMultiValue = (BOOL *)AllocADsMem(sizeof(BOOL));
    pAttrs = (LPWSTR *)AllocADsMem(cAttrs * sizeof(LPWSTR));
    if( pAttrs )
        pAttrs[0] = AllocADsStr(L"ADsPath");

    if( !pAttrs || !pAttrs[0] || !pbMultiValue )
        BAIL_ON_FAILURE( hr=E_OUTOFMEMORY );

    *pbMultiValue = FALSE;
    _pDSSearch->AddRef();

    // Is this an NDS path? If so, set filter appropriately. Fix for #286560.
    WCHAR lpszProgId[MAX_PATH];
    BOOL fIsNds;

    hr = CopyADsProgId(pTableID->uName.pwszName, lpszProgId);
    BAIL_ON_FAILURE( hr );
    if( !wcscmp(L"NDS", lpszProgId) )
        fIsNds = TRUE;
    else
        fIsNds = FALSE;

    hr = CRowProvider::CreateRowProvider(
                            _pDSSearch,
                            NULL,
                            pAttrs,
                            cAttrs,
                            cColumns,
                            prgInfo,
                            pStringBuffer,
                            IID_IRowProvider,
                            pbMultiValue,
                            TRUE,
                            pCreds,
                            (void **) &pRowProvider
                            );

    BAIL_ON_FAILURE( hr );

    //
    // RowProvider responsible for deallocation
    //
    pbMultiValue = NULL;

    hr= CRowset::CreateRowset(
            pRowProvider,
            (LPUNKNOWN)(IAccessor FAR *)this ,
            this,
            NULL,
            cPropertySets,
            rgPropertySets,
            0,
            NULL,
            TRUE,  // ADsPath is requested
            FALSE, // not all attributes are requested 
            riid,
            ppRowset
            );

    BAIL_ON_FAILURE( hr );

error:

    if( _pDSSearch ) {
        _pDSSearch->Release();
        _pDSSearch = NULL;
    }

    if( pRowProvider )
        pRowProvider->Release();

    if( prgInfo )
        _pIMalloc->Free(prgInfo);

    if( pStringBuffer )
        _pIMalloc->Free(pStringBuffer);

    if( pbMultiValue )
        FreeADsMem(pbMultiValue);

    if (pAttrs)
    {
        for (i = 0; i < cAttrs; i++)
        {
            if (pAttrs[i])
                FreeADsStr(pAttrs[0]);
        }
        FreeADsMem(pAttrs);
    }

    if( FAILED(hr) )
        RRETURN( hr );
    else if( fWarning )
        RRETURN( DB_S_ERRORSOCCURRED );
    else
        RRETURN( hr );
}


//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::GetProperties
//
//  Synopsis: Returns current settings of all properties in the DBPROPFLAGS_SESSION property
//            group
//
//  Arguments:
//              cPropertySets         count of restiction guids
//              rgPropertySets        restriction guids
//              pcProperties          count of properties returned
//              prgProperties         property information returned
//
//  Returns:
//              S_OK                    Session Object Interface returned
//              E_INVALIDARG            pcProperties or prgPropertyInfo was NULL
//              E_OUTOFMEMORY           Out of memory
//
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSessionObject::GetProperties(
        ULONG        cPropIDSets,
        const        DBPROPIDSET rgPropIDSets[],
        ULONG *      pcPropSets,
        DBPROPSET ** pprgPropSets
        )
{
    //
    // Asserts
    //
    ADsAssert(_pUtilProp);

    //
    // Check in-params and NULL out-params in case of error
    //
    HRESULT hr = _pUtilProp->GetPropertiesArgChk(
                                cPropIDSets,
                                rgPropIDSets,
                                pcPropSets,
                                pprgPropSets,
                                PROPSET_SESSION);
    if( FAILED(hr) )
        RRETURN( hr );

    //
    // Just pass this call on to the utility object that manages our properties
    //
    RRETURN( _pUtilProp->GetProperties(
                            cPropIDSets,
                            rgPropIDSets,
                            pcPropSets,
                            pprgPropSets,
                            PROPSET_SESSION) );
}


//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::SetProperties
//
//  Synopsis: Set properties in the DBPROPFLAGS_SESSION property group
//
//  Arguments:
//              cProperties
//              rgProperties
//
//  Returns:
//              S_OK             Session Object Interface returned
//              E_INVALIDARG     pcProperties or prgPropertyInfo was NULL
//              E_OUTOFMEMORY    Out of memory
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSessionObject::SetProperties(
        ULONG     cPropertySets,
        DBPROPSET rgPropertySets[]
    )
{
    //
    // Asserts
    //
    ADsAssert(_pUtilProp);

    //
    // Just pass this call on to the utility object that manages our properties
    //
    RRETURN( _pUtilProp->SetProperties(
                            cPropertySets,
                            rgPropertySets,
                            PROPSET_SESSION) );
}

//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::CreateCommand
//
//  Synopsis: Creates a brand new command and returns requested interface
//
//  Arguments:
//              pUnkOuter           outer Unknown
//              riid,               IID desired
//              ppCommand           ptr to interface
//
//
//  Returns:
//              S_OK                Command Object Interface returned
//              E_INVALIDARG        ppCommand was NULL
//              E_NOINTERFACE       IID not supported
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSessionObject::CreateCommand(
        IUnknown *  pUnkOuter,
        REFIID      riid,
        IUnknown ** ppCommand
    )
{
    CCommandObject* pCommand = NULL;
    HRESULT                     hr;

    //
    // check in-params and NULL out-params in case of error
    //
    if( ppCommand )
        *ppCommand = NULL;
    else
        RRETURN( E_INVALIDARG );

    if( pUnkOuter )//&& !InlineIsEqualGUID(riid, IID_IUnknown) )
        RRETURN( DB_E_NOAGGREGATION );

    //
    // open a CCommand object
    //
    pCommand = new CCommandObject(pUnkOuter);
    if( !pCommand )
        RRETURN( E_OUTOFMEMORY );

    //
    // initialize the object
    //
    if( !pCommand->FInit(this, _Credentials) ) {
        delete pCommand;
        RRETURN( E_OUTOFMEMORY );
    }

    //
    // get requested interface pointer on DBSession
    //
    hr = pCommand->QueryInterface(riid, (void **)ppCommand);
    if( FAILED( hr ) ) {
        delete pCommand;
        RRETURN( hr );
    }

    pCommand->Release();

    //
    // all went well
    //
    RRETURN( S_OK );
}



//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::GetDefaultColumnInfo
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CSessionObject::GetDefaultColumnInfo(
        ULONG *         pcColumns,
        DBCOLUMNINFO ** prgInfo,
        OLECHAR **      ppStringBuffer
    )
{
    //
    // Asserts
    //
    ADsAssert(_pIMalloc);
    ADsAssert(pcColumns);
    ADsAssert(prgInfo);
    ADsAssert(ppStringBuffer);

    //
    // Allcoate memory for the Bookmark and ADsPath column
    //
    *prgInfo = (DBCOLUMNINFO*)_pIMalloc->Alloc(2 * sizeof(DBCOLUMNINFO));
    *ppStringBuffer = (WCHAR*)_pIMalloc->Alloc((wcslen(L"ADsPath") + 1) * sizeof(WCHAR));

    //
    // Free memory on a failure
    //
    if( !(*prgInfo) )
        RRETURN( E_OUTOFMEMORY );

    if( !(*ppStringBuffer) ) {
        _pIMalloc->Free(*prgInfo);
        *prgInfo = NULL;
        RRETURN( E_OUTOFMEMORY );
    }

    //
    // Initialize the memory
    //
    ZeroMemory(*prgInfo, 2 * sizeof(DBCOLUMNINFO));
    ZeroMemory(*ppStringBuffer, (wcslen(L"ADsPath") + 1) * sizeof(WCHAR));
    wcscpy(*ppStringBuffer, OLESTR("ADsPath"));

    //
    // Fill up the Bookmark column
    //
    *pcColumns = 2;

    (*prgInfo)[0].pwszName                = NULL;
    (*prgInfo)[0].pTypeInfo               = NULL;
    (*prgInfo)[0].iOrdinal                = 0;
    (*prgInfo)[0].ulColumnSize            = sizeof(ULONG);
    (*prgInfo)[0].wType                   = DBTYPE_UI4;
    (*prgInfo)[0].bPrecision              = 10;
    (*prgInfo)[0].bScale                  = (BYTE) ~ 0;
    (*prgInfo)[0].columnid.eKind          = DBKIND_GUID_PROPID;
    (*prgInfo)[0].columnid.uGuid.guid     = DBCOL_SPECIALCOL;
    (*prgInfo)[0].columnid.uName.ulPropid = 2;
    (*prgInfo)[0].dwFlags                 = DBCOLUMNFLAGS_ISBOOKMARK |
                                            DBCOLUMNFLAGS_ISFIXEDLENGTH;
    //
    // Fill up the ADsPath column
    //
    (*prgInfo)[1].pwszName                = *ppStringBuffer;
    (*prgInfo)[1].pTypeInfo               = NULL;
    (*prgInfo)[1].iOrdinal                = 1;
    (*prgInfo)[1].ulColumnSize            = (ULONG)256;
    (*prgInfo)[1].wType                   = DBTYPE_WSTR|DBTYPE_BYREF;
    (*prgInfo)[1].bPrecision              = (BYTE) ~ 0;
    (*prgInfo)[1].bScale                  = (BYTE) ~ 0;
    (*prgInfo)[1].columnid.eKind          = DBKIND_NAME;
    (*prgInfo)[1].columnid.uName.pwszName = *ppStringBuffer;
    (*prgInfo)[1].columnid.uGuid.guid     = GUID_NULL;
    (*prgInfo)[1].dwFlags                 = DBCOLUMNFLAGS_ISNULLABLE;

    RRETURN( S_OK );
}

#if (!defined(BUILD_FOR_NT40))
//IBindResource::Bind
//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::CSessionObject
//
//  Synopsis: Constructor
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    09-17-1998   mgorti     Created.
//
//----------------------------------------------------------------------------
HRESULT CSessionObject::Bind(
    IUnknown *              punkOuter,
    LPCOLESTR               pwszURL,
    DBBINDURLFLAG           dwBindFlags,
    REFGUID                 rguid,
    REFIID                  riid,
    IAuthenticate *         pAuthenticate,
    DBIMPLICITSESSION *     pImplSession,
    DWORD *                 pdwBindStatus,
    IUnknown **             ppUnk
    )
{
    HRESULT         hr = S_OK;
    CComBSTR        bstrNewURL;

	TRYBLOCK
    
		//Initialize return arguments
        if (pdwBindStatus)
            *pdwBindStatus = 0;

        if (ppUnk)
            *ppUnk = NULL;

        if (pImplSession)
            pImplSession->pSession = NULL;

        //if caller passed a null value for dwBindFlags,
        //get them from initialization properties.
        if (dwBindFlags == 0)
            dwBindFlags = BindFlagsFromDbProps();

        //Generic argument validation
        hr = ValidateBindArgs(punkOuter,
                pwszURL,
                dwBindFlags,
                rguid,
                riid,
                pAuthenticate,
                pImplSession,
                pdwBindStatus,
                ppUnk);

        BAIL_ON_FAILURE(hr);

        //Fill in the pImplSession struct.
        if (pImplSession)
        {
            // Our session doesn't support aggregation.
            if (pImplSession->pUnkOuter != NULL)
                BAIL_ON_FAILURE(hr = DB_E_NOAGGREGATION);

            hr = QueryInterface(*pImplSession->piid,
                                (void**)&(pImplSession->pSession));
            if (FAILED(hr))
                BAIL_ON_FAILURE(hr = E_NOINTERFACE );
        }

        //Specific validation checks
        //We are currently a read-only provider
        if (dwBindFlags & DBBINDURLFLAG_WRITE)
            BAIL_ON_FAILURE(hr = DB_E_READONLY);

        //We currently don't support aggregation
        if (punkOuter != NULL)
            BAIL_ON_FAILURE (hr = DB_E_NOAGGREGATION);

        //We don't support the following flags
        if (dwBindFlags & DBBINDURLFLAG_ASYNCHRONOUS)
            BAIL_ON_FAILURE(hr = DB_E_ASYNCNOTSUPPORTED);

        if (dwBindFlags & DBBINDURLFLAG_OUTPUT              ||
            dwBindFlags & DBBINDURLFLAG_RECURSIVE           ||
            dwBindFlags & DBBINDURLFLAG_DELAYFETCHSTREAM    ||
            dwBindFlags & DBBINDURLFLAG_DELAYFETCHCOLUMNS)
            BAIL_ON_FAILURE(hr = E_INVALIDARG);

        //Now Try to Bind.
        if (InlineIsEqualGUID(rguid, DBGUID_ROW) ||
            InlineIsEqualGUID(rguid, DBGUID_ROWSET))
        {
            //If the URL is not absolute, build the absolute URL
            //using the DBPROP_INIT_PROVIDERSTRING.
            if (! bIsAbsoluteURL(pwszURL))
            {
                hr = BuildAbsoluteURL (pwszURL, bstrNewURL);
                if (FAILED(hr))
                    BAIL_ON_FAILURE(hr = E_INVALIDARG);
            }
            else
                bstrNewURL = pwszURL;

            if ( InlineIsEqualGUID(rguid, DBGUID_ROW) )
            {
                hr = BindToRow(punkOuter,
                               (PWCHAR)bstrNewURL,
                               pAuthenticate,
                               dwBindFlags,
                               riid,
                               ppUnk);
            }
            else
            {
                hr = BindToRowset(punkOuter,
                                  (PWCHAR)bstrNewURL,
                                  pAuthenticate,
                                  dwBindFlags,
                                  riid,
                                  ppUnk);
            }

            BAIL_ON_FAILURE(hr);
        }
        else if (InlineIsEqualGUID(rguid, DBGUID_DSO))
        {
            ADsAssert(_pDSO);
            hr = BindToDataSource(
                            punkOuter,
                            pwszURL,
                            pAuthenticate,
                            dwBindFlags,
                            riid,
                            ppUnk
                    );
            BAIL_ON_FAILURE (hr);
        }
        else if (InlineIsEqualGUID(rguid, DBGUID_SESSION))
        {
            hr = QueryInterface(riid, (void**)ppUnk);
            BAIL_ON_FAILURE (hr);
        }
        else
            BAIL_ON_FAILURE(hr = E_INVALIDARG);

        //Fix for bug Raid-X5#83386 - spec change
        //If caller specified any DENY semantics,
        //set warning status and return value, since
        //we don't support these.
        if (dwBindFlags & DBBINDURLFLAG_SHARE_DENY_READ ||
            dwBindFlags & DBBINDURLFLAG_SHARE_DENY_WRITE ||
            dwBindFlags & DBBINDURLFLAG_SHARE_EXCLUSIVE)
        {
            if (pdwBindStatus)
                *pdwBindStatus = DBBINDURLSTATUS_S_DENYNOTSUPPORTED;
            BAIL_ON_FAILURE (hr = DB_S_ERRORSOCCURRED);
        }
	
	CATCHBLOCKBAIL(hr)

error:

    RRETURN(hr);
}

#endif

//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::CSessionObject
//
//  Synopsis: Constructor
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
CSessionObject::CSessionObject(
        LPUNKNOWN pUnkOuter
    )
{
    //
    // Initialize simple member vars
    //
    _pUnkOuter     = pUnkOuter ? pUnkOuter : (IGetDataSource FAR *)this;
    _cCommandsOpen = 0;
    _pUtilProp     = NULL;
    _pDSSearch     = NULL;
    _pIMalloc      = NULL;
    _pDSO          = NULL;

    ENLIST_TRACKING(CSessionObject);
}


//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::~CSessionObject
//
//  Synopsis: Destructor
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
CSessionObject::~CSessionObject( )
{
    //
    // Free properties management object
    //
    delete _pUtilProp;

    if( _pIMalloc )
        _pIMalloc->Release();

    if( _pDSO ) {
        _pDSO->DecrementOpenSessions();
        _pDSO->Release();
    }
}

//+---------------------------------------------------------------------------
//
//  Function:  CSessionObject::FInit
//
//  Synopsis:  Initialize the session Object
//
//  Arguments:
//
//  Returns:
//             TRUE              Initialization succeeded
//             FALSE             Initialization failed
//
//  Modifies:
//
//  History:    08-28-96   ShankSh     Created.
//
//----------------------------------------------------------------------------
BOOL CSessionObject::FInit(
        CDSOObject *   pDSO,
        CCredentials & Credentials
        )
{
    HRESULT hr;

    //
    // Asserts
    //
    ADsAssert(pDSO);
    ADsAssert(&Credentials);

    //
    // Allocate properties management object
    //
    _pUtilProp = new CUtilProp();
    if( !_pUtilProp )
        return FALSE;

    hr = _pUtilProp->FInit(&Credentials);
    BAIL_ON_FAILURE(hr);

    //
    // IMalloc->Alloc is the way we have to allocate memory for out parameters
    //
    hr = CoGetMalloc(MEMCTX_TASK, &_pIMalloc);
    BAIL_ON_FAILURE(hr);

    //
    // Establish parent object pointer
    //
    _pDSO = pDSO;
    _Credentials = Credentials;
    _pDSO->AddRef();
    _pDSO->IncrementOpenSessions();

    return( TRUE );

error:

    RRETURN( FALSE );
}

STDMETHODIMP
CSessionObject::QueryInterface(REFIID iid, LPVOID FAR* ppv)
{
    if( ppv == NULL )
        RRETURN( E_INVALIDARG );

    if( IsEqualIID(iid, IID_IUnknown) ) {
        *ppv = (IGetDataSource FAR *) this;
    }
    else if( IsEqualIID(iid, IID_IGetDataSource) ) {
        *ppv = (IGetDataSource FAR *) this;
    }
    else if( IsEqualIID(iid, IID_IOpenRowset) ) {
        *ppv = (IOpenRowset FAR *) this;
    }
    else if( IsEqualIID(iid, IID_ISessionProperties) ) {
        *ppv = (ISessionProperties FAR *) this;
    }
    else if( IsEqualIID(iid, IID_IDBCreateCommand) ) {
        *ppv = (IDBCreateCommand FAR *) this;
    }
#if (!defined(BUILD_FOR_NT40))
    else if( IsEqualIID(iid, IID_IBindResource) ) {
        *ppv = (IBindResource FAR *) this;
    }
#endif
    else {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    AddRef();
    return NOERROR;
}

#if (!defined(BUILD_FOR_NT40))
//+---------------------------------------------------------------------------
//
//  Function: CSessionObject::ValidateBindArgs
//
//  Synopsis: Validates IBindResource::Bind function arguments.
//
//----------------------------------------------------------------------------
HRESULT CSessionObject::ValidateBindArgs(
    IUnknown *              punkOuter,
    LPCOLESTR               pwszURL,
    DBBINDURLFLAG           dwBindFlags,
    REFGUID                 rguid,
    REFIID                  riid,
    IAuthenticate *         pAuthenticate,
    DBIMPLICITSESSION *     pImplSession,
    DWORD *                 pdwBindStatus,
    IUnknown **             ppUnk
    )
{
    //General validation checks.
    if (pwszURL == NULL || InlineIsEqualGUID(rguid,GUID_NULL) ||
        InlineIsEqualGUID(riid, GUID_NULL) || ppUnk == NULL )
        RRETURN(E_INVALIDARG);

    if (pImplSession &&
        (pImplSession->pUnkOuter == NULL || pImplSession->piid == NULL))
        RRETURN(E_INVALIDARG);

    if (punkOuter && !InlineIsEqualGUID(riid, IID_IUnknown))
        RRETURN(DB_E_NOAGGREGATION);

    if (pImplSession && pImplSession->pUnkOuter &&
        pImplSession->piid &&
        !InlineIsEqualGUID(*pImplSession->piid, IID_IUnknown))
        RRETURN(DB_E_NOAGGREGATION);

    if (dwBindFlags & DBBINDURLFLAG_RECURSIVE)
    {
        //if DBBINDURLFLAG_RECURSIVE is set, at least one of the SHARE_DENY
        //flags must have been set.
        if (! ( (dwBindFlags & DBBINDURLFLAG_SHARE_DENY_READ) ||
                (dwBindFlags & DBBINDURLFLAG_SHARE_DENY_WRITE) ||
                (dwBindFlags & DBBINDURLFLAG_SHARE_EXCLUSIVE)
                )
                )
            RRETURN(E_INVALIDARG);
    }

    if (!(dwBindFlags & DBBINDURLFLAG_READ) &&
        !(dwBindFlags & DBBINDURLFLAG_WRITE) ) {
        // Must have either read or write access:
        RRETURN(E_INVALIDARG);
    }

    if (InlineIsEqualGUID(rguid, DBGUID_DSO) &&
        !((dwBindFlags & DBBINDURLFLAG_READ) ||
          (dwBindFlags & DBBINDURLFLAG_ASYNCHRONOUS) ||
          (dwBindFlags & DBBINDURLFLAG_WAITFORINIT)
         )
       )
       //if object type is DataSource, only the above flags are allowed
       RRETURN(E_INVALIDARG);

    if (InlineIsEqualGUID(rguid, DBGUID_SESSION) &&
        ! (dwBindFlags == DBBINDURLFLAG_READ))
        //if object type is Session, only DBBINDURLFLAG_READ is allowed
        RRETURN(E_INVALIDARG);

    if (InlineIsEqualGUID(rguid, DBGUID_ROWSET) &&
        ((dwBindFlags & DBBINDURLFLAG_DELAYFETCHCOLUMNS) ||
         (dwBindFlags & DBBINDURLFLAG_DELAYFETCHSTREAM)
        )
       )
       //if object type is Rowset, DELAYFETCHCOLUMNS and DELAYFETCHSTREAM
       //flags are disallowed.
       RRETURN ( E_INVALIDARG );

    if (InlineIsEqualGUID(rguid, DBGUID_STREAM) &&
        ((dwBindFlags & DBBINDURLFLAG_DELAYFETCHCOLUMNS) ||
         (dwBindFlags & DBBINDURLFLAG_DELAYFETCHSTREAM)
        )
       )
       //if object type is Stream, DELAYFETCHCOLUMNS and
       //DELAYFETCHSTREAM flags are disallowed.
       RRETURN(E_INVALIDARG);

    RRETURN(S_OK);
}

//+---------------------------------------------------------------------------
//
//  Function: CSessionObject::BindToRow
//
//  Synopsis: Given a URL, binds to that row object and returns the requested
//            interface.
//
//----------------------------------------------------------------------------
HRESULT
CSessionObject::BindToRow(
    IUnknown *punkOuter,
    LPCOLESTR pwszURL,
    IAuthenticate *pAuthenticate,
    DWORD dwBindFlags,
    REFIID riid,
    IUnknown** ppUnk
    )
{
    CComObject<CRow>        *pRow = NULL;
    auto_rel<IUnknown>      pSession;
    auto_rel<IRow> pRowDelete;
    HRESULT hr = S_OK;

    hr = CComObject<CRow>::CreateInstance(&pRow);
    if (FAILED(hr))
        BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);

    //To make sure we delete the row object in
    //case we encounter errors after this point.
    //Note: this version of auto_rel doesn't addref on assignment.
    pRowDelete = pRow;
    pRowDelete->AddRef();

    hr = QueryInterface(__uuidof(IUnknown), (void **)&pSession);
    if (FAILED(hr))
        BAIL_ON_FAILURE(hr = E_FAIL);

    //Initialize row and bind it to a Directory Object.
    hr = pRow->Initialize((PWSTR)pwszURL,
                    (IUnknown *)pSession,
                    pAuthenticate,
                    dwBindFlags,
                    FALSE, // not a tearoff 
                    FALSE, // don't get column info. from rowset 
                    &_Credentials,
                    true);

    if (FAILED(hr))
    {
        if (INVALID_CREDENTIALS_ERROR(hr))
        {
            BAIL_ON_FAILURE(hr = DB_SEC_E_PERMISSIONDENIED);
        }
        else
        {
            BAIL_ON_FAILURE(hr = DB_E_NOTFOUND);
        }
    }

    hr = pRow->QueryInterface(riid, (void**)ppUnk);
    if (FAILED(hr))
        BAIL_ON_FAILURE (hr = E_NOINTERFACE);

error:

    RRETURN ( hr );
}

//+---------------------------------------------------------------------------
//
//  Function: CSessionObject::BindToRowset
//
//  Synopsis: Given a URL, binds to a rowset object that has all its child
//            nodes as rows and returns the requested interface on the rowset.
//
//----------------------------------------------------------------------------
HRESULT
CSessionObject::BindToRowset(
    IUnknown *pUnkOuter,
    LPCOLESTR pwszURL,
    IAuthenticate *pAuthenticate,
    DWORD dwBindFlags,
    REFIID riid,
    IUnknown** ppUnk
    )
{
    HRESULT hr;
    DWORD fAuthFlags;

    DBID tableID;
    tableID.eKind = DBKIND_NAME;
    tableID.uName.pwszName = (LPWSTR) pwszURL;

    //Create the rowset.

    // Fix for 351040. First try explicit credentials, then session object's
    // credentials, then default credentials. 

    if(pAuthenticate)
    {
        CCredentials creds;

        hr = GetCredentialsFromIAuthenticate(pAuthenticate, creds);
        if (FAILED(hr))
            BAIL_ON_FAILURE(hr = E_INVALIDARG);

        fAuthFlags = creds.GetAuthFlags();
        creds.SetAuthFlags(fAuthFlags |
                        ADS_SECURE_AUTHENTICATION);

        hr = OpenRowsetWithCredentials(pUnkOuter, &tableID, NULL, riid, 
                                       0, NULL, &creds, ppUnk); 
    }

    if( (!pAuthenticate) || (INVALID_CREDENTIALS_ERROR(hr)) )
    // try credentials in session object
        hr = OpenRowset(pUnkOuter, &tableID, NULL, riid, 0, NULL, ppUnk);

    if(INVALID_CREDENTIALS_ERROR(hr))
    // try default credentials
    {
        CCredentials creds; // default credentials

        fAuthFlags = creds.GetAuthFlags();
        creds.SetAuthFlags(fAuthFlags |
                        ADS_SECURE_AUTHENTICATION);

        hr = OpenRowsetWithCredentials(pUnkOuter, &tableID, NULL, riid,
                                       0, NULL, &creds, ppUnk); 
    }

    BAIL_ON_FAILURE(hr);

    RRETURN ( S_OK );

error:
    RRETURN ( hr );
}

//+---------------------------------------------------------------------------
//
//  Function: CSessionObject::BindFlagsFromDbProps
//
//  Synopsis: Synthesizes bind flags from initialization properties
//            DBPROP_INIT_MODE and DBPROP_INIT_BINDFLAGS
//
//----------------------------------------------------------------------------
DWORD CSessionObject::BindFlagsFromDbProps()
{
    HRESULT hr = S_OK;
    auto_rel<IDBProperties> pDBProp;
    ULONG   i, j, cPropertySets = 0;
    DWORD dwMode = 0, dwBindFlags = 0, dwBindFlagProp = 0;
    DWORD dwResult = 0;

    hr = GetDataSource(__uuidof(IDBProperties), (IUnknown **)&pDBProp);
    BAIL_ON_FAILURE(hr);

    DBPROPID  propids[2];
    propids[0] = DBPROP_INIT_MODE;
    propids[1] = DBPROP_INIT_BINDFLAGS;

    DBPROPIDSET rgPropertyIDSets[1];
    rgPropertyIDSets[0].rgPropertyIDs = propids;
    rgPropertyIDSets[0].cPropertyIDs = 2;
    rgPropertyIDSets[0].guidPropertySet = DBPROPSET_DBINIT;

    DBPROPSET *prgPropertySets;
    hr = pDBProp->GetProperties(
                            1,
                            rgPropertyIDSets,
                            &cPropertySets,
                            &prgPropertySets);

    if (hr == DB_E_ERRORSOCCURRED)
        BAIL_ON_FAILURE(hr);

    for (i = 0; i < cPropertySets; i++)
    {
        for (j = 0; j < prgPropertySets[i].cProperties; j++)
        {
            DBPROP *pProp = &prgPropertySets[i].rgProperties[j];
            ADsAssert(pProp);
            if (pProp->dwStatus == S_OK &&
                pProp->dwPropertyID == DBPROP_INIT_MODE)
                dwMode = V_I4(&pProp->vValue);
            else if (pProp->dwStatus == S_OK &&
                    pProp->dwPropertyID == DBPROP_INIT_BINDFLAGS)
                dwBindFlagProp = V_I4(&pProp->vValue);
            else
                continue;
        }
    }

    //Now extract bind flags from dwMode and dwBindFlagProp
    {
        DWORD dwModeMask      =
                DB_MODE_READ |
                DB_MODE_WRITE |
                DB_MODE_READWRITE |
                DB_MODE_SHARE_DENY_READ |
                DB_MODE_SHARE_DENY_WRITE |
                DB_MODE_SHARE_EXCLUSIVE |
                DB_MODE_SHARE_DENY_NONE;

        dwResult |= dwMode & dwModeMask;

        if ( dwBindFlagProp & DB_BINDFLAGS_DELAYFETCHCOLUMNS ) {
            dwBindFlags |= DBBINDURLFLAG_DELAYFETCHCOLUMNS;
        }
        if ( dwBindFlagProp & DB_BINDFLAGS_DELAYFETCHSTREAM ) {
            dwBindFlags |= DBBINDURLFLAG_DELAYFETCHSTREAM;
        }
        if ( dwBindFlagProp & DB_BINDFLAGS_RECURSIVE ) {
            dwBindFlags |= DBBINDURLFLAG_RECURSIVE;
        }
        if ( dwBindFlagProp & DB_BINDFLAGS_OUTPUT ) {
            dwBindFlags |= DBBINDURLFLAG_OUTPUT;
        }

        dwResult |= dwBindFlagProp | dwBindFlags;
    }

error:
    for (i = 0; i < cPropertySets; i++)
    {
        for (j = 0; j < prgPropertySets[i].cProperties; j++)
        {
            DBPROP *pProp = &(prgPropertySets[i].rgProperties[j]);
            ADsAssert(pProp);
            FreeDBID(&pProp->colid);
            VariantClear(&pProp->vValue);
        }

        CoTaskMemFree(prgPropertySets[i].rgProperties);
    }
    CoTaskMemFree(prgPropertySets);

    RRETURN ( dwResult );
}

//+---------------------------------------------------------------------------
//
//  Function: CSessionObject::BindToDataSource
//
//  Synopsis: Initializes the DataSource object if necessary, Sets
//            DBPROP_INIT_PROVIDERSTRING property and returns the requested
//            interface on the datasource.
//
//----------------------------------------------------------------------------
HRESULT
CSessionObject::BindToDataSource(
    IUnknown *pUnkOuter,
    LPCOLESTR pwszURL,
    IAuthenticate *pAuthenticate,
    DWORD dwBindFlags,
    REFIID riid,
    IUnknown** ppUnk
    )
{
    HRESULT                 hr = S_OK;
    auto_rel<IDBProperties> pDBProperties;
    DBPROP                  props[1];
    DBPROPSET               rgPropertySets[1];
    CComBSTR                bstrURL(pwszURL);

    //Initialize DBPROP_INIT_PROVIDERSTRING only if the
    //URL is absolute.
    if (bIsAbsoluteURL (pwszURL))
    {
        // Check if the datasource has already been initialized.
        if (_pDSO->IsInitialized())
            BAIL_ON_FAILURE(hr = DB_E_ALREADYINITIALIZED);
            
        props[0].dwPropertyID = DBPROP_INIT_PROVIDERSTRING;
        props[0].dwOptions = DBPROPOPTIONS_OPTIONAL;
        props[0].vValue.vt = VT_BSTR;
        props[0].vValue.bstrVal = (PWCHAR)bstrURL;

        rgPropertySets[0].rgProperties = props;
        rgPropertySets[0].cProperties = 1;
        rgPropertySets[0].guidPropertySet = DBPROPSET_DBINIT;

        hr = GetDataSource(
                __uuidof(IDBProperties),
                (IUnknown **)&pDBProperties
        );
        BAIL_ON_FAILURE(hr);

        hr = pDBProperties->SetProperties(1, rgPropertySets);
        BAIL_ON_FAILURE(hr);
    }
    //  If consumer doesn't specify DBBINDURLFLAG_WAITFORINIT, it
    //  means consumer wants an initialized DSO
    //
    if (! (dwBindFlags & DBBINDURLFLAG_WAITFORINIT))
    {
        auto_rel<IDBInitialize> pDBInitialize;

        hr = GetDataSource(__uuidof(IDBInitialize), (IUnknown **)&pDBInitialize);
        BAIL_ON_FAILURE(hr);

        hr = pDBInitialize->Initialize();
        BAIL_ON_FAILURE(hr);
    }

    //Return the requested interface on the DSO.
    hr = GetDataSource(riid, ppUnk);
    BAIL_ON_FAILURE(hr);

error:

    RRETURN ( hr );
}

//+---------------------------------------------------------------------------
//
//  Function: CSessionObject::BuildAbsoluteURL
//
//  Synopsis: Given a relative URL, builds absolute URL using the relative URL
//            and the property DBPROP_INIT_PROVIDERSTRING.
//
//----------------------------------------------------------------------------
HRESULT
CSessionObject::BuildAbsoluteURL(
    CComBSTR       bstrLeaf,
    CComBSTR&      bstrAbsoluteURL
    )
{
    HRESULT                 hr = S_OK;
    auto_rel<IDBProperties> pDBProp;
    auto_rel<IADsPathname>  pPathParent;
    auto_rel<IADsPathname>  pPathLeaf;
    ULONG                   cPropertySets = 0;
    long                    i, j, cElements = 0;
    CComBSTR                bstrParent;
    DBPROPSET*              prgPropertySets = NULL;
    DBPROPID                propids[1];
    DBPROPIDSET             rgPropertyIDSets[1];

    hr = GetDataSource(__uuidof(IDBProperties), (IUnknown **)&pDBProp);
    BAIL_ON_FAILURE(hr);

    propids[0] = DBPROP_INIT_PROVIDERSTRING;

    rgPropertyIDSets[0].rgPropertyIDs = propids;
    rgPropertyIDSets[0].cPropertyIDs = 1;
    rgPropertyIDSets[0].guidPropertySet = DBPROPSET_DBINIT;

    hr = pDBProp->GetProperties(
                            1,
                            rgPropertyIDSets,
                            &cPropertySets,
                            &prgPropertySets);

    if (SUCCEEDED(hr) && cPropertySets == 1)
    {
        ADsAssert(prgPropertySets != NULL);
        ADsAssert(prgPropertySets[0].rgProperties != NULL);

        DBPROP* pProp = & (prgPropertySets[0].rgProperties[0]);

        bstrParent = pProp->vValue.bstrVal;
    }

    // Build Absolute Path from Parent and leaf.

    hr = CPathname::CreatePathname(
            __uuidof(IADsPathname),
            (void **)&pPathParent
            );
    BAIL_ON_FAILURE(hr);

    hr = pPathParent->Set(bstrParent, ADS_SETTYPE_FULL);
    BAIL_ON_FAILURE(hr);

    if (bstrLeaf.Length() > 0)
    {
        hr = CPathname::CreatePathname(
                __uuidof(IADsPathname),
                (void **)&pPathLeaf
                );
        BAIL_ON_FAILURE(hr);

        hr = pPathLeaf->Set(bstrLeaf, ADS_SETTYPE_DN);
        BAIL_ON_FAILURE(hr);

        hr = pPathLeaf->GetNumElements(&cElements);
        BAIL_ON_FAILURE(hr);

        //Add leaf elements in reverse order.
        //Ex: if bstrLeaf = "CN=Administrator,CN=Users",
        //we add CN=Users first.
        for (i = cElements-1; i >= 0; i--)
        {
            CComBSTR bstrElement;
            hr = pPathLeaf->GetElement(i, &bstrElement);
            BAIL_ON_FAILURE(hr);

            hr = pPathParent->AddLeafElement(bstrElement);
            BAIL_ON_FAILURE(hr);
        }
    }
    //Read back the fully built path name
    hr = pPathParent->Retrieve(ADS_FORMAT_X500, &bstrAbsoluteURL);
    BAIL_ON_FAILURE(hr);

error:
    // Free memory allocated by GetProperties
    for (i = 0; i < cPropertySets; i++)
    {
        for (j = 0; j < prgPropertySets[i].cProperties; j++)
        {
            DBPROP *pProp = &(prgPropertySets[i].rgProperties[j]);
            ADsAssert(pProp);

            // We should free the DBID in pProp, but we know that
            // GetProperties always returns DB_NULLID and FreeDBID doesn't
            //  handle DB_NULLID. So, DBID is not freed here.

            VariantClear(&pProp->vValue);
        }

        CoTaskMemFree(prgPropertySets[i].rgProperties);
    }
    CoTaskMemFree(prgPropertySets);

    RRETURN(hr);
}

extern PROUTER_ENTRY g_pRouterHead;
extern CRITICAL_SECTION g_csRouterHeadCritSect;

//+---------------------------------------------------------------------------
//
//  Function: CSessionObject::bIsAbsoluteURL
//
//  Synopsis: If the given URL starts with any of the ADS provider prefixes,
//            returns true. Returns false otherwise.
//
//----------------------------------------------------------------------------
bool
CSessionObject::bIsAbsoluteURL( LPCOLESTR pwszURL)
{
    if (pwszURL == NULL)
        return false;

    //
    // Make sure the router has been initialized
    //
    EnterCriticalSection(&g_csRouterHeadCritSect);
    if (!g_pRouterHead) {
        g_pRouterHead = InitializeRouter();
    }
    LeaveCriticalSection(&g_csRouterHeadCritSect);


    for (PROUTER_ENTRY pProvider = g_pRouterHead;
         pProvider != NULL;
         pProvider = pProvider->pNext)
    {
        if (pProvider->szProviderProgId == NULL)
            continue;

        size_t strSize = wcslen(pProvider->szProviderProgId);
        
        if ( _wcsnicmp(pwszURL, pProvider->szProviderProgId, strSize) == 0 )
            return true;
    }

    // Given URL doesn't start with any of the ADSI provider prefixes.
    return false;
}

#endif

//-----------------------------------------------------------------------------
// SetSearchPrefs
//
// Sets ADSI search preferences on the property object.
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CSessionObject::SetSearchPrefs(
        void
        )
{
    PROPSET              *pPropSet;
    PADS_SEARCHPREF_INFO  pSearchPref = NULL;
    HRESULT               hr = S_OK;
    ULONG                 i;

    //
    // Asserts
    //
    ADsAssert(_pUtilProp);
    ADsAssert(_pDSSearch);

    pPropSet = _pUtilProp->GetPropSetFromGuid(DBPROPSET_ADSISEARCH);

    if( !pPropSet || !pPropSet->cProperties )
        RRETURN( S_OK );

    pSearchPref = (PADS_SEARCHPREF_INFO) AllocADsMem(
                                                 pPropSet->cProperties *
                                                 sizeof(ADS_SEARCHPREF_INFO)
                                                 );
    if( !pSearchPref )
        BAIL_ON_FAILURE( hr=E_OUTOFMEMORY );

    for (i=0; i<pPropSet->cProperties; i++) {
        hr = _pUtilProp->GetSearchPrefInfo(
                             pPropSet->pUPropInfo[i].dwPropertyID,
                             &pSearchPref[i]
                                                         );
        BAIL_ON_FAILURE( hr );
    }

    hr = _pDSSearch->SetSearchPreference(
                                pSearchPref,
                                pPropSet->cProperties
                                );

    _pUtilProp->FreeSearchPrefInfo(pSearchPref, pPropSet->cProperties);

    BAIL_ON_FAILURE( hr );

error:

    if( pSearchPref )
        FreeADsMem(pSearchPref);

    RRETURN( hr );
}