//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1999.
//
//  File:       C O M P . C P P
//
//  Contents:   The module implements the operations that are valid on
//              network component datatypes.
//
//  Notes:
//
//  Author:     shaunco   15 Jan 1999
//
//----------------------------------------------------------------------------

#include <pch.h>
#pragma hdrstop
#include "comp.h"
#include "icomp.h"
#include "ncreg.h"
#include "ncsetup.h"
#include "ncstring.h"
#include "util.h"

// NULL entries means we don't use the network subtree for that class.
//
const PCWSTR MAP_NETCLASS_TO_NETWORK_SUBTREE[] =
{
    L"System\\CurrentControlSet\\Control\\Network\\{4d36e972-e325-11ce-bfc1-08002be10318}",
    L"System\\CurrentControlSet\\Control\\Network\\{6BDD1FC5-810F-11D0-BEC7-08002BE2092F}",
    L"System\\CurrentControlSet\\Control\\Network\\{4d36e975-e325-11ce-bfc1-08002be10318}",
    L"System\\CurrentControlSet\\Control\\Network\\{4d36e973-e325-11ce-bfc1-08002be10318}",
    L"System\\CurrentControlSet\\Control\\Network\\{4d36e974-e325-11ce-bfc1-08002be10318}",
    NULL,
    NULL
};

// map of NETCLASS enum to GUIDs for class
//
const GUID* MAP_NETCLASS_TO_GUID[] =
{
    &GUID_DEVCLASS_NET,
    &GUID_DEVCLASS_INFRARED,
    &GUID_DEVCLASS_NETTRANS,
    &GUID_DEVCLASS_NETCLIENT,
    &GUID_DEVCLASS_NETSERVICE,
    &GUID_DEVCLASS_UNKNOWN,
    &GUID_DEVCLASS_UNKNOWN
};

const WCHAR c_szTempNetcfgStorageForUninstalledEnumeratedComponent[] =
    L"System\\CurrentControlSet\\Control\\Network\\Uninstalled\\";

//static
HRESULT
CComponent::HrCreateInstance (
    IN const BASIC_COMPONENT_DATA* pData,
    IN DWORD dwFlags,
    IN const OBO_TOKEN* pOboToken, OPTIONAL
    OUT CComponent** ppComponent)
{
    ULONG cbInfId;
    ULONG cbPnpId;

    Assert (pData);
    Assert (pData->pszInfId && *pData->pszInfId);
    Assert (FIsValidNetClass(pData->Class));
    Assert (FImplies(FIsEnumerated(pData->Class),
                pData->pszPnpId && *pData->pszPnpId));
    Assert (FImplies(pData->pszPnpId, *pData->pszPnpId));
    Assert (FImplies(pData->dwDeipFlags, FIsEnumerated(pData->Class)));
    Assert ((CCI_DEFAULT == dwFlags) ||
            (CCI_ENSURE_EXTERNAL_DATA_LOADED == dwFlags));
    Assert (GUID_NULL != pData->InstanceGuid);

    cbInfId = CbOfSzAndTerm (pData->pszInfId);
    cbPnpId = CbOfSzAndTermSafe (pData->pszPnpId);

    HRESULT hr = E_OUTOFMEMORY;
    CComponent* pComponent = new(extrabytes, cbInfId + cbPnpId) CComponent;
    if (pComponent)
    {
        hr = S_OK;
        ZeroMemory (pComponent, sizeof(CComponent));

        pComponent->m_InstanceGuid  = pData->InstanceGuid;
        pComponent->m_Class         = pData->Class;
        pComponent->m_dwCharacter   = pData->dwCharacter;
        pComponent->m_dwDeipFlags   = pData->dwDeipFlags;

        pComponent->m_pszInfId = (PCWSTR)(pComponent + 1);
        wcscpy ((PWSTR)pComponent->m_pszInfId, pData->pszInfId);
        _wcslwr ((PWSTR)pComponent->m_pszInfId);

        AddOrRemoveDontExposeLowerCharacteristicIfNeeded (pComponent);

        if (cbPnpId)
        {
            pComponent->m_pszPnpId = (PCWSTR)((BYTE*)pComponent->m_pszInfId
                                                + cbInfId);
            wcscpy ((PWSTR)pComponent->m_pszPnpId, pData->pszPnpId);
        }

        if (dwFlags & CCI_ENSURE_EXTERNAL_DATA_LOADED)
        {
            // Let's ensure we can successfully read all of the external
            // data that the component's INF dumped under the instance
            // key.  Failure here means the INF wasn't proper in some
            // way required for us to consider this a valid component.
            //
            hr = pComponent->Ext.HrEnsureExternalDataLoaded ();
        }

        if ((S_OK == hr) && pOboToken)
        {
            // Add a reference by the obo token if we were given one.
            //
            hr = pComponent->Refs.HrAddReferenceByOboToken (pOboToken);
        }

        if (S_OK != hr)
        {
            delete pComponent;
            pComponent = NULL;
        }
    }

    *ppComponent = pComponent;

    TraceHr (ttidError, FAL, hr, FALSE,
        "CComponent::HrCreateAndInitializeInstance");
    return hr;
}

CComponent::~CComponent()
{
    // If we have a cached INetCfgComponent interface, we need to tell it
    // that we (as the component it represents) no longer exist.  Then we
    // need to release the interface, of course.
    //
    ReleaseINetCfgComponentInterface ();
    if(m_hinf)
    {
        SetupCloseInfFile (m_hinf);
    }
}

VOID
CComponent::ReleaseINetCfgComponentInterface ()
{
    Assert (this);

    if (m_pIComp)
    {
        Assert (this == m_pIComp->m_pComponent);
        m_pIComp->m_pComponent = NULL;
        ReleaseObj (m_pIComp->GetUnknown());
        m_pIComp = NULL;
    }
}

INetCfgComponent*
CComponent::GetINetCfgComponentInterface () const
{
    Assert (this);
    Assert (m_pIComp);
    return m_pIComp;
}

BOOL
CComponent::FCanDirectlyBindTo (
    IN const CComponent* pLower,
    OUT const WCHAR** ppStart,
    OUT ULONG* pcch) const
{
    BOOL fCanBind;

    // If this component is a filter and the lower is an adapter,
    // they can bind (by definition) unless the adapter has an upper range
    // that is excluded by the filter.
    //
    if (FIsFilter() && FIsEnumerated(pLower->Class()))
    {
        fCanBind = TRUE;

        // If the filter has the FilterMediaTypes value specified,
        // then we can bind only if we have a match with the adapters
        // lower range.
        //
        if (Ext.PszFilterMediaTypes())
        {
            fCanBind = FSubstringMatch (Ext.PszFilterMediaTypes(),
                            pLower->Ext.PszLowerRange(), NULL, NULL);
        }

        // If we (the filter) have a list of lower interface to exclude,
        // then we can bind only if we don't have a match.
        //
        if (fCanBind && Ext.PszLowerExclude())
        {
            fCanBind = !FSubstringMatch (Ext.PszLowerExclude(),
                            pLower->Ext.PszUpperRange(), NULL, NULL);
        }

        // If the filter can bind to the adapter, and the caller wants
        // the interface name, it will be the first interface the adapter
        // supports.
        //
        if (fCanBind && ppStart && pcch)
        {
            PCWSTR pStart;
            PCWSTR pEnd;

            pStart = pLower->Ext.PszUpperRange();
            Assert (pStart);

            while (*pStart && (*pStart == L' ' || *pStart == L','))
            {
                pStart++;
            }

            pEnd = pStart;
            while (*pEnd && *pEnd != L' ' && *pEnd != L',')
            {
                pEnd++;
            }

            *ppStart = pStart;
            *pcch = pEnd - pStart;
        }
    }
    else
    {
        fCanBind = FSubstringMatch (
                    Ext.PszLowerRange(),
                    pLower->Ext.PszUpperRange(), ppStart, pcch);
    }

    return fCanBind;
}

BOOL
CComponent::FIsBindable () const
{
    return (0 != _wcsicmp(L"nolower", Ext.PszLowerRange())) ||
           (0 != _wcsicmp(L"noupper", Ext.PszUpperRange()));
}

BOOL
CComponent::FIsWanAdapter () const
{
    Assert (this);

    return (NC_NET == Class()) &&
            FSubstringMatch (Ext.PszLowerRange(), L"wan", NULL, NULL);
}

HRESULT
CComponent::HrGetINetCfgComponentInterface (
    IN CImplINetCfg* pINetCfg,
    OUT INetCfgComponent** ppIComp)
{
    HRESULT hr = S_OK;

    Assert (this);
    Assert (pINetCfg);
    Assert (ppIComp);

    // Caller's are responsible for ensuring that if an interface is about
    // to be handed out, and the external data has been loaded, that the
    // data has been loaded successfully.  If we handed out an interface
    // and the data was NOT loaded successfully, it just means we are doomed
    // to fail later when the client of the interface calls a method that
    // requires that data.
    //
    Assert (Ext.FLoadedOkayIfLoadedAtAll());

    // If we don't yet have the cached INetCfgComponent for ourself,
    // create it and hang onto a reference.
    //
    if (!m_pIComp)
    {
        hr = CImplINetCfgComponent::HrCreateInstance (
                pINetCfg, this, &m_pIComp);
    }

    // AddRef and return a copy for the caller.
    //
    if (S_OK == hr)
    {
        AddRefObj (m_pIComp->GetUnknown());
        *ppIComp = m_pIComp;
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CComponent::HrGetINetCfgComponentInterface");
    return hr;
}

HRESULT
CComponent::HrOpenDeviceInfo (
    OUT HDEVINFO* phdiOut,
    OUT SP_DEVINFO_DATA* pdeidOut) const
{
    HRESULT hr;

    Assert (this);
    Assert (phdiOut);
    Assert (pdeidOut);

    hr = ::HrOpenDeviceInfo (
            Class(),
            m_pszPnpId,
            phdiOut,
            pdeidOut);

    TraceHr (ttidError, FAL, hr, SPAPI_E_NO_SUCH_DEVINST == hr,
        "CComponent::HrOpenDeviceInfo (%S)", m_pszPnpId);
    return hr;
}

HRESULT
CComponent::HrOpenInstanceKey (
    IN REGSAM samDesired,
    OUT HKEY* phkey,
    OUT HDEVINFO* phdiOut OPTIONAL,
    OUT SP_DEVINFO_DATA* pdeidOut OPTIONAL) const
{
    HRESULT hr;

    Assert (this);
    Assert (phkey);

    hr = HrOpenComponentInstanceKey (
            Class(),
            m_InstanceGuid,
            m_pszPnpId,
            samDesired,
            phkey,
            phdiOut,
            pdeidOut);

    TraceHr (ttidError, FAL, hr,
        (SPAPI_E_NO_SUCH_DEVINST == hr),
        "CComponent::HrOpenInstanceKey (%S)", PszGetPnpIdOrInfId());
    return hr;
}

HRESULT
CComponent::HrOpenServiceKey (
    IN REGSAM samDesired,
    OUT HKEY* phkey) const
{
    HRESULT hr;
    WCHAR szServiceSubkey [_MAX_PATH];

    Assert (this);
    Assert (phkey);
    Assert (FHasService());

    *phkey = NULL;

    wcscpy (szServiceSubkey, REGSTR_PATH_SERVICES);
    wcscat (szServiceSubkey, L"\\");
    wcscat (szServiceSubkey, Ext.PszService());

    hr = HrRegOpenKeyEx (
            HKEY_LOCAL_MACHINE,
            szServiceSubkey,
            samDesired,
            phkey);

    TraceHr (ttidError, FAL, hr, FALSE,
        "CComponent::HrOpenServiceKey (%S)", Ext.PszService());
    return hr;
}

HRESULT
CComponent::HrStartOrStopEnumeratedComponent (
    IN DWORD dwFlag /* DICS_START or DICS_STOP */) const
{
    HRESULT hr;
    HDEVINFO  hdi;
    SP_DEVINFO_DATA deid;

    Assert (this);
    Assert (FIsEnumerated(Class()));
    Assert ((DICS_START == dwFlag) || (DICS_STOP == dwFlag));

    hr = HrOpenDeviceInfo (&hdi, &deid);
    if (S_OK == hr)
    {
        if (m_dwDeipFlags)
        {
            TraceTag (ttidBeDiag,
                "Using SP_DEVINSTALL_PARAMS.Flags = 0x%08x for %S",
                m_dwDeipFlags,
                m_pszPnpId);

            (VOID) HrSetupDiSetDeipFlags (
                        hdi, &deid,
                        m_dwDeipFlags, SDDFT_FLAGS, SDFBO_OR);
        }

        // $HACK  SetupDi does not honor the DI_DONOTCALLCONFIGMG flag
        // so we can't call it if it is set. If we don't start the device
        // we will return NETCFG_S_REBOOT.
        //
        hr = NETCFG_S_REBOOT;
        if (!(DI_DONOTCALLCONFIGMG & m_dwDeipFlags))
        {
            hr = HrSetupDiSendPropertyChangeNotification (
                    hdi, &deid,
                    dwFlag,
                    DICS_FLAG_CONFIGSPECIFIC,
                    0);
        }

        SetupDiDestroyDeviceInfoList (hdi);
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CComponent::HrStartOrStopEnumeratedComponent (%S)", m_pszPnpId);
    return hr;
}

//-----------------------------------------------------------------------
// A convenience method to get the handle to the components inf file.
// If the file has been opened previously, the cached handle is returned.
// Otherwise, the file is opened and the handle is returned.

HRESULT
CComponent::HrOpenInfFile(
    OUT HINF* phinf) const
{
    HRESULT hr = S_OK;
    HKEY  hkeyInstance = NULL;
    WCHAR szInfPath[_MAX_PATH];
    DWORD cbPath = sizeof (szInfPath);

    Assert(phinf);
    *phinf = NULL;

    if (NULL == m_hinf)
    {
        hr = HrOpenInstanceKey (KEY_READ, &hkeyInstance, NULL, NULL);

        if (S_OK == hr)
        {
            hr = HrRegQuerySzBuffer (hkeyInstance, L"InfPath", szInfPath, &cbPath);

            if (S_OK == hr)
            {
                // open the component's inf file
                hr = HrSetupOpenInfFile (szInfPath, NULL, INF_STYLE_WIN4,
                         NULL, &m_hinf);
            }
            RegSafeCloseKey (hkeyInstance);
        }
    }
    *phinf = m_hinf;

    TraceHr (ttidError, FAL, hr, FALSE,
        "CComponent::HrOpenInfFile (%S)", PszGetPnpIdOrInfId());

    return hr;
}

NETCLASS
NetClassEnumFromGuid (
    const GUID& guidClass)
{
    NETCLASS Class;

    if (GUID_DEVCLASS_NET == guidClass)
    {
        Class = NC_NET;
    }
    else if (GUID_DEVCLASS_INFRARED == guidClass)
    {
        Class = NC_INFRARED;
    }
    else if (GUID_DEVCLASS_NETTRANS == guidClass)
    {
        Class = NC_NETTRANS;
    }
    else if (GUID_DEVCLASS_NETCLIENT == guidClass)
    {
        Class = NC_NETCLIENT;
    }
    else if (GUID_DEVCLASS_NETSERVICE == guidClass)
    {
        Class = NC_NETSERVICE;
    }
    else
    {
        Class = NC_INVALID;
    }

    return Class;
}