//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997-2000.
//
//  File:       N D I S W A N . C P P
//
//  Contents:   Implementation of NdisWan configuration object.
//
//  Notes:
//
//  Author:     shaunco   28 Mar 1997
//
//----------------------------------------------------------------------------

#include "pch.h"
#pragma hdrstop
#include "ndiswan.h"
#include "ncreg.h"
#include "mprerror.h"
#include "rtutils.h"

extern const WCHAR c_szInfId_MS_AppleTalk[];
extern const WCHAR c_szInfId_MS_L2TP[];
extern const WCHAR c_szInfId_MS_L2tpMiniport[];
extern const WCHAR c_szInfId_MS_NWIPX[];
extern const WCHAR c_szInfId_MS_NdisWanAtalk[];
extern const WCHAR c_szInfId_MS_NdisWanBh[];
extern const WCHAR c_szInfId_MS_NdisWanIp[];
extern const WCHAR c_szInfId_MS_NdisWanIpx[];
extern const WCHAR c_szInfId_MS_NdisWanNbfIn[];
extern const WCHAR c_szInfId_MS_NdisWanNbfOut[];
extern const WCHAR c_szInfId_MS_NetBEUI[];
extern const WCHAR c_szInfId_MS_NetMon[];
extern const WCHAR c_szInfId_MS_PPTP[];
extern const WCHAR c_szInfId_MS_PptpMiniport[];
extern const WCHAR c_szInfId_MS_RasMan[];
extern const WCHAR c_szInfId_MS_TCPIP[];
extern const WCHAR c_szInfId_MS_PPPOE[];
extern const WCHAR c_szInfId_MS_PppoeMiniport[];

extern const WCHAR c_szRegValWanEndpoints[]    = L"WanEndpoints";
static const WCHAR c_szRegValMinWanEndpoints[] = L"MinWanEndpoints";
static const WCHAR c_szRegValMaxWanEndpoints[] = L"MaxWanEndpoints";

//$ TODO (shaunco) 3 Feb 1998: rasman has no notify object so just
// merge its services into ndiswan's and eliminate c_apguidInstalledOboNdiswan

//----------------------------------------------------------------------------
// Data used for installing other components.
//
static const GUID* c_apguidInstalledOboNdiswan [] =
{
    &GUID_DEVCLASS_NETSERVICE,  // RasMan
};

static const PCWSTR c_apszInstalledOboNdiswan [] =
{
    c_szInfId_MS_RasMan,
};


static const GUID* c_apguidInstalledOboUser [] =
{
    &GUID_DEVCLASS_NETTRANS,    // L2TP
    &GUID_DEVCLASS_NETTRANS,    // PPTP
    &GUID_DEVCLASS_NETTRANS,    // PPPOE
};

static const PCWSTR c_apszInstalledOboUser [] =
{
    c_szInfId_MS_L2TP,
    c_szInfId_MS_PPTP,
    c_szInfId_MS_PPPOE,
};


CNdisWan::CNdisWan () : CRasBindObject ()
{
    m_fInstalling        = FALSE;
    m_fRemoving          = FALSE;
    m_pnccMe             = NULL;
}

CNdisWan::~CNdisWan ()
{
    ReleaseObj (m_pnccMe);
}


//+---------------------------------------------------------------------------
// INetCfgComponentControl
//
STDMETHODIMP
CNdisWan::Initialize (
    INetCfgComponent*   pncc,
    INetCfg*            pnc,
    BOOL                fInstalling)
{
    Validate_INetCfgNotify_Initialize (pncc, pnc, fInstalling);

    // Hold on to our the component representing us and our host
    // INetCfg object.
    //
    AddRefObj (m_pnccMe = pncc);
    AddRefObj (m_pnc = pnc);

    m_fInstalling = fInstalling;

    return S_OK;
}

STDMETHODIMP
CNdisWan::Validate ()
{
    return S_OK;
}

STDMETHODIMP
CNdisWan::CancelChanges ()
{
    return S_OK;
}

STDMETHODIMP
CNdisWan::ApplyRegistryChanges ()
{
    return S_OK;
}

//+---------------------------------------------------------------------------
// INetCfgComponentSetup
//
STDMETHODIMP
CNdisWan::ReadAnswerFile (
    PCWSTR pszAnswerFile,
    PCWSTR pszAnswerSection)
{
    return S_OK;
}

STDMETHODIMP
CNdisWan::Install (DWORD dwSetupFlags)
{
    HRESULT hr;

    Validate_INetCfgNotify_Install (dwSetupFlags);

    // Install rasman.
    //
    hr = HrInstallComponentsOboComponent (m_pnc, NULL,
            celems (c_apguidInstalledOboNdiswan),
            c_apguidInstalledOboNdiswan,
            c_apszInstalledOboNdiswan,
            m_pnccMe);

//$ TODO (shaunco) 28 Dec 1997: Install L2TP, PPTP, PPPOE obo ndiswan.
//  But, this creates an upgrade problem.

    if (SUCCEEDED(hr))
    {
        hr = HrInstallComponentsOboUser (m_pnc, NULL,
                celems (c_apguidInstalledOboUser),
                c_apguidInstalledOboUser,
                c_apszInstalledOboUser);
    }

    if (SUCCEEDED(hr))
    {
        hr = HrAddOrRemovePti (ARA_ADD);
    }

    if (SUCCEEDED(hr))
    {
        hr = HrFindOtherComponents ();
        if (SUCCEEDED(hr))
        {
            if (SUCCEEDED(hr) && PnccAtalk())
            {
                hr = HrAddOrRemoveAtalkInOut (ARA_ADD);
            }

            if (SUCCEEDED(hr) && PnccIp())
            {
                hr = HrAddOrRemoveIpAdapter (ARA_ADD);
            }

            if (SUCCEEDED(hr) && PnccIpx())
            {
                hr = HrAddOrRemoveIpxInOut (ARA_ADD);
            }

            if (SUCCEEDED(hr) && PnccNetMon())
            {
                hr = HrAddOrRemoveNetMonInOut (ARA_ADD);
            }

            ReleaseOtherComponents ();
        }
    }

    // Recompute (and adjust if needed) the number of ndiswan miniports.
    //
    hr = HrProcessEndpointChange ();
    TraceError ("CNdisWan::Install: HrProcessEndpointChange failed. "
        "(not propagating error)", hr);

    // Normalize the HRESULT.  (i.e. don't return S_FALSE)
    if (S_FALSE == hr)
    {
        hr = S_OK;
    }

    if ((NSF_WINNT_WKS_UPGRADE & dwSetupFlags) ||
        (NSF_WINNT_SBS_UPGRADE & dwSetupFlags) ||
        (NSF_WINNT_SVR_UPGRADE & dwSetupFlags))
    {
        HKEY    hkeyMd5;

        if (SUCCEEDED(HrRegOpenKeyEx (HKEY_LOCAL_MACHINE,
            L"System\\CurrentControlSet\\Services\\Rasman\\PPP\\CHAP\\MD5",
            KEY_READ, &hkeyMd5)))
        {
            HANDLE  hLog;

            // MD5 key was found. Need to log something to the eventlog.
            hLog = RouterLogRegister(L"RemoteAccess");
            if (hLog)
            {
                RouterLogWarning(hLog, WARNING_NO_MD5_MIGRATION, 0,
                                 NULL, NO_ERROR);

                RouterLogDeregister(hLog);
            }

            RegCloseKey (hkeyMd5);
        }
    }

    //  Check to see if Connection Manager is installed and
    //  if there are any profiles to migrate.  We do this
    //  by opening the CM mappings key and checking to see if it contains any
    //  values.  and if so then write a run-once setup key so that
    //  we can migrate profiles when the user logs in for the first time.
    //  Note that this only works for NT to NT upgrades.  The win9x registry
    //  hasn't been filled in by the time that this runs.  Thus our win9x
    //  migration dll (cmmgr\migration.dll) takes care of the win9x case.
    //

    static const WCHAR c_CmdString[] = L"cmstp.exe /m";
    static const WCHAR c_ValueString[] = L"Connection Manager Profiles Upgrade";
    static const WCHAR c_szRegCmMappings[] = L"SOFTWARE\\Microsoft\\Connection Manager\\Mappings";
    static const WCHAR c_szRegRunKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
    static const WCHAR c_szRegCmUninstallKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Connection Manager";

    static const WCHAR c_szRegSysCompValue[] = L"SystemComponent";

    // dwTemp is used as temp DWORD.  Used as Disposition DWORD for RegCreateKey, and
    // as a value holder for RegSetValueEx
    //
    DWORD dwTemp;

    HKEY hKey;
    HRESULT hrT;

    //  Set the Connection Manager key as a system component.  This will prevent 1.0 installs
    //  from writing this key and having it show up in Add/Remove Programs (the syscomp flags
    //  tells ARP not to display the key).
    //
    hrT = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE,
                           c_szRegCmUninstallKey,
                           REG_OPTION_NON_VOLATILE,
                           KEY_ALL_ACCESS,
                           NULL,
                           &hKey,
                           &dwTemp);

    if (SUCCEEDED(hrT))
    {
        dwTemp = 1;
        hrT = HrRegSetDword(hKey, c_szRegSysCompValue, dwTemp);
        RegCloseKey(hKey);
    }

    //  Now try to migrate profiles
    //
    hrT = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE,
                         c_szRegCmMappings,
                         KEY_READ,
                         &hKey);

    if (SUCCEEDED(hrT))
    {
        dwTemp = 0;
        hrT = HrRegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, NULL,
                                 &dwTemp, NULL, NULL, NULL, NULL);

        if ((SUCCEEDED(hrT)) && (dwTemp > 0))
        {
            //  Then we have mappings values, so we need to migrate them.
            //

            RegCloseKey(hKey);

            hrT = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE,
                                  c_szRegRunKey,
                                  REG_OPTION_NON_VOLATILE,
                                  KEY_WRITE | KEY_READ,
                                  NULL,
                                  &hKey,
                                  &dwTemp);

            if (SUCCEEDED(hrT))
            {
                hrT = HrRegSetSz (hKey, c_ValueString, c_CmdString);
                RegCloseKey(hKey);
            }
        }
        else
        {
            RegCloseKey(hKey);
        }
    }

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

STDMETHODIMP
CNdisWan::Removing ()
{
    static const PCWSTR c_apszInfIdRemove [] =
    {
        c_szInfId_MS_L2tpMiniport,
        c_szInfId_MS_NdisWanAtalk,
        c_szInfId_MS_NdisWanBh,
        c_szInfId_MS_NdisWanIp,
        c_szInfId_MS_NdisWanIpx,
        c_szInfId_MS_NdisWanNbfIn,
        c_szInfId_MS_NdisWanNbfOut,
        c_szInfId_MS_PptpMiniport,
        c_szInfId_MS_PtiMiniport,
        c_szInfId_MS_PppoeMiniport,
    };

    m_fRemoving = TRUE;

    HRESULT hr = S_OK;

    // Remove wanarp and rasman.
    //
    HRESULT hrT = HrFindAndRemoveComponentsOboComponent (m_pnc,
                    celems (c_apguidInstalledOboNdiswan),
                    c_apguidInstalledOboNdiswan,
                    c_apszInstalledOboNdiswan,
                    m_pnccMe);

    hr = hrT;

    // Remove L2TP, PPTP and PPPOE on behalf of the user.
    //
    hrT = HrFindAndRemoveComponentsOboUser (m_pnc,
            celems (c_apguidInstalledOboUser),
            c_apguidInstalledOboUser,
            c_apszInstalledOboUser);
    if (SUCCEEDED(hr))
    {
        hr = hrT;
    }

    // Remove all of the adapters we may have created.
    //
    hrT = HrFindAndRemoveAllInstancesOfAdapters (m_pnc,
            celems(c_apszInfIdRemove),
            c_apszInfIdRemove);
    if (SUCCEEDED(hr))
    {
        hr = hrT;
    }

    // Don't return S_FALSE or NETCFG_S_STILL_REFERENCED
    //
    if (SUCCEEDED(hr))
    {
        hr = S_OK;
    }

    Validate_INetCfgNotify_Removing_Return (hr);

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

STDMETHODIMP
CNdisWan::Upgrade (
    DWORD dwSetupFlags,
    DWORD dwUpgradeFromBuildNo)
{
    HRESULT hr;

    hr= HrInstallComponentsOboUser (m_pnc, NULL,
                    celems (c_apguidInstalledOboUser),
                    c_apguidInstalledOboUser,
                    c_apszInstalledOboUser);
    TraceError ("CNdisWan::Upgrade: HrInstallComponentsOboUser failed. "
        "(not propagating error)", hr);

    HKEY hkeyNew;
    hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE,
            L"System\\CurrentControlSet\\Services\\RemoteAccess\\Parameters",
            KEY_WRITE,
            &hkeyNew);

    if (SUCCEEDED(hr))
    {
        HKEY hkeyCurrent;
        DWORD dwValue;

        hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE,
                    L"System\\CurrentControlSet\\Services\\Rasman\\PPP",
                    KEY_READ,
                    &hkeyCurrent);
        if (SUCCEEDED(hr))
        {
            // Move 'ServerFlags' value to new location.  This is for
            // interim NT5 builds.  This can go away after Beta3 ships.
            //
            hr = HrRegQueryDword (hkeyCurrent, L"ServerFlags", &dwValue);
            if (SUCCEEDED(hr))
            {
                hr = HrRegSetDword (hkeyNew, L"ServerFlags", dwValue);

                (VOID) HrRegDeleteValue (hkeyCurrent, L"ServerFlags");
            }

            RegCloseKey (hkeyCurrent);
        }

        // Copy 'RouterType' value to new location.
        //
        hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE,
                    L"Software\\Microsoft\\Ras\\Protocols",
                    KEY_READ,
                    &hkeyCurrent);
        if (SUCCEEDED(hr))
        {
            hr = HrRegQueryDword (hkeyCurrent, L"RouterType", &dwValue);
            if (SUCCEEDED(hr))
            {
                hr = HrRegSetDword (hkeyNew, L"RouterType", dwValue);
            }

            RegCloseKey (hkeyCurrent);
        }

        RegCloseKey (hkeyNew);
    }

    return S_OK;
}

//+---------------------------------------------------------------------------
// INetCfgSystemNotify
//
STDMETHODIMP
CNdisWan::GetSupportedNotifications (
    DWORD*  pdwNotificationFlag)
{
    Validate_INetCfgSystemNotify_GetSupportedNotifications (pdwNotificationFlag);

    *pdwNotificationFlag = NCN_BINDING_PATH |
                           NCN_NETTRANS | NCN_NETSERVICE |
                           NCN_ADD | NCN_REMOVE;

    return S_OK;
}

STDMETHODIMP
CNdisWan::SysQueryBindingPath (
    DWORD               dwChangeFlag,
    INetCfgBindingPath* pncbp)
{
    return S_OK;
}

STDMETHODIMP
CNdisWan::SysQueryComponent (
    DWORD               dwChangeFlag,
    INetCfgComponent*   pncc)
{
    return S_OK;
}

STDMETHODIMP
CNdisWan::SysNotifyBindingPath (
    DWORD               dwChangeFlag,
    INetCfgBindingPath* pncbp)
{
    HRESULT hr;
    HKEY hkey;

    Validate_INetCfgSystemNotify_SysNotifyBindingPath (dwChangeFlag, pncbp);

    hkey = NULL;

    // ndisatm miniports don't write WanEndpoints to their instance key.
    // We default it and write WanEndpoints for them.
    //
    if (dwChangeFlag & NCN_ADD)
    {
        CIterNetCfgBindingInterface ncbiIter(pncbp);
        INetCfgBindingInterface* pncbi;

        hr = S_OK;

        while (!hkey && SUCCEEDED(hr) &&
               (S_OK == (hr = ncbiIter.HrNext (&pncbi))))
        {
            RAS_BINDING_ID RasBindId;

            if (FIsRasBindingInterface (pncbi, &RasBindId) &&
                (RBID_NDISATM == RasBindId))
            {
                INetCfgComponent* pnccLower;

                hr = pncbi->GetLowerComponent (&pnccLower);
                if (SUCCEEDED(hr))
                {
                    TraceTag (ttidRasCfg, "New ATM adapter");
                    hr = pnccLower->OpenParamKey (&hkey);

                    ReleaseObj (pnccLower);
                }
            }
            ReleaseObj(pncbi);
        }
    }

    if (hkey)
    {
        DWORD dwEndpoints;
        DWORD dwValue;

        hr = HrRegQueryDword (hkey, c_szRegValWanEndpoints, &dwEndpoints);
        if (FAILED(hr))
        {
            TraceTag (ttidRasCfg, "Defaulting WanEndpoints");

            dwEndpoints = 5;

            // Validate the default between the min and max
            // specified by the driver (if specified).
            //
            if (SUCCEEDED(HrRegQueryDword (hkey,
                                c_szRegValMaxWanEndpoints,
                                &dwValue)))
            {
                if ((dwValue < INT_MAX) && (dwEndpoints > dwValue))
                {
                    dwEndpoints = dwValue;
                }
            }
            else
            {
                (VOID) HrRegSetDword(hkey, c_szRegValMaxWanEndpoints, 500);
            }

            if (SUCCEEDED(HrRegQueryDword (hkey,
                                c_szRegValMinWanEndpoints,
                                &dwValue)))
            {
                if ((dwValue < INT_MAX) && (dwEndpoints < dwValue))
                {
                    dwEndpoints = dwValue;
                }
            }
            else
            {
                (VOID) HrRegSetDword(hkey, c_szRegValMinWanEndpoints, 0);
            }

            (VOID) HrRegSetDword (hkey, c_szRegValWanEndpoints, dwEndpoints);
        }

        RegCloseKey (hkey);
    }

    return S_FALSE;
}

STDMETHODIMP
CNdisWan::SysNotifyComponent (
    DWORD               dwChangeFlag,
    INetCfgComponent*   pncc)
{
    HRESULT hr = S_OK;

    Validate_INetCfgSystemNotify_SysNotifyComponent (dwChangeFlag, pncc);

    // If a protocol is coming or going, add or remove the
    // ndiswanXXX miniports.
    //
    DWORD dwAraFlags = 0;
    if (dwChangeFlag & NCN_ADD)
    {
        dwAraFlags = ARA_ADD;
    }
    else if (dwChangeFlag & NCN_REMOVE)
    {
        dwAraFlags = ARA_REMOVE;
    }
    if (dwAraFlags)
    {
        BOOL fProcessEndpoingChange = FALSE;

        PWSTR pszId;
        hr = pncc->GetId (&pszId);
        if (SUCCEEDED(hr))
        {
            if (FEqualComponentId (c_szInfId_MS_TCPIP, pszId))
            {
                hr = HrAddOrRemoveIpAdapter (dwAraFlags);
                fProcessEndpoingChange = TRUE;
            }
            else if (FEqualComponentId (c_szInfId_MS_NWIPX, pszId))
            {
                hr = HrAddOrRemoveIpxInOut (dwAraFlags);
            }
            else if (FEqualComponentId (c_szInfId_MS_NetBEUI, pszId))
            {
                fProcessEndpoingChange = TRUE;
            }
            else if (FEqualComponentId (c_szInfId_MS_AppleTalk, pszId))
            {
                hr = HrAddOrRemoveAtalkInOut (dwAraFlags);
            }
            else if (FEqualComponentId (c_szInfId_MS_NetMon, pszId))
            {
                hr = HrAddOrRemoveNetMonInOut (dwAraFlags);
            }

            CoTaskMemFree (pszId);
        }

        if (SUCCEEDED(hr) && fProcessEndpoingChange)
        {
            hr = HrProcessEndpointChange ();
        }
    }

    TraceError ("CNdisWan::SysNotifyComponent", hr);
    return hr;
}