/*++

Microsoft Windows
Copyright (C) Microsoft Corporation, 1981 - 2000

Module Name:

	migrate.cpp

Abstract:

	This module contains helper functions for migrating the installer
	binaries during the upgrade of one NT based OS to another. This is required
	if the OS to which we are upgrading has older versions of the binary
	compared to what we already have. e.g. NT4.0 did not come with the Windows
	installer technology but Win2K had installer version 1.1. If someone uses
	our redistributable to install a higher version of the installer on an NT 4
	box, then we need to make sure that if the NT4 box is upgraded to Win2K then
	our bits stay on and don't get overwritten by the older bits from Win2K. 
	We do this by taking advantage of the exception pack mechanism provided in the 
	setup infrastructure.


Author:

	Rahul Thombre (RahulTh)	2/28/2001

Revision History:

	2/28/2001	RahulTh			Created this module.
	2/28/2001	RahulTh			Added NT4 -> Win2K migration.

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <objbase.h>
extern "C" 
{
#include <excppkg.h>
}
#include "debug.h"
#include "utils.h"
#include "migrate.h"
//
// Also include the header file automatically generated by the perl script.
// This header file contains an array of strings with the names of the system
// protected files on Win2K and higher (present in msi.inf)
//
#include <prot.h>

//
// Function declarations for helper functions.
//
DWORD SetupUpgradeMigration (
	IN const PSETUP_OS_COMPONENT_DATA pCD,
	IN const PSETUP_OS_EXCEPTION_DATA pED
);

//+--------------------------------------------------------------------------
//
//  Function:	HandleNT4Upgrades
//
//  Synopsis:	Make sure that our new bits are not blown away when an NT4
//				machine is upgraded to an OS which contains older bits.
//
//  Arguments:	none.
//
//  Returns:	ERROR_SUCCESS if successful.
//				a Win32 error code otherwise.
//
//  History:	3/6/2001  RahulTh  created
//
//  Notes:
//
//---------------------------------------------------------------------------
DWORD HandleNT4Upgrades (void)
{
#ifndef UNICODE
	return ERROR_CALL_NOT_IMPLEMENTED;
#else	// UNICODE
	SETUP_OS_COMPONENT_DATA ComponentData;
	SETUP_OS_EXCEPTION_DATA ExceptionData;
	int						lenTempPath;
	GUID					GUIDExcpWinXP;
	
	if (g_fWin9X)
		return CO_E_WRONGOSFORAPP;
	
	// Nothing to do for Win2K and above.
	if (5 <= g_osviVersion.dwMajorVersion)
		return ERROR_SUCCESS;
	
	// We must be on NT4 if we are here.
	lenTempPath = lstrlen (g_szTempStore);
	if (MAX_PATH <= (lenTempPath + sizeof(szInfWinXP)/sizeof(TCHAR)) ||
		MAX_PATH <= (lenTempPath + sizeof(szCatWinXP)/sizeof(TCHAR))
		)
	{
		//
		// Note: the second part of the addition expression actually is one more
		// than the length of the string since it includes the null terminator too.
		// However, we do not subtract 1 because we need that extra character for
		// the \ separator when we generate the path.
		// 
		DebugMsg ((TEXT("Path to the inf and cat files is longer than %d characters."), MAX_PATH));
		return ERROR_OUTOFMEMORY;
	}
	
	if (FAILED(IIDFromString (szGUIDExcpWinXP, &GUIDExcpWinXP)))
	{
		DebugMsg ((TEXT("Could not convert string to GUID for the exception package.")));
		return ERROR_OUTOFMEMORY;	// we just return this error code rather than trying to convert the HRESULT
	}
	
	// Populate the component data structure
	memset(&ComponentData, 0, sizeof (SETUP_OS_COMPONENT_DATA));
	ComponentData.SizeOfStruct = sizeof (SETUP_OS_COMPONENT_DATA);
	memcpy (&ComponentData.ComponentGuid, &GUIDExcpWinXP, sizeof (GUID));
	lstrcpy (ComponentData.FriendlyName, FRIENDLY_NAME);
	ComponentData.VersionMajor = VerMajorWinXP;
	ComponentData.VersionMinor = VerMinorWinXP;
	
	
	// Populate the exception data structure
	memset(&ExceptionData, 0, sizeof (SETUP_OS_EXCEPTION_DATA));
	ExceptionData.SizeOfStruct = sizeof (SETUP_OS_EXCEPTION_DATA);
	// Set up the path to the exception inf.
	lstrcpy (ExceptionData.ExceptionInfName, g_szTempStore);
	ExceptionData.ExceptionInfName[lenTempPath] = TEXT('\\');
	lstrcpy (&ExceptionData.ExceptionInfName[lenTempPath+1], szInfWinXP);
	// Setup the path to the catalog file.
	lstrcpy (ExceptionData.CatalogFileName, g_szTempStore);
	ExceptionData.CatalogFileName[lenTempPath] = TEXT('\\');
	lstrcpy (&ExceptionData.CatalogFileName[lenTempPath+1], szCatWinXP);
	
	return SetupUpgradeMigration (&ComponentData, &ExceptionData);
	
#endif 	// UNICODE
}

//+--------------------------------------------------------------------------
//
//  Function:	SetupUpgradeMigration
//
//  Synopsis:	Registers the exception package with the OS so that the bits
//				are not replaced by older bits during an upgrade from one
//				downlevel platform to another.
//
//  Arguments:  [in] pCD : pointer to OS_COMPONENT_DATA structure.
//				[in] pED : pointer to OS_EXCEPTION_DATA structure.
//
//  Returns:	ERROR_SUCCESS if succesful.
//				a Win32 error code otherwise.
//
//  History:	3/6/2001  RahulTh  created
//
//  Notes:		This function only handles OS migration for unicode platforms.
//				It is only a helper function and does not do any parameter
//				validation. That is the responsibility of the caller.
//
//---------------------------------------------------------------------------
DWORD SetupUpgradeMigration (
	IN const PSETUP_OS_COMPONENT_DATA pCD,
	IN const PSETUP_OS_EXCEPTION_DATA pED
)
{
#ifndef UNICODE
	return ERROR_CALL_NOT_IMPLEMENTED;
#else	// UNICODE
    SETUP_OS_COMPONENT_DATA ComponentData;
    SETUP_OS_EXCEPTION_DATA ExceptionData;
	DWORD					Status = ERROR_SUCCESS;
	DWORD					VersionToInstall;
	DWORD					RegisteredVersion;
	
	// Don't run this function on Win9X
	if (g_fWin9X)
		return CO_E_WRONGOSFORAPP;
	
    //
    // Unregister any previously registered versions.
    //
    ComponentData.SizeOfStruct = sizeof(SETUP_OS_COMPONENT_DATA);
    ExceptionData.SizeOfStruct = sizeof(SETUP_OS_EXCEPTION_DATA);
    if (SetupQueryRegisteredOsComponent(
                                &(pCD->ComponentGuid),
                                &ComponentData,
                                &ExceptionData)
		) 
	{
        //
        // Unregister any packages that are superceded by my package
        //
        RegisteredVersion = MAKELONG( 
                                ComponentData.VersionMajor, 
                                ComponentData.VersionMinor );
		VersionToInstall = MAKELONG(
								pCD->VersionMajor,
								pCD->VersionMinor);

        if (RegisteredVersion <= VersionToInstall) 
		{
			if (!SetupUnRegisterOsComponent(&(pCD->ComponentGuid))) 
			{
				Status = GetLastError();
				DebugMsg((TEXT("Failed to unregister previously registered exception package. Error: %d"), Status));
				return Status;
			}        
		}
		else
		{
			DebugMsg ((TEXT("An exception package with a higher version (%d.%d) has already been registered."), ComponentData.VersionMajor, ComponentData.VersionMinor));
			return ERROR_ALREADY_EXISTS;
		}
    }
        
    //
    // Register the package.
    //
    if (!SetupRegisterOsComponent(pCD, pED)) 
	{
		Status = GetLastError();
		DebugMsg((TEXT("Failed to register exception package. Error: %d."), Status));
		return Status;
    }
	
	DebugMsg ((TEXT("Successfully registered the exception package.")));
	return ERROR_SUCCESS;
	
#endif  // UNICODE
}

//+--------------------------------------------------------------------------
//
//  Function:	IsExcpInfoFile
//
//  Synopsis:	Determines if the name of the file matches that of any inf
//				or catalog file that might get registered as an exception inf
//				on the system.
//
//  Arguments:	[in] szFileName : name of the file.
//
//  Returns:	TRUE : if it is.
//				FALSE : otherwise.
//
//  History:	3/8/2001  RahulTh  created
//
//  Notes:
//
//---------------------------------------------------------------------------
BOOL IsExcpInfoFile (IN LPCTSTR szFileName)
{
#ifndef UNICODE
	return FALSE;	// Exception infs cannot not be registered using ansi bits.
#else // UNICODE
	int i;
	
	if (g_fWin9X)
		return FALSE;	// Exception infs cannot be registered on Win9X
	
	if (!szFileName || TEXT('\0') == *szFileName)
		return FALSE;
	
	//
	// If we are on NT4 and this is the exception inf or cat file for the
	// WindowsXP bits, return TRUE
	//
	if (g_osviVersion.dwMajorVersion < 5)
	{
		if (0 == lstrcmpi (szFileName, szInfWinXP) ||
			 0 == lstrcmpi (szFileName, szCatWinXP)
			)
		{
			return TRUE;
		}
		//
		// Make sure it is not one of the binaries mentioned in the inf file.
		//
		for (i=0; pszProtectedFileList[i][0]; i++)
		{
			if (0 == lstrcmpi(szFileName, pszProtectedFileList[i]))
				return TRUE;
		}
	}
	
	
	return FALSE;

#endif // UNICODE
}

//+--------------------------------------------------------------------------
//
//  Function:	PurgeNT4MigrationFiles
//
//  Synopsis:	Deletes any inf and cat files that might be used for registering
//				the exception inf for handling upgrades from NT4. Also deletes
//				any protected files listed in the inf.
//
//  Arguments:	none
//
//  Returns:	ERROR_SUCCESS if successful.
//				a win32 error code otherwise.
//
//  History:	3/8/2001  RahulTh  created
//
//  Notes:		We just make our best attempt at cleaning up those files.
//				if we fail. Too bad. 
//
//				The reason these have to be purged independently is because
//				CopyFileTree does not queue up these files for deletion
//				upon reboot.
//
//---------------------------------------------------------------------------
DWORD PurgeNT4MigrationFiles (void)
{
#ifndef UNICODE
	return ERROR_SUCCESS;
#else // UNICODE

	TCHAR	szPath[MAX_PATH];
	int		lenTempStore;
	TCHAR * szPtr;
	DWORD	Status = ERROR_SUCCESS;
	int		i;

	if (g_fWin9X)
		return ERROR_SUCCESS;
	
	if (g_osviVersion.dwMajorVersion < 5)
	{
		lenTempStore = lstrlen (g_szTempStore);
		if ((MAX_PATH <= lenTempStore + sizeof(szInfWinXP)/sizeof(TCHAR)) ||
			(MAX_PATH <= lenTempStore + sizeof(szCatWinXP)/sizeof(TCHAR))
			)
		{
			return ERROR_OUTOFMEMORY;
		}
		
		lstrcpy (szPath, g_szTempStore);
		szPath[lenTempStore] = TEXT('\\');
		szPtr = szPath + lenTempStore + 1;
		
		lstrcpy (szPtr, szInfWinXP);
		if (! DeleteFile (szPath))
		{
			Status = GetLastError();
		}
		
		lstrcpy (szPtr, szCatWinXP);
		if (! DeleteFile (szPath))
		{
			Status = GetLastError();
		}
		
		//
		// Also delete the protected files listed in the inf
		// Note: These will not be in use because the only time we purge
		// this files is when we failed to update darwin. Also note that
		// currently we are doing this only for NT4. On Win2K, we just use
		// MoveFileEx in the CopyFileTree.
		//
		for (i = 0; pszProtectedFileList[i][0]; i++)
		{
			lstrcpy (szPtr, pszProtectedFileList[i]);
			if (! DeleteFile (szPath))
			{
				Status = GetLastError();
			}
		}
	}
	
	return Status;
	
#endif
}

