//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1999.
//
//  File:       F I L T D E V S . C P P
//
//  Contents:   Implements the basic datatype for a collection of filter
//              devices.
//
//  Notes:
//
//  Author:     shaunco   15 Jan 1999
//
//----------------------------------------------------------------------------

#include <pch.h>
#pragma hdrstop
#include "classinst.h"
#include "filtdevs.h"
#include "nceh.h"
#include "ncreg.h"
#include "ncstl.h"
#include "ncsetup.h"


CFilterDevices::CFilterDevices (
    IN CNetConfigCore* pCore)
{
    Assert (pCore);

    ZeroMemory (this, sizeof(*this));

    m_pCore = pCore;
}

CFilterDevices::~CFilterDevices ()
{
    // Free had better have been called before this.
    //
    Assert (this);
    Assert (!m_hdi);
    Assert (!m_pmszFilterClasses);
    Assert (empty());
}

HRESULT
CFilterDevices::HrInsertFilterDevice (
    IN CFilterDevice* pDevice)
{
    HRESULT hr;

    Assert (this);
    Assert (pDevice);

    // Assert there is not already a device in the list with the
    // same instance guid.
    //
    Assert (!PFindFilterDeviceByInstanceGuid (pDevice->m_szInstanceGuid));

    // Assert there is not already a device in the list with the
    // same parent filter AND the same filtered adapter.
    //
    Assert (!PFindFilterDeviceByAdapterAndFilter (
                pDevice->m_pAdapter,
                pDevice->m_pFilter));

    NC_TRY
    {
        push_back (pDevice);
        hr = S_OK;
    }
    NC_CATCH_ALL
    {
        hr = E_OUTOFMEMORY;
    }

    TraceHr (ttidError, FAL, hr, FALSE, "CFilterDevices::HrInsertFilterDevice");
    return hr;
}

HRESULT
CFilterDevices::HrPrepare ()
{
    HRESULT hr;
    HKEY hkeyNetwork;

    // Reserve room for 8 different filters in our internal member.
    // We use this component list at various times as "scratch space" when
    // figuring out which filters are enabled for an adapter.
    //
    hr = m_Filters.HrReserveRoomForComponents (8);
    if (S_OK != hr)
    {
        goto finished;
    }

    hr = m_BindPathsToRebind.HrReserveRoomForBindPaths (8);
    if (S_OK != hr)
    {
        goto finished;
    }

    // Load the FilterClasses multi-sz.
    //

    hr = HrOpenNetworkKey (KEY_READ, &hkeyNetwork);

    if (S_OK == hr)
    {
        hr = HrRegQueryMultiSzWithAlloc (
                hkeyNetwork,
                L"FilterClasses",
                &m_pmszFilterClasses);

        if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
        {
            Assert (!m_pmszFilterClasses);
            hr = S_OK;
        }

        RegCloseKey (hkeyNetwork);
    }

finished:

    TraceHr (ttidError, FAL, hr, FALSE, "CFilterDevices::HrPrepare");
    return hr;
}

CFilterDevice*
CFilterDevices::PFindFilterDeviceByAdapterAndFilter (
    IN const CComponent* pAdapter,
    IN const CComponent* pFilter) const
{
    const_iterator  iter;
    CFilterDevice*  pDevice;

    Assert (this);
    Assert (pAdapter);
    Assert (pFilter);
    Assert (FIsEnumerated(pAdapter->Class()));
    Assert (NC_NETSERVICE == pFilter->Class());
    Assert (pFilter->FIsFilter());

    for (iter = begin(); iter != end(); iter++)
    {
        pDevice = *iter;
        Assert (pDevice);

        if ((pAdapter == pDevice->m_pAdapter) &&
            (pFilter  == pDevice->m_pFilter))
        {
            return pDevice;
        }
    }

    return NULL;
}

DWORD
CFilterDevices::MapFilterClassToOrdinal (
    IN PCWSTR pszFilterClass)
{
    DWORD Ordinal;
    DWORD dwIndex;
    DWORD cStrings;

    Assert (pszFilterClass);
#if DBG
    Ordinal = 0;
#endif

    // If the class is found in the list, return its position.
    //
    if (FGetSzPositionInMultiSzSafe (
            pszFilterClass,
            m_pmszFilterClasses,
            &dwIndex,
            NULL,
            &cStrings))
    {
        Ordinal = dwIndex + 1;
    }
    else
    {
        HRESULT hr;
        PWSTR pmszNew;
        BOOL fChanged;

        // We're adding another string, so compute the new ordinal value
        // for return.
        //
        Ordinal = cStrings + 1;

        // String was not found, so we append it at the end.
        // It is important to insert at the end so we don't have to
        // change the ordinals of any existing filters that already
        // had their ordinal computed.
        //
        hr = HrAddSzToMultiSz (pszFilterClass, m_pmszFilterClasses,
                STRING_FLAG_ENSURE_AT_END, 0, &pmszNew, &fChanged);

        if (S_OK == hr)
        {
            HKEY hkeyNetwork;

            // It better have changed because we didn't find the string
            // above.
            //
            Assert (fChanged);

            // Out with the old. In with the new.
            //
            MemFree (m_pmszFilterClasses);
            m_pmszFilterClasses = pmszNew;

            // Save it back to the registry.
            //
            hr = HrOpenNetworkKey (KEY_WRITE, &hkeyNetwork);

            if (S_OK == hr)
            {
                hr = HrRegSetMultiSz (
                        hkeyNetwork,
                        L"FilterClasses",
                        m_pmszFilterClasses);

                Assert (S_OK == hr);

                RegCloseKey (hkeyNetwork);
            }
        }
    }

    // By definition, Ordinal is 1-based.  This is so that when stored
    // in CComponent, we know we have to load the filter class and get
    // its ordinal if CComponent::FilterClassOrdinal is zero.  i.e. zero
    // is a sentinel value that means we need to do work and when non-zero
    // means we don't have to do that work again.
    //
    Assert (Ordinal != 0);
    return Ordinal;
}

CFilterDevice*
CFilterDevices::PFindFilterDeviceByInstanceGuid (
    IN PCWSTR pszInstanceGuid) const
{
    const_iterator  iter;
    CFilterDevice*  pDevice;

    Assert (this);
    Assert (pszInstanceGuid && *pszInstanceGuid);

    for (iter = begin(); iter != end(); iter++)
    {
        pDevice = *iter;
        Assert (pDevice);

        if (0 == wcscmp(pszInstanceGuid, pDevice->m_szInstanceGuid))
        {
            return pDevice;
        }
    }

    return NULL;
}

HRESULT
CFilterDevices::HrLoadFilterDevice (
    IN SP_DEVINFO_DATA* pdeid,
    IN HKEY hkeyInstance,
    IN PCWSTR pszFilterInfId,
    OUT BOOL* pfRemove)
{
    HRESULT hr;
    CComponent* pAdapter;
    CComponent* pFilter;
    WCHAR szInstanceGuid [c_cchGuidWithTerm];
    DWORD cbBuffer;

    Assert (pszFilterInfId && *pszFilterInfId);
    Assert (pfRemove);

    *pfRemove = FALSE;

    // Initialize these to NULL.  If we don't find them below, they will
    // remain NULL and this will tell us something.
    //
    pAdapter = NULL;
    pFilter = NULL;

    cbBuffer = sizeof(szInstanceGuid);
    hr = HrRegQuerySzBuffer (
            hkeyInstance,
            L"NetCfgInstanceId",
            szInstanceGuid, &cbBuffer);

    if (S_OK == hr)
    {
        HKEY hkeyLinkage;

        // Read the RootDevice registry value for this filter device.  The
        // last entry in that multi-sz will be the bindname of the adapter
        // being filtered.
        //
        hr = HrRegOpenKeyEx (
                hkeyInstance,
                L"Linkage",
                KEY_READ,
                &hkeyLinkage);

        if (S_OK == hr)
        {
            PWSTR pmszRootDevice;

            hr = HrRegQueryMultiSzWithAlloc (
                    hkeyLinkage,
                    L"RootDevice",
                    &pmszRootDevice);

            if (S_OK == hr)
            {
                PCWSTR pszScan;
                PCWSTR pszLastDevice = NULL;

                // Scan to the last string in the multi-sz and note it.
                //
                for (pszScan = pmszRootDevice;
                     *pszScan;
                     pszScan += wcslen(pszScan) + 1)
                {
                    pszLastDevice = pszScan;
                }

                // The last string in the multi-sz is the bindname of the
                // adapter being filtered.
                //
                if (pszLastDevice)
                {
                    pAdapter = m_pCore->Components.PFindComponentByBindName (
                                                    NC_NET, pszLastDevice);
                    if (!pAdapter)
                    {
                        hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
                    }
                }

                MemFree (pmszRootDevice);
            }

            RegCloseKey (hkeyLinkage);
        }

        if (S_OK == hr)
        {
            // Should have the adapter if no error.
            //
            Assert (pAdapter);

            // Get the enabled filters for the adapter.
            //
            hr = m_pCore->HrGetFiltersEnabledForAdapter (pAdapter, &m_Filters);
            if (S_OK == hr)
            {
                // Use pszFilterInfId to find the parent filter component for
                // this filter device.  If it is not found, it probably means
                // the entire filter is in the process of being removed.
                // (Or the registry was messed with.)
                //
                pFilter = m_pCore->Components.PFindComponentByInfId (
                                                pszFilterInfId, NULL);

                // If the filter corresponding to this device is still
                // installed and is enabled over the adapter, then we'll
                // insert the device into our list.  Otherwise, we're going
                // to remove it.
                //
                if (pFilter && m_Filters.FComponentInList (pFilter))
                {
                    CFilterDevice* pFilterDevice;

                    // Create an instance of the filter device class to
                    // represent this filter device.
                    //
                    hr = CFilterDevice::HrCreateInstance (
                            pAdapter,
                            pFilter,
                            pdeid,
                            szInstanceGuid,
                            &pFilterDevice);

                    if (S_OK == hr)
                    {
                        // Add the filter device to our list of filter devices.
                        //
                        hr = HrInsertFilterDevice (pFilterDevice);

                        if (S_OK != hr)
                        {
                            delete pFilterDevice;
                        }
                    }
                }
                else
                {
                    *pfRemove = TRUE;

                    Assert (pszFilterInfId && *pszFilterInfId);
                    Assert (pAdapter);

                    g_pDiagCtx->Printf (ttidBeDiag,
                        "   Removing filter device for %S over %S adapter\n",
                        pszFilterInfId,
                        pAdapter->m_pszPnpId);

                    // Since we will be removing a filter device from the
                    // chain, we need to rebind the protocols above the
                    // adapter we are removing the filter device for.
                    //
                    // So, get the upper bindings of the adapter (bindpaths
                    // are only 2 levels deep) and add them to the bind set
                    // that we will rebind later on.
                    //
                    hr = m_pCore->HrGetComponentUpperBindings (
                            pAdapter,
                            GBF_ADD_TO_BINDSET | GBF_PRUNE_DISABLED_BINDINGS,
                            &m_BindPathsToRebind);
                }
            }
        }
    }

    TraceHr (ttidError, FAL, hr,
        HRESULT_FROM_WIN32(ERROR_NOT_FOUND) == hr,
        "CFilterDevices::HrLoadFilterDevice");
    return hr;
}

VOID
CFilterDevices::LoadAndRemoveFilterDevicesIfNeeded ()
{
    HRESULT hr;
    SP_DEVINFO_DATA deid;
    DWORD dwIndex;
    DWORD cbBuffer;
    WCHAR szFilterInfId [_MAX_PATH];

    Assert (this);
    Assert (m_pCore);
    Assert (!m_hdi);
    Assert (empty());

    // Filter devices can only be of net class.
    //
    hr = HrSetupDiGetClassDevs (&GUID_DEVCLASS_NET, NULL, NULL,
            DIGCF_PROFILE, &m_hdi);

    if (S_OK != hr)
    {
        return;
    }

    Assert (m_hdi);

    // Enumerate all net class devices from setupapi.
    //
    for (dwIndex = 0; S_OK == hr; dwIndex++)
    {
        hr = HrSetupDiEnumDeviceInfo (m_hdi, dwIndex, &deid);

        if (S_OK == hr)
        {
            HKEY hkeyInstance;

            hr = HrSetupDiOpenDevRegKey (
                    m_hdi, &deid,
                    DICS_FLAG_GLOBAL, 0, DIREG_DRV,
                    KEY_READ, &hkeyInstance);

            if (S_OK == hr)
            {
                // If the device has a "FilterInfId" value under its
                // instance key, its one of ours.
                //
                cbBuffer = sizeof(szFilterInfId);
                hr = HrRegQuerySzBuffer (
                        hkeyInstance,
                        L"FilterInfId",
                        szFilterInfId,
                        &cbBuffer);

                if (S_OK == hr)
                {
                    BOOL fRemove;

                    // Load the rest of the filter device, and add it to
                    // our list.  If this fails for any reason, remove the
                    // filter device because its of no use to us anymore.
                    //
                    hr = HrLoadFilterDevice (
                            &deid,
                            hkeyInstance,
                            szFilterInfId,
                            &fRemove);

                    if ((S_OK != hr) || fRemove)
                    {
                        if (S_OK != hr)
                        {
                            g_pDiagCtx->Printf (ttidBeDiag,
                                "   Removing filter device for %S\n",
                                szFilterInfId);
                        }

                        (VOID) HrCiRemoveFilterDevice (m_hdi, &deid);
                        hr = S_OK;
                    }
                }

                //else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
                //{
                    // Not a filter device.  Skip it.
                //}

                RegCloseKey (hkeyInstance);
            }

            // Allow the loop to continue;
            //
            hr = S_OK;
        }
    }
    if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
    {
        hr = S_OK;
    }

    g_pDiagCtx->Printf (ttidBeDiag, "   Loaded %d filter devices\n", size());
}

VOID
CFilterDevices::InstallFilterDevicesIfNeeded ()
{
    HRESULT hr;
    CComponentList::iterator iterAdapter;
    CComponentList::iterator iterFilter;
    CComponent* pAdapter;
    CComponent* pFilter;
    HKEY hkeyInstance;
    HKEY hkeyNdi;
    DWORD cbBuffer;
    BOOL fAddDevice;
    BOOL fAddedDeviceForAdapter;
    WCHAR szFilterDeviceInfId [_MAX_PATH];
    WCHAR szFilterClass [_MAX_PATH];

    Assert (this);
    Assert (m_pCore);

    // If, for some reason, we couldn't get m_hdi up in
    // RemoveFilterDevicesIfNeeded, we can't proceed.
    //
    if (!m_hdi)
    {
        return;
    }

    // For all adapters (because filters possibly bind to any adapter)
    // we get the filters enabled for each.  For each on of these filters
    // that don't already have an associated filter device for the adapter,
    // we create a new one and associated it.
    //
    for (iterAdapter  = m_pCore->Components.begin();
         iterAdapter != m_pCore->Components.end();
         iterAdapter++)
    {
        pAdapter = *iterAdapter;
        Assert (pAdapter);

        // Skip components that are not network adapters.
        //
        if (NC_NET != pAdapter->Class())
        {
            continue;
        }

        hr = m_pCore->HrGetFiltersEnabledForAdapter (pAdapter, &m_Filters);

        if (S_OK != hr)
        {
            // More than likely, we are out of memory.
            //
            TraceHr (ttidError, FAL, hr, FALSE,
                "HrGetFiltersEnabledForAdapter failed in "
                "InstallFilterDevicesIfNeeded. Adapter=%S",
                pAdapter->m_pszPnpId);
            break;
        }

        // We haven't yet added any devices for this adapter.
        //
        fAddedDeviceForAdapter = FALSE;

        // For each of the filters enabled for this adapter, install
        // a filter device if needed and make sure the filter has its
        // ordinal position with respect other filters read from the
        // registry.  We need m_dwFilterClassOrdinal to be valid (non-zero)
        // before we sort the filter devices when writing their bindings.
        //
        for (iterFilter  = m_Filters.begin();
             iterFilter != m_Filters.end();
             iterFilter++)
        {
            pFilter = *iterFilter;
            Assert (pFilter);

            // If there isn't a filter device for the current adapter
            // and filter, we need to install one.
            //
            fAddDevice = !PFindFilterDeviceByAdapterAndFilter (
                            pAdapter, pFilter);

            // If we don't need to add a filter device and we already
            // have the ordinal position of the filter, we can continue with
            // the next filter for this adapter.
            //
            if (!fAddDevice && (0 != pFilter->m_dwFilterClassOrdinal))
            {
                continue;
            }

            *szFilterDeviceInfId = 0;

            // Open the instance key of the filter so we can read
            // a few values.
            //
            hr = pFilter->HrOpenInstanceKey (KEY_READ, &hkeyInstance,
                    NULL, NULL);

            if (S_OK == hr)
            {
                // Open the Ndi key.
                //
                hr = HrRegOpenKeyEx (hkeyInstance, L"Ndi",
                        KEY_READ, &hkeyNdi);

                if (S_OK == hr)
                {
                    if (0 == pFilter->m_dwFilterClassOrdinal)
                    {
                        // Read the filter class and convert it to an
                        // ordinal based on its position in the
                        // filter classes list.
                        //
                        cbBuffer = sizeof(szFilterClass);

                        hr = HrRegQuerySzBuffer (hkeyNdi,
                                L"FilterClass",
                                szFilterClass,
                                &cbBuffer);

                        if (S_OK == hr)
                        {
                            pFilter->m_dwFilterClassOrdinal =
                                MapFilterClassToOrdinal (szFilterClass);
                        }
                    }

                    if (fAddDevice)
                    {
                        // Read the ind id of the filter device.
                        //
                        cbBuffer = sizeof(szFilterDeviceInfId);

                        hr = HrRegQuerySzBuffer (
                                hkeyNdi, L"FilterDeviceInfId",
                                szFilterDeviceInfId, &cbBuffer);
                    }

                    RegCloseKey (hkeyNdi);
                }

                RegCloseKey (hkeyInstance);
            }

            if ((S_OK == hr) && fAddDevice)
            {
                CFilterDevice* pFilterDevice;

                Assert (*szFilterDeviceInfId);

                g_pDiagCtx->Printf (ttidBeDiag,
                    "   Installing filter device for %S over %S adapter\n",
                    pFilter->m_pszInfId,
                    pAdapter->m_pszPnpId);


                hr = HrCiInstallFilterDevice (m_hdi,
                        szFilterDeviceInfId,
                        pAdapter,
                        pFilter,
                        &pFilterDevice);

                if (S_OK == hr)
                {
                    hr = HrInsertFilterDevice (pFilterDevice);
                    if (S_OK == hr)
                    {
                        fAddedDeviceForAdapter = TRUE;
                    }
                    else
                    {
                        delete pFilterDevice;
                    }
                }
            }
        }

        // If we added at least one filter device in the chain for this
        // adapter, we'll need to unbind the adapter from whatever it is
        // currently bound to before we start the filter device.
        //
        if (fAddedDeviceForAdapter)
        {
            // So, get the upper bindings of the adapter (bindpaths
            // are only 2 levels deep) and add them to the bind set
            // that we will rebind later on.
            //
            hr = m_pCore->HrGetComponentUpperBindings (
                    pAdapter,
                    GBF_ADD_TO_BINDSET | GBF_PRUNE_DISABLED_BINDINGS,
                    &m_BindPathsToRebind);
        }
    }
}

INT
__cdecl
CompareFilterDevices (
    const VOID* pv1,
    const VOID* pv2)
{
    CFilterDevice* pDevice1 = *((CFilterDevice**)pv1);
    CFilterDevice* pDevice2 = *((CFilterDevice**)pv2);

    if (pDevice1->m_pAdapter == pDevice2->m_pAdapter)
    {
        Assert (pDevice1->m_pFilter != pDevice2->m_pFilter);

        if (pDevice1->m_pFilter->m_dwFilterClassOrdinal ==
            pDevice2->m_pFilter->m_dwFilterClassOrdinal)
        {
            AssertSz (0, "We have two filters of the same class installed.");
            return 0;
        }

        return (pDevice1->m_pFilter->m_dwFilterClassOrdinal <
                pDevice2->m_pFilter->m_dwFilterClassOrdinal)
                    ? -1 : 1;
    }

    return (pDevice1->m_pAdapter < pDevice2->m_pAdapter) ? -1 : 1;

/*
    if (pDevice1->m_pFilter == pDevice2->m_pFilter)
    {
        Assert (pDevice1->m_pAdapter != pDevice2->m_pAdapter);

        return (pDevice1->m_pAdapter < pDevice2->m_pAdapter) ? -1 : 1;
    }

    if (pDevice1->m_pFilter->m_dwFilterClassOrdinal ==
        pDevice2->m_pFilter->m_dwFilterClassOrdinal)
    {
        AssertSz (0, "We have two filters of the same class installed.");
        return 0;
    }

    return (pDevice1->m_pFilter->m_dwFilterClassOrdinal <
            pDevice2->m_pFilter->m_dwFilterClassOrdinal)
                ? -1 : 1;
*/
}

VOID
CFilterDevices::SortForWritingBindings ()
{
    Assert (this);

    // If we're empty, there is nothing to do.
    //
    if (empty())
    {
        return;
    }

    qsort (begin(), size(), sizeof(CFilterDevice*), CompareFilterDevices);
}

VOID
CFilterDevices::StartFilterDevices ()
{
    HRESULT hr;
    CFilterDevices::reverse_iterator iter;
    CFilterDevice* pDevice;

    Assert (this);
    Assert (m_pCore);

    // If we're empty, there is nothing to do.
    //
    if (empty())
    {
        return;
    }

    // If we're not empty, we must have had m_hdi to insert something.
    //
    Assert (m_hdi);

    for (iter = rbegin(); iter != rend(); iter++)
    {
        pDevice = *iter;
        Assert (pDevice);

        g_pDiagCtx->Printf (ttidBeDiag, "   %S filter over %S adapter\n",
            pDevice->m_pFilter->m_pszInfId,
            pDevice->m_pAdapter->m_pszPnpId);

        hr = HrSetupDiSendPropertyChangeNotification (
                m_hdi,
                &pDevice->m_deid,
                DICS_START,
                DICS_FLAG_CONFIGSPECIFIC,
                0);

        if (S_OK != hr)
        {
            g_pDiagCtx->Printf (ttidBeDiag, "   Failed to start filter device for "
                "%S over %S adapter\n",
                pDevice->m_pFilter->m_pszInfId,
                pDevice->m_pAdapter->m_pszPnpId);
        }
    }
}

VOID
CFilterDevices::Free ()
{
    Assert (this);

    MemFree (m_pmszFilterClasses);
    m_pmszFilterClasses = NULL;

    SetupDiDestroyDeviceInfoListSafe (m_hdi);
    m_hdi = NULL;

    FreeCollectionAndItem (*this);

    // Do NOT free m_BindPathsToRebind.  This is used even after ApplyChanges
    // calls Free.
    //
}