/////////////////////////////////////////////////////////////////////////////
//
// Copyright(C) 1999-2000 Microsoft Corporation all rights reserved.
//
// Module:      migratemdb.cpp
//
// Project:     Windows 2000 IAS
//
// Description: IAS NT 4 MDB to IAS W2K MDB Migration Logic
//
// Author:      TLP 1/13/1999
//
//
// Revision     02/24/2000 Moved to a separate dll
//              03/15/2000 Almost completely rewritten
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"

#include "Attributes.h"
#include "Clients.h"
#include "DefaultProvider.h"
#include "GlobalData.h"
#include "migratemdb.h"
#include "migrateregistry.h"
#include "Objects.h"
#include "Policy.h"
#include "Properties.h"
#include "Profiles.h"
#include "profileattributelist.h"
#include "Providers.h"
#include "proxyservergroupcollection.h"
#include "RadiusAttributeValues.h"
#include "Realms.h"
#include "RemoteRadiusServers.h"
#include "ServiceConfiguration.h"
#include "Version.h"
#include "updatemschap.h"

// To remember:
// IAS_MAX_VSA_LENGTH = (253 * 2);
// 1.0 Format Offsets
//VSA_OFFSET                   =  0;
//VSA_OFFSET_ID                =  0;
//VSA_OFFSET_TYPE              =  8;
//VSA_OFFSET_LENGTH            = 10;
//VSA_OFFSET_VALUE_RFC         = 12;
//VSA_OFFSET_VALUE_NONRFC      =  8;

// 2.0 Format Offsets
//VSA_OFFSET_NEW               =  2;
//VSA_OFFSET_ID_NEW            =  2;
//VSA_OFFSET_TYPE_NEW          = 10;
//VSA_OFFSET_LENGTH_NEW        = 12;
//VSA_OFFSET_VALUE_NONRFC_NEW  = 10;


//////////////////////////////////////////////////////////////////////////////
// Helper Functions
//////////////////////////////////////////////////////////////////////////////
/////// From inet.c in ias util directory /////////////
//////////
// Macro to test if a character is a digit.
//////////
#define IASIsDigit(p) ((_TUCHAR)(p - _T('0')) <= 9)

//////////
// Macro to strip one byte of an IP address from a character string.
//    'p'   pointer to the string to be parsed
//    'ul'  unsigned long that will receive the result.
//////////
#define STRIP_BYTE(p,ul) {                \
   if (!IASIsDigit(*p)) goto error;          \
   ul = *p++ - _T('0');                   \
   if (IASIsDigit(*p)) {                     \
      ul *= 10; ul += *p++ - _T('0');     \
      if (IASIsDigit(*p)) {                  \
         ul *= 10; ul += *p++ - _T('0');  \
      }                                   \
   }                                      \
   if (ul > 0xff) goto error;             \
}


///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
//    ias_inet_addr
//
// DESCRIPTION
//
//    This function is similar to the WinSock inet_addr function (q.v.) except
//    it returns the address in host order and it can operate on both ANSI
//    and UNICODE strings.
//
///////////////////////////////////////////////////////////////////////////////
unsigned long __stdcall ias_inet_addr(const WCHAR* cp)
{
   unsigned long token;
   unsigned long addr;

   STRIP_BYTE(cp,addr);
   if (*cp++ != _T('.')) goto error;

   STRIP_BYTE(cp,token);
   if (*cp++ != _T('.')) goto error;
   addr <<= 8;
   addr  |= token;

   STRIP_BYTE(cp,token);
   if (*cp++ != _T('.')) goto error;
   addr <<= 8;
   addr  |= token;

   STRIP_BYTE(cp,token);
   addr <<= 8;
   return addr | token;

error:
   return 0xffffffff;
}


//////////////////////////////////////////////////////////////////////////////
// ConvertVSA
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::ConvertVSA(
                               /*[in]*/ LPCWSTR     pAttributeValueName, 
                               /*[in]*/ LPCWSTR     pAttrValue,
                                        _bstr_t&    NewString
                            )
{
    const long  IAS_MAX_VSA_LENGTH          = (253 * 2);
    const byte  VSA_OFFSET                  =  0;
    const byte  VSA_OFFSET_NEW              =  2;
    const byte  VSA_OFFSET_VALUE_RFC_NEW    = 14;

    wchar_t     szNewValue[IAS_MAX_VSA_LENGTH + 1];

    szNewValue[0] = '\0';

    // RFC compliant integer
    if ( ! lstrcmp(pAttributeValueName, L"URDecimal or Hexadecimal (0x.. "
                                        L"format) Integer") )
    {
        // Add the "02" as the type to the head of the string
        lstrcat(szNewValue,L"02"); 
        // Copy the old value to the new
        lstrcpy(szNewValue + VSA_OFFSET_NEW, pAttrValue + VSA_OFFSET);
        // Strip of the "0x" if necessary and convert to decimal
        if ( ! wcsncmp(&szNewValue[VSA_OFFSET_VALUE_RFC_NEW], L"0x", 2) )
        {
            lstrcpy(&szNewValue[VSA_OFFSET_VALUE_RFC_NEW],
                    &szNewValue[VSA_OFFSET_VALUE_RFC_NEW + 2] );
        }
    }
    // RFC compliant string
    else if ( ! lstrcmp(pAttributeValueName, L"URString") )
    {
        // Set the new string type to "01"
        lstrcat(szNewValue,L"01");
        // Copy the old string to the new
        lstrcpy(szNewValue + VSA_OFFSET_NEW, pAttrValue + VSA_OFFSET);
        // Convert the old hex formatted string to a BSTR (in place)
        wchar_t  wcSaved;
        wchar_t* pXlatPos = &szNewValue[VSA_OFFSET_VALUE_RFC_NEW];
        wchar_t* pNewCharPos = pXlatPos;
        wchar_t* pEnd;
        while ( *pXlatPos != '\0' )
        {
            wcSaved = *(pXlatPos + 2);
            *(pXlatPos + 2) = '\0';
            *pNewCharPos = (wchar_t) wcstol(pXlatPos, &pEnd, 16);
            *(pXlatPos + 2) = wcSaved;
            pXlatPos += 2;
            ++pNewCharPos;
        }
        *pNewCharPos = '\0';
    }
    // RFC compliant hex
    else if ( ! lstrcmp(pAttributeValueName, L"URHexadecimal") )
    {
        // Set the new type and copy the old string to the new
        lstrcat(szNewValue,L"03");
        lstrcpy(szNewValue + VSA_OFFSET_NEW, pAttrValue + VSA_OFFSET);
    }
    // Non RFC compliant (always hex)
    else if ( ! lstrcmp(pAttributeValueName, L"UHHexadecimal") )
    {
        // Set the new type and copy the old string to the new
        lstrcat(szNewValue,L"00");
        lstrcpy(szNewValue + VSA_OFFSET_NEW, pAttrValue + VSA_OFFSET);
    }
    // Error
    else
    {
        _ASSERT(FALSE);
    }

    // Return the new string
    NewString = szNewValue;
}


//////////////////////////////////////////////////////////////////////////////
// MigrateProxyServers 
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::MigrateProxyServers()
{
    const long DEFAULT_PRIORITY = 1;
    const long DEFAULT_WEIGHT   = 50;

    // If there isn't any servers.
    if ( m_GlobalData.m_pRadiusServers->IsEmpty() )
    {
        return;
    }

    CProxyServerGroupCollection& ServerCollection 
                        = CProxyServerGroupCollection::Instance();

    // Do a loop on the RadiusServers sorted by server group
    _bstr_t     CurrentGroupName;

    CProxyServersGroupHelper* pCurrentServerGroup = NULL; //avoid warning
    
    // CurrentGroupName will never match a name received therefore
    // pCurrentServerGroup will always be initialized before being used
    HRESULT hr;
    do 
    {
        _bstr_t GroupName = m_GlobalData.m_pRadiusServers->GetGroupName();
    
        if ( CurrentGroupName != GroupName )
        {
            CProxyServersGroupHelper ServerGroup(m_GlobalData);
            CurrentGroupName = GroupName;

            ServerGroup.SetName(GroupName);

            // Add a server to the collection
            pCurrentServerGroup = ServerCollection.Add(ServerGroup);
        }

        if ( !pCurrentServerGroup )
        {
            _com_issue_error(E_FAIL);
        }

        CProxyServerHelper  Server(m_GlobalData);

        _bstr_t ServerName = m_GlobalData.m_pRadiusServers->
                                                        GetProxyServerName();
        Server.SetAddress(ServerName);
        Server.SetAuthenticationPort(
            m_GlobalData.m_pRadiusServers->GetAuthenticationPortNumber()
                                    );
        Server.SetAccountingPort(
                m_GlobalData.m_pRadiusServers->GetAccountingPortNumber()
                                );
        _bstr_t Secret = m_GlobalData.m_pRadiusServers->GetSharedSecret();
        Server.SetAuthenticationSecret(Secret);
        Server.SetPriority(DEFAULT_PRIORITY);
        Server.SetWeight(DEFAULT_WEIGHT);

        pCurrentServerGroup->Add(Server); // cannot be NULL pointer

        hr = m_GlobalData.m_pRadiusServers->GetNext();
    } 
    while (hr == S_OK);
    // persist everything in the database
    ServerCollection.Persist();

    _com_util::CheckError(hr);
}   


//////////////////////////////////////////////////////////////////////////////
// NewMigrateClients 
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::NewMigrateClients()
{
    // If there isn't any client, return
    if ( m_GlobalData.m_pClients->IsEmpty() )
    {
        return;
    }

    // for each client in the client table, add it (blindly) to the dest table
    // i.e. walkpath to find the clients container
    // create the properties for that containes (clients).
    const WCHAR ClientContainerPath[] = 
                                L"Root\0"
                                L"Microsoft Internet Authentication Service\0"
                                L"Protocols\0"
                                L"Microsoft Radius Protocol\0"
                                L"Clients\0";

    LONG        ClientContainerIdentity;
    m_GlobalData.m_pObjects->WalkPath(
                                         ClientContainerPath, 
                                         ClientContainerIdentity
                                     );

    HRESULT     hr;
    do 
    {
        _bstr_t     ClientName   = m_GlobalData.m_pClients->GetHostName();
        _bstr_t     ClientSecret = m_GlobalData.m_pClients->GetSecret();
    
        LONG    ClientIdentity;
        m_GlobalData.m_pObjects->InsertObject(
                                                ClientName, 
                                                ClientContainerIdentity,
                                                ClientIdentity
                                               );

        // Now insert the properties:
        // IP Address
        _bstr_t PropertyName = L"IP Address";
        m_GlobalData.m_pProperties->InsertProperty(
                                                     ClientIdentity,
                                                     PropertyName,
                                                     VT_BSTR,
                                                     ClientName
                                                  );
        // NAS Manufacturer
        PropertyName = L"NAS Manufacturer";
        _bstr_t     StrValZero = L"0"; // RADIUS Standard  
        m_GlobalData.m_pProperties->InsertProperty(
                                                     ClientIdentity,
                                                     PropertyName,
                                                     VT_I4,
                                                     StrValZero
                                                  );
        // Require Signature
        PropertyName = L"Require Signature";
        m_GlobalData.m_pProperties->InsertProperty(
                                                     ClientIdentity,
                                                     PropertyName,
                                                     VT_BOOL,
                                                     StrValZero
                                                  );
        // Shared Secret
        PropertyName = L"Shared Secret";
        m_GlobalData.m_pProperties->InsertProperty(
                                                     ClientIdentity,
                                                     PropertyName,
                                                     VT_BSTR,
                                                     ClientSecret
                                                  );
        hr = m_GlobalData.m_pClients->GetNext();
    }
    while ( hr == S_OK );

    _com_util::CheckError(hr);
}


//////////////////////////////////////////////////////////////////////////////
// ConvertAttribute
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::ConvertAttribute(
                                      const _bstr_t&    Value,
                                            LONG        Syntax,
                                            LONG&       Type,
                                            bstr_t&     StrVal
                                  )
{
    const size_t    SIZE_LONG_MAX = 14;
    switch (Syntax)
    {
      case IAS_SYNTAX_OCTETSTRING:
        {
            // abinary => OctetString
            Type = VT_BSTR;
            StrVal = Value;
            break;
        }

    case IAS_SYNTAX_STRING:
    case IAS_SYNTAX_UTCTIME:
    case IAS_SYNTAX_PROVIDERSPECIFIC:
        {
            Type = VT_BSTR;
            StrVal = Value;
            break;
        }
    case IAS_SYNTAX_INETADDR:
        {
            unsigned long ulValue = ias_inet_addr(Value);
            _ASSERT( ulValue != 0xffffffff );
            Type = VT_I4;
            WCHAR   TempString[SIZE_LONG_MAX];
            StrVal = _ultow(ulValue, TempString, 10);
            break;
        }
    case IAS_SYNTAX_BOOLEAN:            
        {
            LONG lValue = _wtol(Value);
            Type = VT_BOOL;
            StrVal = lValue? L"-1":L"0";
            break;
        }
    case IAS_SYNTAX_INTEGER:
    case IAS_SYNTAX_UNSIGNEDINTEGER:    
    case IAS_SYNTAX_ENUMERATOR:         
        {
            Type = VT_I4;
            StrVal = Value;
            break;
        }
    default:
        {
            _com_issue_error(E_INVALIDARG);
        }
    }
}


//////////////////////////////////////////////////////////////////////////////
// MigrateAttribute
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::MigrateAttribute(
                                      const _bstr_t&    Attribute,
                                            LONG        AttributeNumber,
                                      const _bstr_t&    AttributeValueName,
                                      const _bstr_t&    StringValue,
                                            LONG        RASProfileIdentity
                                  ) 
{
    // Note: Order might not be needed if the previous DB is always sorted
    const size_t    SIZE_LONG_MAX = 14;
    _bstr_t         LDAPName, StrVal;
    LONG            Syntax, Type; // Type: VT_BSTR, VT_I4, VT_BOOL 
    BOOL            IsMultiValued;
    HRESULT hr = m_GlobalData.m_pAttributes->GetAttribute(
                                                            AttributeNumber,
                                                            LDAPName,
                                                            Syntax,
                                                            IsMultiValued
                                                         );
    if ( FAILED(hr) )
    {
        // Attribute unknown in the new dictionary
        // that should never happen 
        return;
    }

    const LONG VSA = 26; //Vendor-Specific-Attribute
    if ( StringValue.length() && ( AttributeNumber != VSA) )
    {
        // ordinary attribute, not an enumerator
        ConvertAttribute(
                            StringValue,
                            Syntax,
                            Type,
                            StrVal
                        );
        if ( IsMultiValued )
        {
            // If the attribute is multivalued then we need to add the value 
            // Otherwise we just update the attribute value
            m_GlobalData.m_pProperties->InsertProperty(
                                                          RASProfileIdentity,
                                                          LDAPName,
                                                          Type,
                                                          StrVal
                                                      );
        }
        else
        {
            m_GlobalData.m_pProperties->UpdateProperty(
                                                          RASProfileIdentity,
                                                          LDAPName,
                                                          Type,
                                                          StrVal
                                                      );
        }
    }
    else if ( StringValue.length() && ( AttributeNumber == VSA) )
    {
        // VSA Attribute (convert...)
        if ( !AttributeValueName )
        {
            _com_issue_error(E_INVALIDARG);
        }
        ConvertVSA(AttributeValueName, StringValue, StrVal);
        Type = VT_BSTR;
        m_GlobalData.m_pProperties->InsertProperty(
                                                      RASProfileIdentity,
                                                      LDAPName,
                                                      Type,
                                                      StrVal
                                                  );
    }
    else if ( !StringValue.length() )
    {
        // Multivalued attribute.
        Type = VT_I4;
        // Get the number associated with it from the RadiusAttributeValues
        // table
        LONG Number = m_GlobalData.m_pRADIUSAttributeValues->GetAttributeNumber
                                                        (
                                                            Attribute,
                                                            AttributeValueName
                                                        );

        WCHAR   TempString[SIZE_LONG_MAX ];
        StrVal = _ltow(Number, TempString, 10);
        
        // The attribute should be multivalued 
        _ASSERTE(IsMultiValued);
        m_GlobalData.m_pProperties->InsertProperty(
                                                      RASProfileIdentity,
                                                      LDAPName,
                                                      Type,
                                                      StrVal
                                                  );

    }
    else
    {
        // other (unknown)
        _com_issue_error(E_FAIL);
    }
}


//////////////////////////////////////////////////////////////////////////////
// MigrateOtherProfile
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::MigrateOtherProfile(
                                        const _bstr_t&    ProfileName,
                                              LONG        ProfileIdentity
                                     )
{
    _bstr_t    Attribute, AttributeValueName, StringValue;
    LONG       Order, AttributeNumber;

    // Now add the attributes from the NT4 file
    HRESULT hr = m_GlobalData.m_pProfileAttributeList->GetAttribute(
                                                         ProfileName,
                                                         Attribute,
                                                         AttributeNumber,
                                                         AttributeValueName,
                                                         StringValue,
                                                         Order
                                                                   );
    LONG    IndexAttribute = 1;
    // For each attribute in the Profile Attribute List
    // with szProfile = ProfileName
    while ( SUCCEEDED(hr) )
    {
        // migrate it to the default RAS profile in IAS.mdb
        MigrateAttribute(
                            Attribute,
                            AttributeNumber,
                            AttributeValueName,
                            StringValue,
                            ProfileIdentity
                        );        

        hr = m_GlobalData.m_pProfileAttributeList->GetAttribute(
                                                        ProfileName,
                                                        Attribute,
                                                        AttributeNumber,
                                                        AttributeValueName,
                                                        StringValue,
                                                        Order,
                                                        IndexAttribute
                                                               );
        ++IndexAttribute;
    }
}


//////////////////////////////////////////////////////////////////////////////
// MigrateCorpProfile
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::MigrateCorpProfile(
                                        const _bstr_t& ProfileName,
                                        const _bstr_t& Description
                                    )
{
    _bstr_t    Attribute, AttributeValueName, StringValue;
    LONG       Order, AttributeNumber;

    // empty the default profiles attributes
    const WCHAR RASProfilePath[] = 
                            L"Root\0"
                            L"Microsoft Internet Authentication Service\0"
                            L"RadiusProfiles\0";

    LONG        RASProfileIdentity;
    m_GlobalData.m_pObjects->WalkPath(RASProfilePath, RASProfileIdentity);
    
    // Now get the first profile: that's the default (localized) RAS profile
    _bstr_t     DefaultProfileName;
    LONG        DefaultProfileIdentity;
    m_GlobalData.m_pObjects->GetObject(
                                          DefaultProfileName, 
                                          DefaultProfileIdentity,
                                          RASProfileIdentity
                                      );

    // Clean the default attributes
    m_GlobalData.m_pProperties->DeleteProperty(
                                                 DefaultProfileIdentity, 
                                                 L"msRADIUSServiceType"
                                              );
    m_GlobalData.m_pProperties->DeleteProperty(
                                                 DefaultProfileIdentity, 
                                                 L"msRADIUSFramedProtocol"
                                              );

    // Now add the attributes from the NT4 file
    HRESULT hr = m_GlobalData.m_pProfileAttributeList->GetAttribute(
                                                         ProfileName,
                                                         Attribute,
                                                         AttributeNumber,
                                                         AttributeValueName,
                                                         StringValue,
                                                         Order
                                                                   );
    LONG    IndexAttribute = 1;
    // For each attribute in the Profile Attribute List
    // with szProfile = ProfileName
    while ( SUCCEEDED(hr) )
    {
        // migrate it to the default RAS profile in IAS.mdb
        MigrateAttribute(
                            Attribute,
                            AttributeNumber,
                            AttributeValueName,
                            StringValue,
                            DefaultProfileIdentity
                        );        

        hr = m_GlobalData.m_pProfileAttributeList->GetAttribute(
                                                        ProfileName,
                                                        Attribute,
                                                        AttributeNumber,
                                                        AttributeValueName,
                                                        StringValue,
                                                        Order,
                                                        IndexAttribute
                                                               );
        ++IndexAttribute;
    }

    // now not matter what, if Description == ODBC, 
    // Then the policy should have a condition to never match. 
    // (update msNPConstraint
    const _bstr_t   BadProvider = L"ODBC";
    if ( Description == BadProvider ) //safe compare
    {
        // Get the Policy container
        const WCHAR RASPolicyPath[] = 
                            L"Root\0"
                            L"Microsoft Internet Authentication Service\0"
                            L"NetworkPolicy\0";
        // Get its (unique) child
        LONG        RASPolicyIdentity;
        m_GlobalData.m_pObjects->WalkPath(RASPolicyPath, RASPolicyIdentity);
    
        // Now get the first policy: that's the default (localized) RAS policy
        _bstr_t     DefaultPolicyName;
        LONG        DefaultPolicyIdentity;
        m_GlobalData.m_pObjects->GetObject(
                                              DefaultPolicyName, 
                                              DefaultPolicyIdentity,
                                              RASPolicyIdentity
                                          );
        // delete the msNPConstraint (s)
        const _bstr_t Constraint = L"msNPConstraint";
        m_GlobalData.m_pProperties->DeleteProperty(
                                                        DefaultPolicyIdentity, 
                                                        Constraint
                                                    );
        // add a TIMEOFDAY that never matches
        const _bstr_t DumbTime = L"TIMEOFDAY(\"\")";
        m_GlobalData.m_pProperties->InsertProperty( 
                                                     RASPolicyIdentity,
                                                     Constraint,
                                                     VT_BSTR,
                                                     DumbTime
                                                  );
    }
}


//////////////////////////////////////////////////////////////////////////////
// NewMigrateProfiles
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::NewMigrateProfiles()
{
    const LONG      AUTH_PROVIDER_WINDOWS       = 1;
    const LONG      AUTH_PROVIDER_RADIUS_PROXY  = 2;
    const LONG      ACCT_PROVIDER_RADIUS_PROXY  = 2;
    const _bstr_t   RemoteRADIUSServers         = L"Remote RADIUS Servers";
    const _bstr_t   MCIS                        = L"MCIS";
    const _bstr_t   MCISv2                      = L"MCIS version 2.0";
    const _bstr_t   ODBC                        = L"ODBC";
    const _bstr_t   WindowsNT                   = L"Windows NT";
    const _bstr_t   MatchAll = L"TIMEOFDAY(\"0 00:00-24:00; 1 00:00"
            L"-24:00; 2 00:00-24:00; 3 00:00-24:00; 4 00:00-24:00; 5 00:"
            L"00-24:00; 6 00:00-24:00\")";
    
    // Get the Default Provider's data
    _bstr_t        DPUserDefinedName, DPProfile;
    VARIANT_BOOL   DPForwardAccounting, DPSupressAccounting
                 , DPLogoutAccounting;

    m_GlobalData.m_pDefaultProvider->GetDefaultProvider(
                                          DPUserDefinedName,
                                          DPProfile,
                                          DPForwardAccounting,
                                          DPSupressAccounting,
                                          DPLogoutAccounting
                                      );
    _bstr_t ProfileName = m_GlobalData.m_pProfiles->GetProfileName();

    // Do the NT4 CORP migration first if needed
    if ( m_Utils.IsNT4Corp() )
    {
        // This is NT4 CORP. migrate the default profile into 
        // the default policy/profile (not the proxy default)
        _bstr_t     Description = m_GlobalData.m_pProviders->
                            GetProviderDescription(DPUserDefinedName);

        MigrateCorpProfile(ProfileName, Description);
        
        // stop here
        return;
    }
    // Now that is not a NT4 CORP migration

    // Delete the default Proxy Policy / profile
    const WCHAR ProxyPoliciesPath[] = 
                        L"Root\0"
                        L"Microsoft Internet Authentication Service\0"
                        L"Proxy Policies\0";

    LONG        ProxyPolicyIdentity;
    m_GlobalData.m_pObjects->WalkPath(ProxyPoliciesPath, ProxyPolicyIdentity);

    // Now get the first profile: that's the default (localized) RAS policy
    _bstr_t     DefaultPolicyName;
    LONG        DefaultPolicyIdentity;
    m_GlobalData.m_pObjects->GetObject(
                                          DefaultPolicyName, 
                                          DefaultPolicyIdentity,
                                          ProxyPolicyIdentity
                                      );
    m_GlobalData.m_pObjects->DeleteObject(DefaultPolicyIdentity);
    // From now on the default proxy policy / profile is deleted

    // Now empty the default RAS profiles attributes
    const WCHAR RASProfilePath[] = 
                            L"Root\0"
                            L"Microsoft Internet Authentication Service\0"
                            L"RadiusProfiles\0";

    LONG        RASProfileIdentity;
    m_GlobalData.m_pObjects->WalkPath(RASProfilePath, RASProfileIdentity);
    
    // Now get the first profile: that's the default (localized) RAS profile
    _bstr_t     DefaultProfileName;
    LONG        DefaultProfileIdentity;
    m_GlobalData.m_pObjects->GetObject(
                                          DefaultProfileName, 
                                          DefaultProfileIdentity,
                                          RASProfileIdentity
                                      );

    // Clean the default attributes
    m_GlobalData.m_pProperties->DeleteProperty(
                                                 DefaultProfileIdentity, 
                                                 L"msRADIUSServiceType"
                                              );
    m_GlobalData.m_pProperties->DeleteProperty(
                                                 DefaultProfileIdentity, 
                                                 L"msRADIUSFramedProtocol"
                                              );

    // from now on the default RAS profile has its default attributes (the one 
    // in the Advanced tab in the UI) deleted.

    HRESULT     hr;
    LONG        Sequence = 1;
    // Get the list of the profiles
    do
    {
        LONG    RealmIndex = 0;
        ProfileName = m_GlobalData.m_pProfiles->GetProfileName();
        
        // Note: hr should be set only by GetRealmIndex
        do
        {
            // Get the realms associated with that profile
            CPolicy             TempPolicy;
            hr = m_GlobalData.m_pRealms->GetRealmIndex(ProfileName,RealmIndex);
            if ( hr != S_OK )
            {
                // exit that internal do / while  to get the next profile
                break;
            }
            _bstr_t     RealmName = m_GlobalData.m_pRealms->GetRealmName();
            TempPolicy.SetmsNPAction(RealmName);

            ++RealmIndex;
            
            // That will set the realm part of the profile based on the 
            // values in NT4 as well as the reg keys values
            m_GlobalData.m_pRealms->SetRealmDetails(
                                                      TempPolicy, 
                                                      m_Utils
                                                   );

        
            _bstr_t     UserDefinedName = m_GlobalData.m_pRealms
                                               ->GetUserDefinedName();
            // Look up the provider in the providers table. Note: Assume 
            // the proxy servers (and groups) are already migrated.
            _bstr_t ProviderDescription = m_GlobalData.m_pProviders
                                    ->GetProviderDescription(UserDefinedName);

            // Set the sequence order
            TempPolicy.SetmsNPSequence(Sequence);

            // Now set the authentication provider
            if ( ProviderDescription == RemoteRADIUSServers )
            {
                TempPolicy.SetmsAuthProviderType(
                                                  AUTH_PROVIDER_RADIUS_PROXY,
                                                  UserDefinedName
                                                );
            }
            else if ( ( ProviderDescription == MCIS )     || 
                      ( ProviderDescription == MCISv2 )   ||
                      ( ProviderDescription == WindowsNT ) )
            {
                TempPolicy.SetmsAuthProviderType(AUTH_PROVIDER_WINDOWS);
            }
            else if ( ProviderDescription == ODBC )
            {
                // If ODBC is the authentication provider, 
                // then convert that realm into a policy that would never match. 
                // Authentication provider should be NT Domain.
                TempPolicy.SetmsAuthProviderType(AUTH_PROVIDER_WINDOWS);
                const _bstr_t MatchNothing = L"TIMEOFDAY(\"\")";
                TempPolicy.SetmsNPConstraint(MatchNothing);
            }
            else
            {
                _com_issue_error(E_INVALIDARG);
            }

            // persist the policy
            LONG    ProfileIdentity = TempPolicy.Persist(m_GlobalData);
            
            // migrate the profile associated with that policy
            MigrateOtherProfile(ProfileName, ProfileIdentity);
        
            ++Sequence;
        } while (hr == S_OK);
        
        hr = m_GlobalData.m_pProfiles->GetNext();
    } while ( hr == S_OK );
    
    if ( DPUserDefinedName.length() )
    {
        // there is a default provider: a default policy needs to be created
        // same logic as above (mostly)
        CPolicy     DefaultPolicy;
        DefaultPolicy.SetmsNPAction(DPProfile);

        _bstr_t ProviderDescription = m_GlobalData.m_pProviders
                                ->GetProviderDescription(DPUserDefinedName);

        if ( ProviderDescription == RemoteRADIUSServers )
        {
            DefaultPolicy.SetmsAuthProviderType(
                                                  AUTH_PROVIDER_RADIUS_PROXY,
                                                  DPUserDefinedName
                                               );
            DefaultPolicy.SetmsNPConstraint(MatchAll);
        }
        else if ( ( ProviderDescription == MCIS )     || 
                  ( ProviderDescription == MCISv2 )   ||
                  ( ProviderDescription == WindowsNT ) )
        {
            DefaultPolicy.SetmsNPConstraint(MatchAll);
            DefaultPolicy.SetmsAuthProviderType(AUTH_PROVIDER_WINDOWS);
        }
        else if ( ProviderDescription == ODBC )
        {
            // If ODBC is the authentication provider, 
            // then convert that realm into a policy that would never match. 
            // Authentication provider should be NT Domain.
            DefaultPolicy.SetmsAuthProviderType(AUTH_PROVIDER_WINDOWS);
            const _bstr_t MatchNothing = L"TIMEOFDAY(\"\")";
            DefaultPolicy.SetmsNPConstraint(MatchNothing);
        }
        else
        {
            _com_issue_error(E_INVALIDARG);
        }
      
        DefaultPolicy.SetmsNPSequence(Sequence);
        if ( DPForwardAccounting )
        {
            DefaultPolicy.SetmsAcctProviderType(ACCT_PROVIDER_RADIUS_PROXY);
        }
    
        LONG    ProfileIdentity = DefaultPolicy.Persist(m_GlobalData);

        MigrateOtherProfile(DPProfile, ProfileIdentity);
    }
    // else no default provider: no default policy
}


//////////////////////////////////////////////////////////////////////////////
// NewMigrateAccounting
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::NewMigrateAccounting()
{
    const WCHAR AccountingPath[] = 
                            L"Root\0"
                            L"Microsoft Internet Authentication Service\0"
                            L"RequestHandlers\0"
                            L"Microsoft Accounting\0";

    LONG        AccountingIdentity;
    m_GlobalData.m_pObjects->WalkPath(AccountingPath, AccountingIdentity);

    _bstr_t MaxLogSize = m_GlobalData.m_pServiceConfiguration->GetMaxLogSize();

    _bstr_t LogFrequency = m_GlobalData.m_pServiceConfiguration->
                                                    GetLogFrequency();


    _bstr_t PropertyName = L"New Log Frequency";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 AccountingIdentity,
                                                 PropertyName,
                                                 VT_I4,
                                                 LogFrequency
                                              );
 
    PropertyName = L"New Log Size";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 AccountingIdentity,
                                                 PropertyName,
                                                 VT_I4,
                                                 MaxLogSize
                                              );
    DWORD   Value;
    m_Utils.NewGetAuthSrvParameter(L"LogAuthentications", Value);

    _bstr_t     LogAuth;
    Value ? LogAuth = L"-1": LogAuth = L"0";

    PropertyName = L"Log Authentication Packets";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 AccountingIdentity,
                                                 PropertyName,
                                                 VT_BOOL,
                                                 LogAuth
                                              );

    m_Utils.NewGetAuthSrvParameter(L"LogAccounting", Value);

    _bstr_t     LogAcct;
    Value ? LogAcct = L"-1": LogAcct = L"0";

    PropertyName = L"Log Accounting Packets";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 AccountingIdentity,
                                                 PropertyName,
                                                 VT_BOOL,
                                                 LogAcct
                                              );

    _bstr_t     FormatIAS1 = L"0";
    PropertyName = L"Log Format";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 AccountingIdentity,
                                                 PropertyName,
                                                 VT_I4,
                                                 FormatIAS1
                                              );
}


//////////////////////////////////////////////////////////////////////////////
// NewMigrateEventLog
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::NewMigrateEventLog()
{
    const WCHAR EventLogPath[] = 
                            L"Root\0"
                            L"Microsoft Internet Authentication Service\0"
                            L"Auditors\0"
                            L"Microsoft NT Event Log Auditor\0";

    LONG        EventLogIdentity;
    m_GlobalData.m_pObjects->WalkPath(EventLogPath, EventLogIdentity);

    DWORD   Value;
    m_Utils.NewGetAuthSrvParameter(L"LogData", Value);

    _bstr_t     LogData;
    Value ? LogData = L"-1": LogData = L"0";

    _bstr_t PropertyName = L"Log Verbose";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 EventLogIdentity,
                                                 PropertyName,
                                                 VT_BOOL,
                                                 LogData
                                              );

    m_Utils.NewGetAuthSrvParameter(L"LogBogus", Value);

    _bstr_t     LogBogus;
    Value ? LogBogus = L"-1": LogBogus = L"0";

    PropertyName = L"Log Malformed Packets";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 EventLogIdentity,
                                                 PropertyName,
                                                 VT_BOOL,
                                                 LogBogus
                                              );
}


//////////////////////////////////////////////////////////////////////////////
// NewMigrateService
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::NewMigrateService()
{
    const LONG  PORT_SIZE_MAX = 34;
    const WCHAR ServicePath[] = 
                            L"Root\0"
                            L"Microsoft Internet Authentication Service\0"
                            L"Protocols\0"
                            L"Microsoft Radius Protocol\0";

    LONG        ServiceIdentity;

    m_GlobalData.m_pObjects->WalkPath(ServicePath, ServiceIdentity);


    DWORD       Value;
    m_Utils.NewGetAuthSrvParameter(L"RadiusPort", Value);

    WCHAR       TempString[PORT_SIZE_MAX];
    _bstr_t     RadiusPort = _ultow(Value, TempString, 10);


    _bstr_t     PropertyName = L"Authentication Port";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 ServiceIdentity,
                                                 PropertyName,
                                                 VT_BSTR,
                                                 RadiusPort
                                              );

    m_Utils.NewGetAuthSrvParameter(L"AcctPort", Value);

    _bstr_t     AcctPort = _ltow(Value, TempString, 10);


    PropertyName = L"Accounting Port";
    m_GlobalData.m_pProperties->UpdateProperty(
                                                 ServiceIdentity,
                                                 PropertyName,
                                                 VT_BSTR,
                                                 AcctPort
                                              );
}


//////////////////////////////////////////////////////////////////////////////
// NewMigrate
//////////////////////////////////////////////////////////////////////////////
void CMigrateMdb::NewMigrate()
{
    NewMigrateClients();

    if ( !m_Utils.IsNT4Corp() ) // it's either Win2k or NT4 ISP
    {
        // the proxy servers must be migrated before the policies and
        // profiles 
        MigrateProxyServers();
    }
    
    NewMigrateProfiles();
    NewMigrateAccounting(); 
    NewMigrateEventLog();
    NewMigrateService();

    /////////////////////////////
    // Migrate the Registry Keys
    /////////////////////////////
    CMigrateRegistry    MigrateRegistry(m_Utils);
    MigrateRegistry.MigrateProviders();

    //////////////////////////////////////////////////////
    // Update the MSChap Authentication types (password)
    //////////////////////////////////////////////////////
    CUpdateMSCHAP    UpdateMSCHAP(m_GlobalData);
    UpdateMSCHAP.Execute();
}