//---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1996
//
//  File:  cextmgr.cxx
//
//  Contents:  LDAP ExtMgr Object
//
//
//  History:   06-15-96     yihsins     Created.
//
//----------------------------------------------------------------------------
#include "nwcompat.hxx"
#pragma hdrstop


//  Class CADsExtMgr

CADsExtMgr::CADsExtMgr():
    _pClassEntry(NULL),
    _pDispMgr(NULL)
{

}

HRESULT
CADsExtMgr::CreateExtMgr(
    IUnknown FAR * pUnkOuter,
    CAggregatorDispMgr * pDispMgr,
    LPTSTR pszClassName,
    CADsExtMgr ** ppExtMgr
    )
{
    PCLASS_ENTRY pClassEntry =  NULL;
    CADsExtMgr FAR * pExtMgr = NULL;
    HRESULT hr = S_OK;

    hr = AllocateExtMgrObject(
                &pExtMgr
                );
    BAIL_ON_FAILURE(hr);


    //
    // Now store the DispatchMgr of the Aggregator
    //

    pExtMgr->_pDispMgr = pDispMgr;

    hr = ADSIGetExtensionList(
            pszClassName,
            &(pExtMgr->_pClassEntry)
            );

    if (pExtMgr->_pClassEntry) {

        hr = ADSILoadExtensions(
                    pUnkOuter,
                    pExtMgr->_pClassEntry
                    );
    }

    *ppExtMgr = pExtMgr;

    RRETURN(hr);

error:
    *ppExtMgr = NULL;
    delete pExtMgr;
    RRETURN(hr);
}


CADsExtMgr::~CADsExtMgr( )
{
    //
    // Free the ClassEntry
    //

    if (_pClassEntry) {

        FreeClassEntry(_pClassEntry);
    }

    //
    // And do nothing with the DispMgr - we just keep a pointer
    //

}

STDMETHODIMP
CADsExtMgr::QueryInterface(REFIID iid, LPVOID FAR* ppv)
{
    PCLASS_ENTRY  pClassEntry = _pClassEntry;
    IUnknown * pUnknown = NULL;
    PINTERFACE_ENTRY pIID = NULL;
    PEXTENSION_ENTRY pExtensionEntry = NULL;
    HRESULT hr = S_OK;

    if (!pClassEntry) {

        RRETURN(E_NOINTERFACE);
    }

    pExtensionEntry = pClassEntry->pExtensionHead;

    while (pExtensionEntry) {

        pUnknown = pExtensionEntry->pUnknown;
        pIID = pExtensionEntry->pIID;

        while (pIID) {

            if (IsEqualIID(pIID->iid, iid)) {


                if (!pUnknown) {

                    RRETURN(E_NOINTERFACE);
                }

                hr = pUnknown->QueryInterface(
                            iid,
                            ppv
                            );
                RRETURN(hr);
            }

            pIID = pIID->pNext;

        }

        pExtensionEntry = pExtensionEntry->pNext;

    }

    RRETURN(hr = E_NOINTERFACE);
}


HRESULT
CADsExtMgr::AllocateExtMgrObject(
    CADsExtMgr ** ppExtMgr
    )
{
    CADsExtMgr FAR * pExtMgr = NULL;
    HRESULT hr = S_OK;

    pExtMgr = new CADsExtMgr();
    if (pExtMgr == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    *ppExtMgr = pExtMgr;

    RRETURN(hr);

error:

    if (pExtMgr) {
        delete pExtMgr;
    }

    RRETURN(hr);

}


HRESULT
ADSILoadExtensionManager(
    LPWSTR pszClassName,
    IUnknown * pUnkOuter,
    CAggregatorDispMgr * pDispMgr,
    CADsExtMgr ** ppExtMgr
    )
{

    HRESULT hr = S_OK;

    hr = CADsExtMgr::CreateExtMgr(
            pUnkOuter,
            pDispMgr,
            pszClassName,
            ppExtMgr
            );

    RRETURN(hr);
}


STDMETHODIMP
CADsExtMgr::GetTypeInfoCount(
    unsigned int FAR* pctinfo
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CADsExtMgr::GetTypeInfo(
    unsigned int itinfo,
    LCID lcid,
    ITypeInfo FAR* FAR* pptinfo
    )
{
    RRETURN(E_NOTIMPL);
}

STDMETHODIMP
CADsExtMgr::GetIDsOfNames(
    REFIID iid,
    LPWSTR FAR* rgszNames,
    unsigned int cNames,
    LCID lcid,
    DISPID FAR* rgdispid
    )
{
    HRESULT hr = S_OK;
    PEXTENSION_ENTRY pExtension = NULL;
    IPrivateDispatch FAR * pPrivDisp = NULL;


    hr = _pDispMgr->GetIDsOfNames(
            iid,
            rgszNames,
            cNames,
            lcid,
            rgdispid
            );

    if (FAILED(hr)) {

        if (!_pClassEntry) {
            RRETURN(DISP_E_UNKNOWNNAME);
        }

        pExtension = _pClassEntry->pExtensionHead;

        while (pExtension) {

            if (pExtension->fDisp) {

                //
                // fDisp = TRUE indicates
                //  1)  extension supports pADsExt AND
                //  2)  either
                //      a) PrivateGetIDsOfNames() does Not return E_NOTIMPL
                //      OR
                //      b) we don't know if a) is true or not yet
                //

                ASSERT(pExtension->pADsExt);

                hr = (pExtension->pADsExt)->PrivateGetIDsOfNames(
                          iid,
                          rgszNames,
                          cNames,
                          lcid,
                          rgdispid
                          );

                if (SUCCEEDED(hr)) {

                    //
                    // check & prefix extension id to dispid(s) returned
                    // by extension
                    //

                    hr = CheckAndPrefixExtIDArray(
                                pExtension->dwExtensionID,
                                cNames,
                                rgdispid
                                );

                    if (SUCCEEDED(hr) )
                    {
                        RRETURN(hr);
                    }

                    //
                    // if cannot prefix extension id because NOT ALL
                    // dispids returned by PrivateGetIDsOfNames() are
                    // valid, this extension does not support this property
                    // or method -> try next extension
                    //
                    // If the extension returns ANY invalid dispids, then
                    // we treat the extension as invalid.  This way, we can
                    // see if a later extension exists that supports this
                    // property or method, instead of just bailing out.
                    // This is important, since there
                    // can be multiple extensions and we are supposed to
                    // "find the first extension which implements a function"
                    // (per ADSI SDK).
                    //
                }

                else if (hr == E_NOTIMPL) {

                    //
                    // extension object does not support the optional
                    // IADsExtension::PrivateGetIDsOfNames()/PrivateInvoke()
                    // -> remember this in cache & try next extension object
                    //

                    pExtension->fDisp = FALSE;
                }

                else {

                    //
                    // extens'n object supports PrivateGetIDsOfNames()/Invoke()
                    // but does not know about this property or method
                    // -> try next extension object
                    //
                }

            } // end "if (pExtension->pADs && pExtension->fDisp)"

            pExtension = pExtension->pNext;

        } // end while

    }


    //
    // Unify the final error code retuned to ADSI client to DISP_E_UNKNOWNNAME
    //

    if ( FAILED(hr) && hr!=E_OUTOFMEMORY) {

        hr = DISP_E_UNKNOWNNAME;
    }

    RRETURN(hr);
}


STDMETHODIMP
CADsExtMgr::Invoke(
    DISPID dispidMember,
    REFIID iid,
    LCID lcid,
    unsigned short wFlags,
    DISPPARAMS FAR* pdispparams,
    VARIANT FAR* pvarResult,
    EXCEPINFO FAR* pexcepinfo,
    unsigned int FAR* puArgErr
    )
{
    DWORD dwExtensionId = 0;
    HRESULT hr = S_OK;
    PEXTENSION_ENTRY pExtension = NULL;
    IPrivateDispatch * pPrivDisp = NULL;
    DISPID rgExtDispid = DISPID_UNKNOWN;

    //
    // This could be a special dispatch id - pass it to
    // the aggregator
    //

    if (dispidMember <= 0) {

        hr = _pDispMgr->Invoke(
                    dispidMember,
                    iid,
                    lcid,
                    wFlags,
                    pdispparams,
                    pvarResult,
                    pexcepinfo,
                    puArgErr
                    );
        RRETURN(hr);

    }

    //
    // It is not a special dispatch id, so compute the extension
    // id and pass it to the appropriate dispatch manager
    //

    dwExtensionId = EXTRACT_EXTENSION_ID(dispidMember);

    if (!dwExtensionId) {

        hr = _pDispMgr->Invoke(
                    dispidMember,
                    iid,
                    lcid,
                    wFlags,
                    pdispparams,
                    pvarResult,
                    pexcepinfo,
                    puArgErr
                    );
        RRETURN(hr);

    }

    if (!_pClassEntry) {

        RRETURN(DISP_E_MEMBERNOTFOUND);
    }


    pExtension = _pClassEntry->pExtensionHead;

    rgExtDispid = REMOVE_EXTENSION_ID(dispidMember);

    while (pExtension) {

        if (dwExtensionId == pExtension->dwExtensionID) {

            if (pExtension->fDisp) {

                //
                // fDisp = TRUE indicates
                //  1)  extension supports pADsExt AND
                //  2)  either
                //      a) PrivateGetIDsOfNames() does Not return E_NOTIMPL
                //      OR
                //      b) we don't know if a) is true or not yet
                //

                ASSERT(pExtension->pADsExt);

                hr = (pExtension->pADsExt)->PrivateInvoke(
                        rgExtDispid,
                        iid,
                        lcid,
                        wFlags,
                        pdispparams,
                        pvarResult,
                        pexcepinfo,
                        puArgErr
                        );
                RRETURN(hr);

            } else {

                //
                // A dwExtensionId match indicates THIS extens'n has returned
                // a valid dispid to clients thru' pADs->PrivateGetIDsOfNames.
                // Thus, fDisp should be TURE.
                //
                // But since dispid goes thru' clients before passed back to
                // PrivateInovke(), don't ASSERT in case of clients errors.
                //

                RRETURN(DISP_E_MEMBERNOTFOUND);
            }
        }

        pExtension = pExtension->pNext;

    } // end while

    RRETURN(DISP_E_MEMBERNOTFOUND);
}


HRESULT
CADsExtMgr::CheckAndPrefixExtIDArray(
    IN      DWORD dwExtensionID,
    IN      unsigned int cDispids,
    IN OUT  DISPID * rgDispids
    )
{

    HRESULT hrEach = S_OK;
    HRESULT hrAll = S_OK;


    ASSERT_VALID_EXTENSION_ID(dwExtensionID);


    for (unsigned int i = 0; i<cDispids; i++)
    {
        hrEach = CheckAndPrefixExtID(
                    dwExtensionID,
                    rgDispids[i],
                    rgDispids+i
                    );

        if (FAILED(hrEach))
        {
            hrAll = E_FAIL;

            //
            // The entire operation is considered as failure as a whole.
            // But continue to get other dispid s.t. debugger or user knows
            // which dispid in the array is causing problem -> DISPID_UNKOWN
            //
        }
    }

    RRETURN(hrAll);

}


HRESULT
CADsExtMgr::CheckAndPrefixExtID(
    IN      DWORD   dwExtensionID,
    IN      DISPID  dispid,
    IN OUT  DISPID  * pNewDispid
    )
{
    ASSERT(pNewDispid);

    if  ( (dispid>= ADS_EXT_MINEXTDISPID) &&
          (dispid<= ADS_EXT_MAXEXTDISPID) )
    {
        *pNewDispid = PREFIX_EXTENSION_ID(dwExtensionID, dispid) ;

        RRETURN(S_OK);
    }
    else
    {
        *pNewDispid = DISPID_UNKNOWN;

        RRETURN(E_FAIL);
    }

}


//+------------------------------------------------------------------------
//
//  Function:   CADsExtMgr::FinalInitializeExtensions
//
//  Synopsis:   At this point we call Operate on all the extensions
//           so that they can do initialization stuff that
//
//
//
//  Arguments: None
//
//  AjayR - added on 1-30-99.
//-------------------------------------------------------------------------
HRESULT
CADsExtMgr::FinalInitializeExtensions()
{
    HRESULT hr = S_OK;
    PEXTENSION_ENTRY pExtEntry = NULL;
    VARIANT varDummy;
    VariantInit(&varDummy);

    if (!_pClassEntry || !(pExtEntry=_pClassEntry->pExtensionHead) ) {
        RRETURN(S_OK);
    }

    while (pExtEntry) {

        //
        // Call operate only if the extension supports the interface
        //
        if (pExtEntry->pADsExt) {

            pExtEntry->pADsExt->Operate(
                                ADS_EXT_INITIALIZE_COMPLETE,
                                varDummy,
                                varDummy,
                                varDummy
                                );
        }

        //
        // we cannot really do much if there is a failure here
        //

        pExtEntry = pExtEntry->pNext;

    }   // end while


    //
    // Cannot fail because of one bad extension.
    //
    RRETURN(S_OK);      // "okay" error if any, optional support
}