//+----------------------------------------------------------------------------
//
// File:     main.cpp
//      
// Module:   MIGRATE.DLL 
//
// Synopsis: Main entry point for Migrate.DLL
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
// Author:   quintinb   created     08/21/98
//
//+----------------------------------------------------------------------------

#include "migrate.h"

#include "linkdll.h" // LinkToDll and BindLinkage for cmsecure.lib
#include "linkdll.cpp" // LinkToDll and BindLinkage for cmsecure.lib

const int c_NumFiles = 28;
char OriginalNames[c_NumFiles][MAX_PATH+1] = {
    "\\showicon.exe",
    "\\swflash.ocx",
    "\\urlmon.dll",
    "\\iexpress.exe",
    "\\oleaut32.dll",
    "\\wextract.exe",
    "\\cm32\\enu\\advapi32.dll",
    "\\cm32\\enu\\advpack.dll",
    "\\cm32\\enu\\cmdial32.dll",
    "\\cm32\\enu\\cmdl32.exe",
    "\\cm32\\enu\\cmmgr32.exe",
    "\\cm32\\enu\\cmmgr32.hlp",
    "\\cm32\\enu\\cmpbk32.dll",
    "\\cm32\\enu\\cmstats.dll",
    "\\cm32\\enu\\comctl32.dll",
    "\\cm32\\enu\\ccfg95.dll",
    "\\cm32\\enu\\ccfgnt.dll",
    "\\cm32\\enu\\icwscrpt.exe",
    "\\cm32\\enu\\cnet16.dll",
    "\\cm32\\enu\\cnetcfg.dll",
    "\\cm32\\enu\\mbslgn32.dll",
    "\\cm32\\enu\\readme.txt",
    "\\cm32\\enu\\rnaph.dll",
    "\\cm32\\enu\\w95inf16.dll",
    "\\cm32\\enu\\w95inf32.dll",
    "\\cm32\\enu\\wininet.dll",
    "\\cm32\\enu\\wintrust.dll",
    "\\cm32\\enu\\cmcfg32.dll",
};

char TempNames[c_NumFiles][MAX_PATH+1] = {
    "\\showicon.tmp",
    "\\swflash.tmp",
    "\\urlmon.tmp",
    "\\iexpress.tmp",
    "\\oleaut32.tmp",
    "\\wextract.tmp",
    "\\advapi32.tmp",
    "\\advpack.tmp",
    "\\cmdial32.tmp",
    "\\cmdl32.tmp",
    "\\cmmgr32.001",
    "\\cmmgr32.002",
    "\\cmpbk32.tmp",
    "\\cmstats.tmp",
    "\\comctl32.tmp",
    "\\ccfg95.tmp",
    "\\ccfgnt.tmp",
    "\\icwscrpt.tmp",
    "\\cnet16.tmp",
    "\\cnetcfg.tmp",
    "\\mbslgn32.tmp",
    "\\readme.tmp",
    "\\rnaph.tmp",
    "\\w95inf16.tmp",
    "\\w95inf32.tmp",
    "\\wininet.tmp",
    "\\wintrust.tmp",
    "\\cmcfg32.tmp",
};

//
//  Global Vars
//
BOOL g_bMigrateCmak10;
BOOL g_bMigrateCmak121;
BOOL g_bMigrateCm;
BOOL g_fInitSecureCalled;
DWORD g_dwNumValues;
DWORD  g_dwTlsIndex; // thread local storage index
HINSTANCE g_hInstance;
TCHAR g_szWorkingDir[MAX_PATH+1];
TCHAR g_szCmakPath[MAX_PATH+1];
VENDORINFO g_VendorInfo;
           
//+---------------------------------------------------------------------------
//
//	Function:	DllMain
//
//	Synopsis:	Main initialization function for this dll.  Called whenever
//              a new instance of this dll is loaded or a new thread created.
//
//	Arguments:	HINSTANCE hinstDLL - handle to DLL module 
//              DWORD fdwReason - reason for calling function 
//              LPVOID lpvReserved - reserved 
//
//	Returns:	BOOL - TRUE if initialization was successful, FALSE otherwise
//
//	History:	quintinb    Created Header      01/13/2000
//
//----------------------------------------------------------------------------
extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	if (fdwReason == DLL_PROCESS_ATTACH)
    {
        //
        //  Init Globals
        //

        g_hInstance = hinstDLL;
        g_fInitSecureCalled = FALSE;
		g_bMigrateCmak10 = FALSE;
		g_bMigrateCmak121 = FALSE;

        ZeroMemory(g_szCmakPath, sizeof(g_szCmakPath));

        //
        // alloc tls index
        //
        g_dwTlsIndex = TlsAlloc();
        if (g_dwTlsIndex == TLS_OUT_OF_INDEXES)
        {
            return FALSE;
        }

        MYVERIFY(DisableThreadLibraryCalls(hinstDLL));
    }

    else if (fdwReason == DLL_PROCESS_DETACH)
    {
        //
        // free the tls index
        //
        if (g_dwTlsIndex != TLS_OUT_OF_INDEXES)
        {
            TlsFree(g_dwTlsIndex);
        }
    }

	return TRUE;
}

//+----------------------------------------------------------------------------
//
// Function:  QueryVersion
//
// Synopsis:  Supplies the Dll's version and identification information.
//
// Arguments: OUT LPCSTR  *ProductID - buffer to hold a string that uniquely 
//                                     identifies the migration dll
//            OUT LPUINT DllVersion - Pointer to an Integer to hold the version 
//                                    number of the migration DLL
//            OUT LPINT *CodePageArray - pointer to an array of code pages that
//                                       the migration dll supports
//            OUT LPCSTR  *ExeNamesBuf - a pointer to a multi-sz string.  The
//                                       buffer contains a null separated list
//                                       of executable file names that the
//                                       migration engine should search for.
//                                       Full paths to all occurences of these
//                                       executables will be copied to the
//                                       [Migration Paths] section of migrate.inf.
//            OUT PVENDORINFO  *VendorInfo - pointer to a VENDORINFO structure
//
// Returns:   LONG -  ERROR_NOT_INSTALLED if the component that this dll is to 
//                    migrate isn't installed.  The migration dll won't be called
//                    in any of the other stages if this is the return value.
//                    ERROR_SUCCESS if the component that this dll is to migrate
//                    is installed and requires migration.  This will allow the
//                    migration dll to be called again for further migration.
//
// History:   quintinb  Created Header    8/27/98
//
//+----------------------------------------------------------------------------
LONG CALLBACK QueryVersion(OUT LPCSTR  *ProductID, OUT LPUINT DllVersion, 
                               OUT LPINT *CodePageArray, OUT LPCSTR  *ExeNamesBuf, 
                               OUT PVENDORINFO  *VendorInfo)
{
    //
    //  Record our version information.
    //
    if (NULL != ProductID)
    {
        *ProductID = c_pszProductIdString;
    }

    if (NULL != DllVersion)
    {
        *DllVersion = uCmMigrationVersion;
    }    

    if (NULL != CodePageArray)
    {
        *CodePageArray = NULL; // no code page dependencies, language neutral
    }

    if (NULL != ExeNamesBuf)
    {
        *ExeNamesBuf = NULL; // 
    }

    if (NULL != VendorInfo)
    {
        *VendorInfo= &g_VendorInfo;
        ZeroMemory(&g_VendorInfo, sizeof(VENDORINFO));

        //
        //  Use the standard MS vendor info from vendinfo.mc
        //
        FormatMessage( 
            FORMAT_MESSAGE_FROM_HMODULE,
            g_hInstance,
            MSG_VI_COMPANY_NAME,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            &g_VendorInfo.CompanyName[0],
            sizeof(g_VendorInfo.CompanyName),
            NULL
            );
    
        FormatMessage( 
            FORMAT_MESSAGE_FROM_HMODULE,
            g_hInstance,
            MSG_VI_SUPPORT_NUMBER,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            &g_VendorInfo.SupportNumber[0],
            sizeof(g_VendorInfo.SupportNumber),
            NULL
            );
    
        FormatMessage( 
            FORMAT_MESSAGE_FROM_HMODULE,
            g_hInstance,
            MSG_VI_SUPPORT_URL,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            &g_VendorInfo.SupportUrl[0],
            sizeof(g_VendorInfo.SupportUrl),
            NULL
            );
    
        FormatMessage( 
            FORMAT_MESSAGE_FROM_HMODULE,
            g_hInstance,
            MSG_VI_INSTRUCTIONS,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            &g_VendorInfo.InstructionsToUser[0],
            sizeof(g_VendorInfo.InstructionsToUser),
            NULL
            );
    }
        
    //
    //  Now try to detect if CMAK or CM are installed.  If they are and the versions
    //  are such that they need to be migrated, then return ERROR_SUCCESS.  Otherwise
    //  we don't need to do any migration, so return ERROR_NOT_INSTALLED.
    //

    LONG lReturnValue = ERROR_NOT_INSTALLED;
	CmVersion CmVer;
    if (CmVer.IsPresent())
    {
        lReturnValue = ERROR_SUCCESS;
    }
    else
    {
		CmakVersion CmakVer;
        //
        //  Okay, CM wasn't installed so look for CMAK.
        //
        if (CmakVer.IsPresent())
        {
            //
            //  Okay, CMAK exists
            //
            lReturnValue = ERROR_SUCCESS;
        }
    }

    return lReturnValue;
}

//+----------------------------------------------------------------------------
//
// Function:  Initialize9x
//
// Synopsis:  This function is called so that the migration dll can initialize
//            itself on the Win9x side of the migration.  The migration dll
//            should not make any modifications to the system in this call, as
//            it is only for initialization and searching to see if your component
//            is installed.
//
// Arguments: IN LPCSTR WorkingDirectory - path of the temporary storage dir for
//                                         the migration dll.
//            IN LPCSTR SourceDirectories - a multi-sz list of the Win2k source
//                                          directory or directories
//            IN LPCSTR MediaDirectory - specifies the path to the original media
//                                       directory
//
// Returns:   LONG -  ERROR_NOT_INSTALLED if the component that this dll is to 
//                    migrate isn't installed.  The migration dll won't be called
//                    in any of the other stages if this is the return value.
//                    ERROR_SUCCESS if the component that this dll is to migrate
//                    is installed and requires migration.  This will allow the
//                    migration dll to be called again for further migration.
//
// History:   quintinb  Created Header    8/27/98
//
//+----------------------------------------------------------------------------
LONG CALLBACK Initialize9x(IN LPCSTR WorkingDirectory, IN LPCSTR SourceDirectories, 
                           IN LPCSTR MediaDirectory)
{
    HKEY hKey;
    //
    //  Check to see if we need to Migrate CMAK
    //

	CmakVersion CmakVer;

	lstrcpy(g_szWorkingDir, WorkingDirectory);

	if (CmakVer.IsPresent())
	{
		if (CmakVer.GetInstallLocation(g_szCmakPath))
		{
                        //
                        //  Then we have a CMAK path.  Write this to the handled key so that
                        //  they won't mess with our files.
                        //

			TCHAR szTemp[MAX_PATH+1];
                        wsprintf(szTemp, "%s\\migrate.inf", WorkingDirectory);
                        MYVERIFY(0 != WritePrivateProfileString(c_pszSectionHandled, g_szCmakPath, 
                        c_pszDirectory, szTemp));

			//
			//	Now try to figure out what version of CMAK we have to see if we need
			//  to run the migration DLL or not.  If the CMAK.exe version is 6.00.613.0 (1.0)
			//  then we should migrate it.  If it is higher than that, 1.1 or 1.2 it is 
			//  beta and we shouldn't support the upgrade anyway (I purposely am not
			//  going to run the migration on it).  If it is IE5 IEAK CMAK, then it should
			//  survive upgrade without a problem.
			//
			
			if (CmakVer.Is10Cmak())
			{
				g_bMigrateCmak10 = TRUE;
			}
			else if (CmakVer.Is121Cmak())
			{
				g_bMigrateCmak121 = TRUE;
			}
		}	
	}

    //
    //  Check to see if we need to migrate CM Profiles
    //
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0, 
        KEY_READ, &hKey))
    {
        if ((ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, 
            &g_dwNumValues, NULL, NULL, NULL, NULL)) && (g_dwNumValues > 0))
        {
            //
            //  Then we have mappings values, so we need to migrate them.
            //
            g_bMigrateCm = TRUE;

        }
        RegCloseKey(hKey);
    }

    if (g_bMigrateCmak10 || g_bMigrateCmak121 || g_bMigrateCm)
    {
        return ERROR_SUCCESS;
    }
    else
    {
        return ERROR_NOT_INSTALLED;
    }
}



//+----------------------------------------------------------------------------
//
// Function:  MigrateUser9x
//
// Synopsis:  Called once for each Win9x user being migrated.  Its purpose is to
//            allow migration of per user settings.
//
// Arguments: IN HWND ParentWnd - Window handle of the parent window, used if
//                                the migration dll needs to display UI.  If NULL,
//                                running in unattended mode and no UI should be
//                                displayed.
//            IN LPCSTR AnswerFile - Supplies the path to the answer file.
//            IN HKEY UserRegKey - reg key for the HKEY_CURRENT_USER key of the
//                                 user currently being migrated.
//            IN LPCSTR UserName - username of the user being migrated
//            LPVOID Reserved - reserved
//
// Returns:   LONG - ERROR_NOT_INSTALLED - if no per user processing is required.
//                   ERROR_CANCELLED - if the user wants to exit setup
//                   ERROR_SUCCESS - the migration dll processed this user successfully
//
// History:   quintinb  Created Header    8/27/98
//
//+----------------------------------------------------------------------------
LONG
CALLBACK MigrateUser9x(IN HWND ParentWnd, IN LPCSTR AnswerFile, 
                           IN HKEY UserRegKey, IN LPCSTR UserName, LPVOID Reserved)
{
    return ERROR_NOT_INSTALLED; 
}


//+----------------------------------------------------------------------------
//
// Function:  MigrateSystem9x
//
// Synopsis:  Allows migration of system wide settings on the Windows 9x side.
//
// Arguments: IN HWND ParentWnd - parent window handle for the display of UI, 
//                                NULL if in unattended mode
//            IN LPCSTR AnswerFile - full path to the answer file
//            LPVOID Reserved - reserved
//
// Returns:   LONG -  ERROR_NOT_INSTALLED if the component that this dll is to 
//                    migrate isn't installed.  The migration dll won't be called
//                    in any of the other stages if this is the return value.
//                    ERROR_SUCCESS if the component that this dll is to migrate
//                    is installed and requires migration.  This will allow the
//                    migration dll to be called again for further migration.
//
// History:   quintinb  Created Header    8/27/98
//
//+----------------------------------------------------------------------------
LONG
CALLBACK MigrateSystem9x(IN HWND ParentWnd, IN LPCSTR AnswerFile, LPVOID Reserved)
{
    LONG lReturn = ERROR_NOT_INSTALLED;
	TCHAR szSystemDir[MAX_PATH+1];

	GetSystemDirectory(szSystemDir, MAX_PATH);

	//
	//	Setup deletes a bunch of the files that 1.0 CMAK or IEAK5 CMAK need to function.
	//  Since we currently don't support NT5 CMAK on WKS, we need to copy these files
	//  to the setup provided working directory, so that we can copy them bak once
	//  we boot into NT.
	//
    if (g_bMigrateCmak10 && (TEXT('\0') != g_szCmakPath[0]) && (TEXT('\0') != g_szWorkingDir[0]))
    {
        TCHAR szDest[MAX_PATH+1];
        TCHAR szSrc[MAX_PATH+1];
        for (int i=0; i < c_NumFiles; i++)
        {
            MYVERIFY(CELEMS(szSrc) > (UINT)wsprintf(szSrc, TEXT("%s%s"), g_szCmakPath, OriginalNames[i]));
            MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s%s"), g_szWorkingDir, TempNames[i]));
            if (FileExists(szSrc))
            {
                MYVERIFY(FALSE != CopyFile(szSrc, szDest, FALSE));
            }
        }

        lReturn &= ERROR_SUCCESS;    
    }
	else if (g_bMigrateCmak121 && (TEXT('\0') != szSystemDir[0]) && 
		     (TEXT('\0') != g_szWorkingDir[0]))
	{
        TCHAR szDest[MAX_PATH+1];
        TCHAR szSrc[MAX_PATH+1];

		//
		//	Copy w95inf16.dll to the working directory and rename it w95inf16.tmp
		//
        MYVERIFY(CELEMS(szSrc) > (UINT)wsprintf(szSrc, TEXT("%s\\%s%s"), szSystemDir, c_pszW95Inf16, c_pszDll));
        MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s\\%s%s"), g_szWorkingDir, c_pszW95Inf16, c_pszTmp));
        if (FileExists(szSrc))
        {
            MYVERIFY(FALSE != CopyFile(szSrc, szDest, FALSE));
        }

		//
		//	Copy w95inf32.dll to the working directory and rename it w95inf32.tmp
		//
        MYVERIFY(CELEMS(szSrc) > (UINT)wsprintf(szSrc, TEXT("%s\\%s%s"), szSystemDir, c_pszW95Inf32, c_pszDll));
        MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s\\%s%s"), g_szWorkingDir, c_pszW95Inf32, c_pszTmp));
        if (FileExists(szSrc))
        {
            MYVERIFY(FALSE != CopyFile(szSrc, szDest, FALSE));
        }

        lReturn &= ERROR_SUCCESS;	
	}

    if (g_bMigrateCm)
    {
        //
        //  Enumerate all the installed profiles on the machine.  For each profile check
        //  for a UserInfo\<CurrentServiceNameKey>.  If this key exists, then go to the next
        //  profile or user.  If it doesn't exist, then read the data from the cmp file.  If
        //  the cmp has data marked as being stored then we need to save the password.  If
        //  the password isn't in the cmp then it is in the wnet cache.  We must then
        //  retrieve it.
        //
        HKEY hKey;
        HKEY hTempKey;
        TCHAR szTemp[MAX_PATH+1];
        TCHAR szLongServiceName[MAX_PATH+1];
        TCHAR szCmpPath[MAX_PATH+1];

        if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0, KEY_READ, 
        &hKey))
        {
            DWORD dwIndex = 0;
            DWORD dwValueSize = MAX_PATH;
            DWORD dwDataSize = MAX_PATH;
            DWORD dwType;
                
            while (ERROR_SUCCESS == RegEnumValue(hKey, dwIndex, szLongServiceName, 
                &dwValueSize, NULL, &dwType, (LPBYTE)szCmpPath, &dwDataSize))
            {
                if (REG_SZ == dwType)
                {
                    MYDBGASSERT(TEXT('\0') != szLongServiceName[0]);
                    MYDBGASSERT(TEXT('\0') != szCmpPath[0]);

                    //
                    //  If the user saved their password or their Internet password,
                    //  then we must make sure that it exists in the cmp (in encrypted form)
                    //  so that when the user runs CM on the NT5 side of the migration,
                    //  CM will move the settings to the new format.  Note, that if the 
                    //  cmp doesn't specify that the password(s) be saved, then this
                    //  function just returns as there is no password to ensure is in the
                    //  cmp.
                    MYVERIFY(EnsureEncryptedPasswordInCmpIfSaved(szLongServiceName, szCmpPath));
                }

                dwValueSize = MAX_PATH;
                dwDataSize = MAX_PATH;
                dwIndex++;
            
                if (dwIndex == g_dwNumValues)
                {
                    break;
                }
            }
            MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
        }

        lReturn &= ERROR_SUCCESS;    
    }

    return lReturn;
}



//+----------------------------------------------------------------------------
//
// Function:  InitializeNT
//
// Synopsis:  First function called on the Win2k side of the migration, used to
//            setup the Win2k migration.  Similar to Initialize9x but on the Win2k
//            side.  No changes to the system should be made in this function.
//
// Arguments: IN LPCWSTR WorkingDirectory - temporary storage, same as path given
//                                          on the Win9x side
//            IN LPCWSTR SourceDirectories - a multi-sz list of the Win2k source
//                                           directory or directories
//            LPVOID Reserved - reserved
//
// Returns:   LONG - ERROR_SUCCESS unless an init error occurs.
//
// History:   quintinb  Created Header    8/27/98
//
//+----------------------------------------------------------------------------
LONG
CALLBACK InitializeNT(IN LPCWSTR WorkingDirectory, IN LPCWSTR SourceDirectories, LPVOID Reserved)
{
    HKEY hKey;
	//
	//	Convert the WorkingDirectory to MultiByte
	//
	MYVERIFY (0 != WideCharToMultiByte(CP_THREAD_ACP, WC_COMPOSITECHECK, WorkingDirectory, -1, 
		g_szWorkingDir, MAX_PATH, NULL, NULL));

    //
    //  Check to see if we need to Migrate CMAK
    //
	CmakVersion CmakVer;

	if (CmakVer.IsPresent())
	{
		if (CmakVer.GetInstallLocation(g_szCmakPath))
		{
			//
			//	Now try to figure out what version of CMAK we have to see if we need
			//  to run the migration DLL or not.  If the CMAK.exe version is 6.00.613.0 (1.0)
			//  then we should migrate it.  If it is higher than that, 1.1 or 1.2 it is 
			//  beta and we shouldn't support the upgrade anyway (I purposely am not
			//  going to run the migration on it).  If it is IE5 IEAK CMAK, then it should
			//  survive upgrade without a problem.
			//
			
			if (CmakVer.Is10Cmak())
			{
				g_bMigrateCmak10 = TRUE;
			}
			else if (CmakVer.Is121Cmak())
			{
				g_bMigrateCmak121 = TRUE;
			}
		}	
	}

    //
    //  Check to see if we need to migrate CM Profiles
    //
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0, 
        KEY_READ, &hKey))
    {
        if ((ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, 
            &g_dwNumValues, NULL, NULL, NULL, NULL)) && (g_dwNumValues > 0))
        {
            //
            //  Then we have mappings values, so we need to migrate them.
            //
            g_bMigrateCm = TRUE;

        }
        RegCloseKey(hKey);
    }

    if (g_bMigrateCmak10 || g_bMigrateCmak121 || g_bMigrateCm)
    {
        return ERROR_SUCCESS;
    }
    else
    {
        return ERROR_NOT_INSTALLED;
    }
}



//+----------------------------------------------------------------------------
//
// Function:  MigrateUserNT
//
// Synopsis:  Called once per migrated user on win2k.  Used to migrated any
//            per user settings saved from MigrateUser9x.
//
// Arguments: IN HINF UnattendInfHandle - valid inf handle to unattend.txt, 
//                                        for use with the setup API's
//            IN HKEY UserRegHandle - HKEY_CURRENT_USER of the user currently
//                                    being migrated
//            IN LPCWSTR UserName - username of the user currently being migrated
//            LPVOID Reserved - reserved
//
// Returns:   LONG - ERROR_SUCCESS or a win32 error code (will abort migration dll
//                   processing)
//
// History:   quintinb  Created Header    8/27/98
//
//+----------------------------------------------------------------------------
LONG
CALLBACK MigrateUserNT(IN HINF UnattendInfHandle, IN HKEY UserRegHandle, 
                            IN LPCWSTR UserName, LPVOID Reserved)
{
    return ERROR_SUCCESS;
}



//+----------------------------------------------------------------------------
//
// Function:  MigrateSystemNT
//
// Synopsis:  Called to allow system wide migration changes to be made on the
//            Win2k side.
//
// Arguments: IN HINF UnattendInfHandle - handle to the unattend.txt file
//            LPVOID Reserved - reserved
//
// Returns:   LONG - ERROR_SUCCESS or a win32 error code (will abort migration dll
//                   processing)
//
// History:   quintinb  Created Header    8/27/98
//
//+----------------------------------------------------------------------------
LONG
CALLBACK MigrateSystemNT(IN HINF UnattendInfHandle, LPVOID Reserved)
{

    LONG lReturn = ERROR_NOT_INSTALLED;
    DWORD dwDisposition;
	TCHAR szSystemDir[MAX_PATH+1];

	GetSystemDirectory(szSystemDir, MAX_PATH);

    const TCHAR* const c_pszSystemFmt = TEXT("%s\\system\\%s.inf");
    const TCHAR* const c_pszValueString = TEXT("Connection Manager Profiles Upgrade");
    const TCHAR* const c_pszRegRunKey = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run");
    const TCHAR* const c_pszCmdLine = TEXT("cmstp.exe /m");

    if (g_bMigrateCmak10 && (TEXT('\0') != g_szCmakPath[0]) && (TEXT('\0') != g_szWorkingDir[0]))
    {
        TCHAR szDest[MAX_PATH+1];
        TCHAR szSrc[MAX_PATH+1];
        for (int i=0; i < c_NumFiles; i++)
        {
            MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s%s"), g_szCmakPath, 
				OriginalNames[i]));

            MYVERIFY(CELEMS(szSrc) > (UINT)wsprintf(szSrc, TEXT("%s%s"), g_szWorkingDir, 
				TempNames[i]));

            if (FileExists(szSrc))
            {
                MYVERIFY(FALSE != CopyFile(szSrc, szDest, FALSE));
            }
        }

        lReturn &= ERROR_SUCCESS;    
    }
	else if (g_bMigrateCmak121 && (TEXT('\0') != g_szCmakPath[0]) && 
		     (TEXT('\0') != szSystemDir[0]))
	{
        TCHAR szDest[MAX_PATH+1];
        TCHAR szSrc[MAX_PATH+1];

		//
		//	Copy w95inf16.tmp in the working dir back to the systemdir and rename it .dll
		//
        MYVERIFY(CELEMS(szSrc) > (UINT)wsprintf(szSrc, TEXT("%s\\%s%s"), g_szWorkingDir, 
			c_pszW95Inf16, c_pszTmp));

        MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s\\%s%s"), szSystemDir, 
			c_pszW95Inf16, c_pszDll));

        if (FileExists(szSrc))
        {
            MYVERIFY(FALSE != CopyFile(szSrc, szDest, FALSE));
        }

		//
		//	Copy w95inf32.tmp in the working dir back to the systemdir and rename it .dll
		//
        MYVERIFY(CELEMS(szSrc) > (UINT)wsprintf(szSrc, TEXT("%s\\%s%s"), g_szWorkingDir, 
			c_pszW95Inf32, c_pszTmp));

        MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s\\%s%s"), szSystemDir, 
			c_pszW95Inf32, c_pszDll));

        if (FileExists(szSrc))
        {
            MYVERIFY(FALSE != CopyFile(szSrc, szDest, FALSE));
        }

        lReturn &= ERROR_SUCCESS;	
	}


    if (g_bMigrateCm)
    {
        //
        //  Enumerate all the installed profiles on the machine.  For each profile check to
        //  see if the profile inf is located in the system (that's system not system32) dir.
        //  If it is, then we need to move it to system32 so that our code will know where to
        //  find it.
        //
        HKEY hKey;
        HKEY hTempKey;
        TCHAR szSource[MAX_PATH+1];
        TCHAR szDest[MAX_PATH+1];
        TCHAR szLongServiceName[MAX_PATH+1];
        TCHAR szWindowsDir[MAX_PATH+1];
        TCHAR szSystemDir[MAX_PATH+1];
        TCHAR szCmpPath[MAX_PATH+1];

        if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 
            0, KEY_READ, &hKey))
        {
            DWORD dwIndex = 0;
            DWORD dwValueSize = MAX_PATH;
            DWORD dwDataSize = MAX_PATH;
            DWORD dwType;
                
            while (ERROR_SUCCESS == RegEnumValue(hKey, dwIndex, szLongServiceName, 
                &dwValueSize, NULL, &dwType, (LPBYTE)szCmpPath, &dwDataSize))
            {
                if (REG_SZ == dwType)
                {
                    MYDBGASSERT(TEXT('\0') != szLongServiceName[0]);
                    MYDBGASSERT(TEXT('\0') != szCmpPath[0]);
                    CFileNameParts CmpPath(szCmpPath);

                    GetWindowsDirectory(szWindowsDir, MAX_PATH);
                    MYDBGASSERT(TEXT('\0') != szWindowsDir[0]);

                    GetSystemDirectory(szSystemDir, MAX_PATH);
                    MYDBGASSERT(TEXT('\0') != szSystemDir[0]);

                    MYVERIFY(CELEMS(szSource) > (UINT)wsprintf(szSource, c_pszSystemFmt, szWindowsDir, CmpPath.m_FileName));
                    MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s\\%s.inf"), szSystemDir, CmpPath.m_FileName));

                    if (CopyFile(szSource, szDest, FALSE))
                    {
                        DeleteFile(szSource);
                    }
                }

                dwValueSize = MAX_PATH;
                dwDataSize = MAX_PATH;
                dwIndex++;
            
                if (dwIndex == g_dwNumValues)
                {
                    break;
                }
            }
            MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
        }

        if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, c_pszRegRunKey, 0, NULL,
            REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL, &hKey, &dwDisposition))
        {
            RegSetValueEx(hKey, c_pszValueString, 0, REG_SZ, (CONST BYTE*)c_pszCmdLine, 
                sizeof(TCHAR)*(lstrlen(c_pszCmdLine)+1));

            RegCloseKey(hKey);
        }

        lReturn &= ERROR_SUCCESS;    
    }

    return lReturn;
}

//+----------------------------------------------------------------------------
//
// Function:  EnsureEncryptedPasswordInCmpIfSaved
//
// Synopsis:  This function is called on the Win9x side of the migration so
//            that if a password is in the Win9x password cache (which only
//            happens if the user has profiling enabled), it will be retrieved,
//            encrypted, and written to the cmp.  This enables CM to populate
//            a users registry with the starting password whenever they first
//            launch the CM profile.  This no functionality is lost from the
//            shared password feature that win9x had.
//
// Arguments: szLongServiceName - Service name of the profile to retrieve 
//                                the password for
//            szCmpPath - full path to the cmp to write the password too
//
// Returns:   BOOL - returns TRUE if successful
//
// History:   quintinb      Created             08/27/98
//            nickball      CmWipePassword      08/04/99
//
//+----------------------------------------------------------------------------
BOOL EnsureEncryptedPasswordInCmpIfSaved(LPCTSTR pszLongServiceName, LPCTSTR pszCmpPath)
{
    TCHAR szPassword[MAX_PATH+1] = TEXT("");
    TCHAR szCacheEntryName[MAX_PATH+1];
    TCHAR szEncryptedPassword[MAX_PATH+1];
    DWORD dwSize;
    DWORD dwCryptType = 0;
    GetCachedPassword pfnGetCachedPassword = NULL;
    int iTemp=0;

    CDynamicLibrary MprDll(TEXT("mpr.dll"));
   
    iTemp = GetPrivateProfileInt(c_pszCmSection, c_pszCmEntryRememberPwd, 0, pszCmpPath);
    if (iTemp)
    {
        GetPrivateProfileString(c_pszCmSection, c_pszCmEntryPassword, TEXT(""), szPassword, MAX_PATH, pszCmpPath);

        if (TEXT('\0') == szPassword[0])
        {
            //
            //  The string must be in the password cache.  Build the key string
            //  for the cache.
            //
            MYVERIFY(CELEMS(szCacheEntryName) > (UINT)wsprintf(szCacheEntryName, 
            TEXT("%s - Sign-In (Connection Manager)"), pszLongServiceName));
            
            pfnGetCachedPassword = (GetCachedPassword)MprDll.GetProcAddress(TEXT("WNetGetCachedPassword"));

            if (NULL == pfnGetCachedPassword)
            {
                CmWipePassword(szPassword);
                return FALSE;
            }
            else
            {
                WORD wStr = (WORD)256;

                if (ERROR_SUCCESS == pfnGetCachedPassword(szCacheEntryName, 
                    (WORD)lstrlen(szCacheEntryName), szPassword, &wStr, 80))
                {
                    //
                    //  Okay, we retrived the password, now lets encrypt it and write it 
                    //  to the cmp
                    //

                    if (EncryptPassword(szPassword, szEncryptedPassword, &dwSize, &dwCryptType))
                    {
                        //
                        //  Write the encrypted password
                        //
                        WritePrivateProfileString(c_pszCmSection, c_pszCmEntryPassword, szEncryptedPassword, 
                            pszCmpPath);

                        //
                        //  Write the encryption type
                        //
                        wsprintf(szPassword, TEXT("%u"), dwCryptType);
                        WritePrivateProfileString(c_pszCmSection, c_pszCmEntryPcs, szPassword, 
                            pszCmpPath);
                    }
                }
                
            }
           
        }
    }

    //
    //  Now do the same for the Internet Password
    //
    
    iTemp = GetPrivateProfileInt(c_pszCmSection, c_pszCmEntryRememberInetPwd, 0, pszCmpPath);

    if (iTemp)
    {
        GetPrivateProfileString(c_pszCmSection, c_pszCmEntryInetPassword, TEXT(""), szPassword, MAX_PATH, pszCmpPath);

        if (TEXT('\0') == szPassword[0])
        {
            //
            //  The string must be in the password cache.  Build the key string
            //  for the cache.
            //
            MYVERIFY(CELEMS(szCacheEntryName) > (UINT)wsprintf(szCacheEntryName, 
            TEXT("%s - Sign-In (Connection Manager)-tunnel"), pszLongServiceName));
            
            pfnGetCachedPassword = (GetCachedPassword)MprDll.GetProcAddress(TEXT("WNetGetCachedPassword"));

            if (NULL == pfnGetCachedPassword)
            {
                CmWipePassword(szPassword);
                return FALSE;
            }
            else
            {
                WORD wStr = (WORD)256;

                if (ERROR_SUCCESS == pfnGetCachedPassword(szCacheEntryName, 
                    (WORD)lstrlen(szCacheEntryName), szPassword, &wStr, 80))
                {
                    //
                    //  Okay, we retrived the password, now lets encrypt it and write it 
                    //  to the cmp
                    //
                    
                    dwCryptType = 0;

                    if (EncryptPassword(szPassword, szEncryptedPassword, &dwSize, &dwCryptType))
                    {
                        //
                        //  Write the encrypted password
                        //
                        WritePrivateProfileString(c_pszCmSection, c_pszCmEntryInetPassword, szEncryptedPassword, 
                            pszCmpPath);

                        //
                        //  Write the encryption type
                        //
                        wsprintf(szPassword, TEXT("%u"), dwCryptType);
                        WritePrivateProfileString(c_pszCmSection, c_pszCmEntryPcs, szPassword, 
                            pszCmpPath);
                    }
                }
                
            }
           
        }
    }
    
    CmWipePassword(szPassword);

    return TRUE;
}

//+----------------------------------------------------------------------------
//
// Function:  EncryptPassword
//
// Synopsis:  
//
// Arguments: IN LPCTSTR pszPassword - 
//            OUT LPTSTR pszEncryptedPassword - 
//            OUT LPDWORD lpdwBufSize - 
//            OUT LPDWORD lpdwCryptType - 
//
// Returns:   BOOL - 
//
// History:   quintinb      Created Header      8/27/98
//            nickball      CmWipePassword      08/04/99
//
//+----------------------------------------------------------------------------
BOOL EncryptPassword(IN LPCTSTR pszPassword, OUT LPTSTR pszEncryptedPassword, OUT LPDWORD lpdwBufSize, OUT LPDWORD lpdwCryptType)
{
    MYDBGASSERT(pszPassword);
    MYDBGASSERT(pszEncryptedPassword);
    MYDBGASSERT(lpdwBufSize);

    MYDBGASSERT(lpdwCryptType);
    DWORD dwEncryptedBufferLen;
    DWORD dwSize = 0;

    LPTSTR pszEncryptedData = NULL;

    TCHAR szSourceData[PWLEN + sizeof(TCHAR)];

    if ((NULL == pszPassword) || (NULL == pszEncryptedPassword) || (NULL == lpdwBufSize))
    { 
        return NULL;
    }

    //
    // Standard encryption, copy the password
    //

    lstrcpy(szSourceData, pszPassword);
   
    //
    // It is not safe to call InitSecure more than once
    //
    if (!g_fInitSecureCalled)
    {
        BOOL bFastEncryption = FALSE;
        MYVERIFY(FALSE != ReadEncryptionOption(&bFastEncryption));
        InitSecure(bFastEncryption);
        g_fInitSecureCalled = TRUE;
    }

    //
    // Encrypt the provided password
    //

    if (EncryptData((LPBYTE)szSourceData, (lstrlen(szSourceData)+1) * sizeof(TCHAR),
            (LPBYTE*)&pszEncryptedData, &dwEncryptedBufferLen, lpdwCryptType, NULL, NULL, NULL))
    {
        if (lpdwBufSize)
        {
            *lpdwBufSize = dwEncryptedBufferLen;
        }

        if (pszEncryptedData)
        {
            _tcscpy(pszEncryptedPassword, pszEncryptedData);
            HeapFree(GetProcessHeap(), 0, pszEncryptedData);
            pszEncryptedData = NULL;
            CmWipePassword(szSourceData);
            return TRUE;
        }        
    }
    
    CmWipePassword(szSourceData);
    return FALSE;
}

//+----------------------------------------------------------------------------
//
// Function:  ReadEncryptionOption
//
// Synopsis:  
//
// Arguments: BOOL* pfFastEncryption - boolean to fill in with fast encryption flag
//
// Returns:   BOOL - TRUE if successful
//
// History:   quintinb  Created Header    8/27/98
//            copied from the version written by Fengsun in cmdial\connect.cpp
//
//+----------------------------------------------------------------------------
BOOL ReadEncryptionOption(BOOL* pfFastEncryption)
{
    HKEY hKeyCm;
    DWORD dwType;
    DWORD dwSize = sizeof(DWORD);

    MYDBGASSERT(pfFastEncryption != NULL);

    *pfFastEncryption = FALSE;

	if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmRoot, 0, 
        KEY_QUERY_VALUE ,&hKeyCm))
    {
        RegQueryValueEx(hKeyCm, c_pszRegCmEncryptOption, NULL, &dwType, 
            (BYTE*)pfFastEncryption, &dwSize);

        RegCloseKey(hKeyCm);
    }
    return TRUE;
}