//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997.
//
//  File:       T C P I P O B J . C P P
//
//  Contents:   TCP/IP notify object
//
//  Notes:
//
//  Author:     tongl
//
//----------------------------------------------------------------------------

#include "pch.h"
#pragma hdrstop

#include "ncreg.h"
#include "ncstl.h"
#include "tcpconst.h"
#include "tcpipobj.h"
#include "tcputil.h"
#include "tcprsvp.h"

extern const WCHAR c_szBiNdisAtm[];

extern const WCHAR c_szInfId_MS_NetBT[];
extern const WCHAR c_szInfId_MS_NetBT_SMB[];
extern const WCHAR c_szInfId_MS_RSVP[];


HICON   g_hiconUpArrow;
HICON   g_hiconDownArrow;

// Constructor
CTcpipcfg::CTcpipcfg()
:   m_ipaddr(NULL),
    m_pUnkContext(NULL),
    m_pnc(NULL),
    m_pnccTcpip(NULL),
    m_pTcpipPrivate(NULL),
    m_pnccWins(NULL),
    m_fRemoving(FALSE),
    m_fInstalling(FALSE),
    m_fUpgradeCleanupDnsKey(FALSE),
    m_fUpgradeGlobalDnsDomain(FALSE),
    m_pSecondMemoryAdapterInfo(NULL)
{ }

//+---------------------------------------------------------------------------
// INetCfgComponentControl
//
//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::Initialize
//
STDMETHODIMP CTcpipcfg::Initialize(INetCfgComponent * pnccTcpip,
                                   INetCfg * pncNetCfg,
                                   BOOL fInstalling)
{
    HRESULT hr = S_OK;

    Assert(pncNetCfg);
    Assert(pnccTcpip);

    m_fRemoving = FALSE;
    m_fInstalling = FALSE;

    m_fSaveRegistry = FALSE;

    m_ConnType = CONNECTION_UNSET;
    m_fReconfig = FALSE;

    // we havn't changed LmHost file
    m_fLmhostsFileSet = FALSE;

    // IPSec is removed from connection UI
    // we have not change ipsec policy
    //m_fIpsecPolicySet = FALSE;

    // by default, this should be an admin
    m_fRasNotAdmin = FALSE;

    Validate_INetCfgNotify_Initialize(pnccTcpip, pncNetCfg, fInstalling);

    do // psudo loop ( so we don't use goto's on err )
    {
        // in case of Initialize called twice, for resurect the component
        ReleaseObj(m_pnc);
        m_pnc = NULL;

        ReleaseObj(m_pnccTcpip);
        m_pnccTcpip = NULL;

        ReleaseObj(m_pTcpipPrivate);
        m_pTcpipPrivate = NULL;

        ReleaseObj(m_pnccWins);
        m_pnccWins = NULL;

        // store reference to the INetCfg in our object
        m_pnc = pncNetCfg;
        m_pnc->AddRef();

        // Store a reference to the INetCfgComponent for tcpip in our object
        m_pnccTcpip = pnccTcpip;
        m_pnccTcpip->AddRef();

        hr = pnccTcpip->QueryInterface(
                    IID_INetCfgComponentPrivate,
                    reinterpret_cast<void**>(&m_pTcpipPrivate));
        if (FAILED(hr))
            break;

        // Get a copy of the WINS component and store in our object

        // NOTE: WINS client is not necessarily installed yet!
        // we also try to get a pointer at the Install sections
        hr = pncNetCfg->FindComponent(c_szInfId_MS_NetBT,
                            &m_pnccWins);
        if (FAILED(hr))
            break;

        if (S_FALSE == hr) // NetBt not found
        {
            if (!fInstalling) // We are in trouble if NetBt is not there
            {
                TraceError("CTcpipcfg::Initialize - NetBt has not been installed yet", hr);
                break;
            }
            else // We are ok since tcpip will install netbt
            {
                hr = S_OK;
            }
        }

        // Set default global parameters
        hr = m_glbGlobalInfo.HrSetDefaults();
        if (FAILED(hr))
            break;

        // If tcpip is being installed, we don't have any cards to load
        if (!fInstalling)
        {
            // Get list of cards which are currently in the system + if they are bound
            hr = HrGetNetCards();

            if (SUCCEEDED(hr))
            {
                // Let's read parameters from registry
                hr = HrLoadSettings();
            }
        }
    } while(FALSE);

    // Have we got any bound cards ?
    m_fHasBoundCardOnInit = FHasBoundCard();

    if (SUCCEEDED(hr))
    {
        hr = S_OK;
    }

    Validate_INetCfgNotify_Initialize_Return(hr);

    TraceError("CTcpipcfg::Initialize", hr);
    return hr;
}

//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::Validate
//
STDMETHODIMP CTcpipcfg::Validate()
{
    return S_OK;
}

//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::Cancel
//
STDMETHODIMP CTcpipcfg::CancelChanges()
{
    // Note: first memory state is release in destructor

    // If the lmhosts file was set, we need to roll it back to the backup
    if (m_fLmhostsFileSet)
    {
        ResetLmhostsFile();
        m_fLmhostsFileSet = FALSE;
    }

    return S_OK;
}

//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::ApplyRegistryChanges
//
STDMETHODIMP CTcpipcfg::ApplyRegistryChanges()
{
    HRESULT hr = S_OK;
    HRESULT hrTmp = S_OK;

    if (m_fRemoving)
    {
        // (nsun) we should remove the Nt4 duplicate registry here because the cards
        // may have been marked as deleted
        hr = HrRemoveNt4DuplicateRegistry();

        ReleaseObj(m_pnccWins);
        m_pnccWins = NULL;

        // $REVIEW(tongl 9/29/97): Removing ServiceProvider value from registry
        // Remove "Tcpip" from the:
        // System\CurrentControlSet\Control\ServiceProvider\Order\ProviderOrder value
        hrTmp = ::HrRegRemoveStringFromMultiSz(c_szTcpip,
                                            HKEY_LOCAL_MACHINE,
                                            c_szSrvProvOrderKey,
                                            c_szProviderOrderVal,
                                            STRING_FLAG_REMOVE_ALL);
        if (SUCCEEDED(hr))
            hr = hrTmp;

        // Remove RSVP providers
        //
        RemoveRsvpProvider();
    }
    else
    {
        // Cleanup the adapters marked as for deletion from the memory structure
        // Change made for #95637
        for(size_t i = 0 ; i < m_vcardAdapterInfo.size() ; ++i)
        {
            if (m_vcardAdapterInfo[i]->m_fDeleted)
            {
                //delete it
                FreeVectorItem(m_vcardAdapterInfo, i);
                i--; //move the pointer back ?
            }
        }

        // install RSVP providers
        //
        if (m_fInstalling)
        {
            hr = HrInstallRsvpProvider();
            TraceError("CTcpipcfg::ApplyRegistryChanges: HrInstallRsvpProvider failed", hr);

            // if RSVP install fails, we still want to continue apply
            //
            hr = S_OK;
        }

        if (m_fSaveRegistry)
        {
            // Save info in first memory state to registry
            // m_glbGlobalInfo and m_vcardAdapterInfo
            hrTmp = HrSaveSettings();
            if (SUCCEEDED(hr))
                hr = hrTmp;
        }
        else
        {
            // No change
            hr = S_FALSE;
        }
    }


    Validate_INetCfgNotify_Apply_Return(hr);

    TraceError("CTcpipcfg::ApplyRegistryChanges", (hr == S_FALSE) ? S_OK : hr);
    return hr;
}

//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::ApplyPnpChanges
//
STDMETHODIMP CTcpipcfg::ApplyPnpChanges(IN INetCfgPnpReconfigCallback* pICallback)
{
    HRESULT hr = S_OK;
    HRESULT hrTmp = S_OK;

    Assert(pICallback);

    if (!m_fRemoving)
    {
        if(!m_fInstalling)
        {
            if (m_fReconfig)
            {
                // Notify protocols/services of changes

                // Notify Tcpip of any changes in the IP Addresses
                hrTmp = HrNotifyDhcp();
                if (S_OK == hr)
                    hr = hrTmp;

                // reconfig tcpip
                hrTmp = HrReconfigIp(pICallback);
                if (S_OK == hr)
                    hr = hrTmp;

                // reconfig netbt
                hrTmp = HrReconfigNbt(pICallback);
                if (S_OK == hr)
                    hr = hrTmp;

                // reconfig dns
                hrTmp = HrReconfigDns();
                if (S_OK == hr)
                    hr = hrTmp;
            }

            if (IsBindOrderChanged())
            {
                //notify DNS cache of binding order changes
                hrTmp = HrReconfigDns(TRUE);
                if (S_OK == hr)
                    hr = hrTmp;
            }

        }

        
//IPSec is removed from connection UI
//        if (m_fIpsecPolicySet)
//            hrTmp = HrSetActiveIpsecPolicy();

        if (S_OK == hr)
            hr = hrTmp;
    }

    // Current state has been applied, reset the flags and
    // "Old" value of parameters
    if (S_OK == hr)
    {
        ReInitializeInternalState();
    }

    TraceError("CTcpipcfg::ApplyPnpChanges", hr);
    return hr;
}


//+---------------------------------------------------------------------------
// INetCfgComponentSetUp
//
//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::Install
//
STDMETHODIMP CTcpipcfg::Install(DWORD dwSetupFlags)
{
    HRESULT hr;

    Validate_INetCfgNotify_Install(dwSetupFlags);

    m_fSaveRegistry = TRUE;
    m_fInstalling = TRUE;

    // Install the WINS client on behalf of TCPIP.
    Assert(!m_pnccWins);
    hr = HrInstallComponentOboComponent(m_pnc, NULL,
            GUID_DEVCLASS_NETTRANS,
            c_szInfId_MS_NetBT, m_pnccTcpip,
            &m_pnccWins);

    if (SUCCEEDED(hr))
    {
        Assert(m_pnccWins);

        hr = HrInstallComponentOboComponent(m_pnc, NULL,
                GUID_DEVCLASS_NETTRANS,
                c_szInfId_MS_NetBT_SMB, m_pnccTcpip,
                NULL);

        if (SUCCEEDED(hr))
        {
            // Install RSVP client on behalf of TCPIP.
            hr = HrInstallComponentOboComponent(m_pnc, NULL,
                    GUID_DEVCLASS_NETSERVICE,
                    c_szInfId_MS_RSVP, m_pnccTcpip,
                    NULL);
        }
    }

    TraceError("CTcpipcfg::Install", hr);
    return hr;
}

//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::Upgrade
//
STDMETHODIMP CTcpipcfg::Upgrade(DWORD dwSetupFlags,
                                DWORD dwUpgradeFomBuildNo )
{
    HrCleanUpPerformRouterDiscoveryFromRegistry();
    return S_FALSE;
}

//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::ReadAnswerFile
//
//  Purpose:    Reads the appropriate fields from the given answer file into
//              our in-memory state.
//
//  Arguments:
//      pszAnswerFile       [in]   Filename of answer file for upgrade.
//      pszAnswerSection   [in]   Comma-separated list of sections in the
//                                  file appropriate to this component.
//
//  Returns:    HRESULT, Error code.
//
//  Author:     tongl  7 May 1997
//
//  Notes:
//
STDMETHODIMP CTcpipcfg::ReadAnswerFile( PCWSTR pszAnswerFile,
                                        PCWSTR pszAnswerSection)
{
    m_fSaveRegistry = TRUE;

    if (pszAnswerFile && pszAnswerSection)
    {
        // Process answer file
        (void) HrProcessAnswerFile(pszAnswerFile, pszAnswerSection);
    }

    return S_OK;
}

//+---------------------------------------------------------------------------
//  Member:     CTcpipcfg::Removing
//
STDMETHODIMP CTcpipcfg::Removing()
{
    HRESULT hr;

    m_fRemoving = TRUE;

    // Remove NetBt protocol. This doesn't actually remove the
    // component, it simply marks it as needing to be removed,
    // and in Apply() it will be fully removed.
    hr = HrRemoveComponentOboComponent(
            m_pnc, GUID_DEVCLASS_NETTRANS,
            c_szInfId_MS_NetBT, m_pnccTcpip);
    if (SUCCEEDED(hr))
    {
        // remove NetBt_SMB
        hr = HrRemoveComponentOboComponent(
                m_pnc, GUID_DEVCLASS_NETTRANS,
                c_szInfId_MS_NetBT_SMB, m_pnccTcpip);

    }

    // Also remove RSVP
    if (SUCCEEDED(hr))
    {
        hr = HrRemoveComponentOboComponent(
                m_pnc, GUID_DEVCLASS_NETSERVICE,
                c_szInfId_MS_RSVP, m_pnccTcpip);
    }

    TraceError("CTcpipcfg::Removing", hr);
    return hr;
}

// INetCfgProperties
STDMETHODIMP CTcpipcfg::SetContext(IUnknown * pUnk)
{
    // release previous context, if any
    ReleaseObj(m_pUnkContext);
    m_pUnkContext = NULL;

    if (pUnk) // set the new context
    {
        m_pUnkContext = pUnk;
        m_pUnkContext->AddRef();
    }

    return S_OK;
}

STDMETHODIMP CTcpipcfg::MergePropPages(
    IN OUT DWORD* pdwDefPages,
    OUT LPBYTE* pahpspPrivate,
    OUT UINT* pcPages,
    IN HWND hwndParent,
    OUT PCWSTR* pszStartPage)
{
    Validate_INetCfgProperties_MergePropPages (
        pdwDefPages, pahpspPrivate, pcPages, hwndParent, pszStartPage);

    // Initialize output parameter
    HPROPSHEETPAGE *ahpsp = NULL;
    int cPages = 0;

    // We don't want any default pages to be shown
    *pdwDefPages = 0;
    *pcPages = NULL;
    *pahpspPrivate = NULL;

    // get the connection context in which we are bringing up the UI
    HRESULT hr = HrSetConnectionContext();

    if (SUCCEEDED(hr))
    {
        AssertSz(((CONNECTION_LAN == m_ConnType)||
                  (CONNECTION_RAS_PPP == m_ConnType)||
                  (CONNECTION_RAS_SLIP == m_ConnType)||
                  (CONNECTION_RAS_VPN == m_ConnType)),
                  "How come we don't know the connection type yet on MergePropPages?");

        // Initialize the common controls library
        INITCOMMONCONTROLSEX icc;
        icc.dwSize = sizeof(icc);
        icc.dwICC  = ICC_INTERNET_CLASSES;

        SideAssert(InitCommonControlsEx(&icc));

        hr = HrSetupPropSheets(&ahpsp, &cPages);
        if (SUCCEEDED(hr))
        {
            *pahpspPrivate = (LPBYTE)ahpsp;
            *pcPages = cPages;

            // Set the global up\down arrows
            if (!g_hiconUpArrow && !g_hiconDownArrow)
            {
                g_hiconUpArrow = (HICON)LoadImage(_Module.GetResourceInstance(),
                                                  MAKEINTRESOURCE(IDI_UP_ARROW),
                                                  IMAGE_ICON, 16, 16, 0);
                g_hiconDownArrow = (HICON)LoadImage(_Module.GetResourceInstance(),
                                                    MAKEINTRESOURCE(IDI_DOWN_ARROW),
                                                    IMAGE_ICON, 16, 16, 0);
            }

        }
        else
        {
            *pcPages = 0;
            CoTaskMemFree(ahpsp);

        }
    }

    Validate_INetCfgProperties_MergePropPages_Return(hr);

    TraceError("CTcpipcfg::MergePropPages", hr);
    return hr;
}

STDMETHODIMP CTcpipcfg::ValidateProperties(HWND hwndSheet)
{
    return S_OK;
}

STDMETHODIMP CTcpipcfg::CancelProperties()
{
    // If the lmhosts file was set, we need to roll it back to the backup
    if (m_fSecondMemoryLmhostsFileReset)
    {
        ResetLmhostsFile();
    }

    // Release second memory state
    ExitProperties();

    return S_OK;
}

STDMETHODIMP CTcpipcfg::ApplyProperties()
{
    HRESULT hr = S_OK;

    if (!m_fReconfig)
    {
        m_fReconfig = m_fSecondMemoryModified ||
                      m_fSecondMemoryLmhostsFileReset;

        //IPSec is removed from connection UI
        // || m_fSecondMemoryIpsecPolicySet;
    }

    if (!m_fLmhostsFileSet)
        m_fLmhostsFileSet = m_fSecondMemoryLmhostsFileReset;

    //IPSec is removed from connection UI
    //if (!m_fIpsecPolicySet)
    //    m_fIpsecPolicySet = m_fSecondMemoryIpsecPolicySet;

    if (!m_fSaveRegistry)
        m_fSaveRegistry = m_fSecondMemoryModified;

    // Copy info from second memory state to first memory state
    if (m_fSecondMemoryModified)
    {
        m_glbGlobalInfo = m_glbSecondMemoryGlobalInfo;
        hr = HrSaveAdapterInfo();
    }

    // Release second memory state
    ExitProperties();

    Validate_INetCfgProperties_ApplyProperties_Return(hr);

    TraceError("CTcpipcfg::ApplyProperties", hr);
    return hr;
}

STDMETHODIMP CTcpipcfg::QueryBindingPath(DWORD dwChangeFlag,
                                         INetCfgBindingPath * pncbp)
{
    HRESULT hr = S_OK;

    // If the binding is to an atm adapter (i.e. interface = ndisatm),
    // then return NETCFG_S_DISABLE_QUERY
    //
    if (dwChangeFlag & NCN_ADD)
    {

        INetCfgComponent* pnccLastComponent;
        PWSTR pszInterfaceName;

        hr = HrGetLastComponentAndInterface(pncbp,
                &pnccLastComponent,
                &pszInterfaceName);

        if (SUCCEEDED(hr))
        {
            // If adding an adapter through interface ndisatm,
            // we want to disable the binding interface since it's
            // the IP over ATM direct binding
            if (0 == lstrcmpW(c_szBiNdisAtm,  pszInterfaceName))
            {
                hr = NETCFG_S_DISABLE_QUERY;
            }

            ReleaseObj (pnccLastComponent);
            CoTaskMemFree (pszInterfaceName);
        }
    }

    TraceError("CTcpipcfg::QueryBindingPath",
        (NETCFG_S_DISABLE_QUERY == hr) ? S_OK : hr);
    return hr;
}

STDMETHODIMP CTcpipcfg::NotifyBindingPath(
    DWORD                   dwChangeFlag,
    INetCfgBindingPath *    pncbp)
{
    Assert(!(dwChangeFlag & NCN_ADD && dwChangeFlag & NCN_REMOVE));
    Assert(!(dwChangeFlag & NCN_ENABLE && dwChangeFlag & NCN_DISABLE));

    // If we are told to add a card, we must be told at the same time whether the
    // binding is enabled or disabled
    Assert(FImplies((dwChangeFlag & NCN_ADD),
                    ((dwChangeFlag & NCN_ENABLE)||(dwChangeFlag & NCN_DISABLE))));

    HRESULT hr = S_OK;

    Validate_INetCfgBindNotify_NotifyBindingPath(dwChangeFlag, pncbp);

    INetCfgComponent * pnccLastComponent;
    PWSTR pszInterfaceName;
    hr = HrGetLastComponentAndInterface(pncbp,
            &pnccLastComponent,
            &pszInterfaceName);
    if (SUCCEEDED(hr))
    {
#if DBG
        GUID guidNetClass;
        hr = pnccLastComponent->GetClassGuid (&guidNetClass);

        AssertSz(
            SUCCEEDED(hr) &&
            IsEqualGUID(guidNetClass, GUID_DEVCLASS_NET),
            "Why the last component on the path is not an adapter?");
#endif

        // If we are adding/removing cards, set m_fSaveRegistry
        // so we apply the changes to registry

        if (dwChangeFlag & (NCN_ADD | NCN_REMOVE))
            m_fSaveRegistry = TRUE;

        hr = HrAdapterBindNotify(pnccLastComponent,
                                 dwChangeFlag,
                                 pszInterfaceName);

        ReleaseObj (pnccLastComponent);
        CoTaskMemFree (pszInterfaceName);
    }

    if (SUCCEEDED(hr))
        hr = S_OK;

    Validate_INetCfgBindNotify_NotifyBindingPath_Return(hr);

    TraceError("CTcpipcfg::NotifyBindingPath", hr);
    return hr;
}


//+---------------------------------------------------------------------------
// INetCfgComponentUpperEdge
//

// Return an array of interface ids for an adapter bound to
// this component.  If the specified adapter does not have explicit
// interfaces exported from it, S_FALSE is returned.
// pAdapter is the adapter in question.
// pdwNumInterfaces is the address of a DWORD where the count of elements
// returned via ppguidInterfaceIds is stored.
// ppguidInterfaceIds is the address of a pointer where an allocated
// block of memory is returned.  This memory is an array of interface ids.
// *ppguidInterfaceIds should be free with CoTaskMemFree if S_OK is returned.
// if S_FALSE is returned, *pdwNumInterfaces and *ppguidInterfaceIds should
// be NULL.
//

HRESULT
CTcpipcfg::GetInterfaceIdsForAdapter (
    INetCfgComponent*   pnccAdapter,
    DWORD*              pdwNumInterfaces,
    GUID**              ppguidInterfaceIds)
{
    Assert (pnccAdapter);
    Assert (pdwNumInterfaces);

    HRESULT hr = S_FALSE;

    // Initialize output parameters.
    //
    *pdwNumInterfaces = 0;
    if (ppguidInterfaceIds)
    {
        *ppguidInterfaceIds = NULL;
    }

    ADAPTER_INFO* pAdapterInfo = PAdapterFromNetcfgComponent(pnccAdapter);

    if (pAdapterInfo &&
        pAdapterInfo->m_fIsWanAdapter &&
        pAdapterInfo->m_fIsMultipleIfaceMode)
    {
        hr = GetGuidArrayFromIfaceColWithCoTaskMemAlloc(
                pAdapterInfo->m_IfaceIds,
                ppguidInterfaceIds,
                pdwNumInterfaces);
    }

    TraceHr(ttidError, FAL, hr, (S_FALSE == hr),
        "CTcpipcfg::GetInterfaceIdsForAdapter");
    return hr;
}


// Add the specified number of new interfaces to the specified adapter.
// The implementation will choose the interface ids.
//
HRESULT
CTcpipcfg::AddInterfacesToAdapter (
    INetCfgComponent*   pnccAdapter,
    DWORD               dwNumInterfaces)
{
    Assert (pnccAdapter);

    HRESULT         hr = S_FALSE;
    ADAPTER_INFO*   pAdapterInfo;

    if ((NULL == pnccAdapter) || (0 == dwNumInterfaces))
    {
        hr = E_INVALIDARG;
        goto end_AddInterfacesToAdapter;
    }

    pAdapterInfo = PAdapterFromNetcfgComponent(pnccAdapter);

    if (pAdapterInfo &&
        pAdapterInfo->m_fIsWanAdapter)
    {
        AddInterfacesToAdapterInfo(
            pAdapterInfo,
            dwNumInterfaces);

        pAdapterInfo->m_fIsMultipleIfaceMode = TRUE;
        pAdapterInfo->m_fNewlyChanged = TRUE;
        m_fSaveRegistry = TRUE;
        m_fReconfig = TRUE;

        // Notify the binding engine that our upper edge has changed.
        //
        (VOID)m_pTcpipPrivate->NotifyUpperEdgeConfigChange ();
        hr = S_OK;
    }

end_AddInterfacesToAdapter:
    TraceErrorSkip1("CTcpipcfg::AddInterfacesToAdapter", hr, S_FALSE);
    return hr;
}


// Remove the specified interface ids from the specified adapter.
// pguidInterfaceIds is the array of ids to be removed.  dwNumInterfaces
// is the count in that array.
//
HRESULT
CTcpipcfg::RemoveInterfacesFromAdapter (
    INetCfgComponent*   pnccAdapter,
    DWORD               dwNumInterfaces,
    const GUID*         pguidInterfaceIds)
{
    Assert (pnccAdapter);
    Assert (pguidInterfaceIds);

    HRESULT         hr = E_UNEXPECTED;
    ADAPTER_INFO*   pAdapterInfo;

    if ((NULL == pnccAdapter) ||
        (0 == dwNumInterfaces) ||
        (NULL == pguidInterfaceIds))
    {
        hr = E_INVALIDARG;
        goto end_RemoveInterfacesFromAdapter;
    }

    pAdapterInfo = PAdapterFromNetcfgComponent(pnccAdapter);

    AssertSz( pAdapterInfo,
        "CTcpipcfg::AddInterfacesToAdapter cannot find the adapter "
        "GUID from the adapter list");

    if (pAdapterInfo &&
        pAdapterInfo->m_fIsWanAdapter &&
        pAdapterInfo->m_fIsMultipleIfaceMode)
    {
        DWORD       dwNumRemoved = 0;
        IFACEITER   iter;
        for (DWORD i = 0; i < dwNumInterfaces; i++)
        {
            iter = find(pAdapterInfo->m_IfaceIds.begin(),
                        pAdapterInfo->m_IfaceIds.end(),
                        pguidInterfaceIds[i]);

            if (iter != pAdapterInfo->m_IfaceIds.end())
            {
                pAdapterInfo->m_IfaceIds.erase(iter);
                dwNumRemoved++;
            }
        }

        //$REVIEW (nsun) mark the adapter as NewlyAdded so that we will re-write its adapter registry
        if (dwNumRemoved > 0)
        {
            pAdapterInfo->m_fNewlyChanged = TRUE;
            m_fSaveRegistry = TRUE;
        }

        // Notify the binding engine that our upper edge has changed.
        //
        (VOID)m_pTcpipPrivate->NotifyUpperEdgeConfigChange ();

        hr = (dwNumRemoved == dwNumInterfaces) ? S_OK : S_FALSE;
    }

end_RemoveInterfacesFromAdapter:
    TraceError("CTcpipcfg::RemoveInterfacesFromAdapter", hr);
    return hr;
}


//+---------------------------------------------------------------------------
// ITcpipProperties
//

// The following two methods are for remote tcpip configuration.
/*
typedef struct tagREMOTE_IPINFO
{
    DWORD   dwEnableDhcp;
    PWSTR pszIpAddrList;
    PWSTR pszSubnetMaskList;
    PWSTR pszOptionList;

} REMOTE_IPINFO;
*/

HRESULT CTcpipcfg::GetIpInfoForAdapter(const GUID*      pguidAdapter,
                                       REMOTE_IPINFO**  ppRemoteIpInfo)
{
    Assert(pguidAdapter);
    Assert(ppRemoteIpInfo);

    // Initialize the output parameter.
    //
    *ppRemoteIpInfo = NULL;

    HRESULT hr = S_OK;

    ADAPTER_INFO* pAdapter = PAdapterFromInstanceGuid(pguidAdapter);
    if (pAdapter)
    {
        // get the strings from the list
        tstring strIpAddressList;
        ConvertColStringToString(pAdapter->m_vstrIpAddresses,
                                 c_chListSeparator,
                                 strIpAddressList);

        tstring strSubnetMaskList;
        ConvertColStringToString(pAdapter->m_vstrSubnetMask,
                                 c_chListSeparator,
                                 strSubnetMaskList);

        //bug 272647 add gateway metric and interface metric into REMOTE_IPINFO
        tstring strOptionList;
        ConstructOptionListString(pAdapter,
                                  strOptionList);

        // allocate buffer for the output param
        DWORD dwBytes = sizeof(REMOTE_IPINFO) +
                        sizeof(WCHAR)*(strIpAddressList.length() + 1) +
                        sizeof(WCHAR)*(strSubnetMaskList.length() + 1) +
                        sizeof(WCHAR)*(strOptionList.length() + 1);

        PVOID   pbBuf;
        hr = HrCoTaskMemAlloc(dwBytes, &pbBuf);

        if (SUCCEEDED(hr))
        {
            ZeroMemory(pbBuf, dwBytes);

            REMOTE_IPINFO * pRemoteIpInfo = reinterpret_cast<REMOTE_IPINFO *>(pbBuf);
            pRemoteIpInfo->dwEnableDhcp = pAdapter->m_fEnableDhcp;

            BYTE* pbByte = reinterpret_cast<BYTE*>(pbBuf);

            // ip address
            pbByte+= sizeof(REMOTE_IPINFO);
            pRemoteIpInfo->pszwIpAddrList = reinterpret_cast<WCHAR *>(pbByte);
            lstrcpyW(pRemoteIpInfo->pszwIpAddrList, strIpAddressList.c_str());

            // subnet mask
            pbByte += sizeof(WCHAR)*(strIpAddressList.length() + 1);
            pRemoteIpInfo->pszwSubnetMaskList = reinterpret_cast<WCHAR *>(pbByte);
            lstrcpyW(pRemoteIpInfo->pszwSubnetMaskList, strSubnetMaskList.c_str());

            // default gateway
            pbByte += sizeof(WCHAR)*(strSubnetMaskList.length() + 1);
            pRemoteIpInfo->pszwOptionList = reinterpret_cast<WCHAR *>(pbByte);
            lstrcpyW(pRemoteIpInfo->pszwOptionList, strOptionList.c_str());

            *ppRemoteIpInfo = pRemoteIpInfo;
        }
    }
    else
    {
        hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
    }

    TraceError("CTcpipcfg::GetIpInfoForAdapter", hr);
    return hr;
}

HRESULT CTcpipcfg::SetIpInfoForAdapter(const GUID*      pguidAdapter,
                                       REMOTE_IPINFO*   pRemoteIpInfo)
{
    Assert(pguidAdapter);
    Assert(pRemoteIpInfo);

    HRESULT hr = S_OK;

    ADAPTER_INFO* pAdapter = PAdapterFromInstanceGuid(pguidAdapter);
    if (pAdapter)
    {
    
        // Tell INetCfg that our component is dirty
        Assert(m_pTcpipPrivate);
        m_pTcpipPrivate->SetDirty();

        // set the flags so we write this to registry & send notification
        // at apply
        m_fSaveRegistry = TRUE;
        m_fReconfig = TRUE;

        // copy over the info to our data structure
        pAdapter->m_fEnableDhcp = !!pRemoteIpInfo->dwEnableDhcp;

        ConvertStringToColString(pRemoteIpInfo->pszwIpAddrList,
                                 c_chListSeparator,
                                 pAdapter->m_vstrIpAddresses);

        ConvertStringToColString(pRemoteIpInfo->pszwSubnetMaskList,
                                 c_chListSeparator,
                                 pAdapter->m_vstrSubnetMask);

        hr = HrParseOptionList(pRemoteIpInfo->pszwOptionList, pAdapter);

    }
    else
    {
        hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
    }

    TraceError("CTcpipcfg::SetIpInfoForAdapter", hr);
    return hr;
}


STDMETHODIMP
CTcpipcfg::GetUiInfo (
    RASCON_IPUI*  pIpui)
{
    HRESULT hr = S_OK;

    // Validate parameters.
    //
    if (!pIpui)
    {
        hr = E_POINTER;
    }
    else
    {
        ZeroMemory (pIpui, sizeof(*pIpui));

        ADAPTER_INFO* pAdapter = PAdapterFromInstanceGuid(
                                    &m_guidCurrentConnection);
        if (pAdapter)
        {
            if (!pAdapter->m_fEnableDhcp &&
                pAdapter->m_vstrIpAddresses.size())
            {
                pIpui->dwFlags |= RCUIF_USE_IP_ADDR;

                lstrcpyW(pIpui->pszwIpAddr,
                        pAdapter->m_vstrIpAddresses[0]->c_str());
            }

            if (pAdapter->m_vstrDnsServerList.size() > 0)
            {
                pIpui->dwFlags |= RCUIF_USE_NAME_SERVERS;

                lstrcpyW(pIpui->pszwDnsAddr,
                        pAdapter->m_vstrDnsServerList[0]->c_str());

                if (pAdapter->m_vstrDnsServerList.size() > 1)
                {
                    lstrcpyW(pIpui->pszwDns2Addr,
                            pAdapter->m_vstrDnsServerList[1]->c_str());
                }
            }

            if (pAdapter->m_vstrWinsServerList.size() > 0)
            {
                pIpui->dwFlags |= RCUIF_USE_NAME_SERVERS;

                lstrcpyW(pIpui->pszwWinsAddr,
                        pAdapter->m_vstrWinsServerList[0]->c_str());

                if (pAdapter->m_vstrWinsServerList.size() > 1)
                {
                    lstrcpyW(pIpui->pszwWins2Addr,
                            pAdapter->m_vstrWinsServerList[1]->c_str());
                }
            }

            if (pAdapter->m_fUseRemoteGateway)
            {
                 pIpui->dwFlags |= RCUIF_USE_REMOTE_GATEWAY;
            }

            if (pAdapter->m_fUseIPHeaderCompression)
            {
                pIpui->dwFlags |= RCUIF_USE_HEADER_COMPRESSION;
            }

            if (pAdapter->m_fDisableDynamicUpdate)
            {
                pIpui->dwFlags |= RCUIF_USE_DISABLE_REGISTER_DNS;
            }

            if (pAdapter->m_fEnableNameRegistration)
            {
                pIpui->dwFlags |= RCUIF_USE_PRIVATE_DNS_SUFFIX;
            }

            if (c_dwEnableNetbios == pAdapter->m_dwNetbiosOptions)
            {
                pIpui->dwFlags |= RCUIF_ENABLE_NBT;
            }

            lstrcpynW(pIpui->pszwDnsSuffix, 
                     pAdapter->m_strDnsDomain.c_str(), 
                     sizeof(pIpui->pszwDnsSuffix)/sizeof(pIpui->pszwDnsSuffix[0]));

            pIpui->dwFrameSize = pAdapter->m_dwFrameSize;
        }
        else
        {
            hr = E_UNEXPECTED;
        }
    }
    TraceError("CTcpipcfg::GetUiInfo", hr);
    return hr;
}