//*************************************************************
//
//  Personal Classes Profile management routines
//
//  Microsoft Confidential
//  Copyright (c) Microsoft Corporation 1995
//  All rights reserved
//
//*************************************************************

#include "uenv.h"
#include "windows.h"

// Local Data Structures

LPTSTR SpecialSubtrees[] = 
    {
    TEXT("CLSID"),
    TEXT("Interface"),
    TEXT("TypeLib"),
    TEXT("Licenses"),
    TEXT("FileType")
    };

#define MAX_SPECIAL_SUBTREE (sizeof(SpecialSubtrees)/sizeof(LPTSTR))
//
// Local function proto-types
//
typedef struct _RegKeyInfo {
	DWORD	SubKeyCount;
	DWORD	MaxSubKeyLen;
	DWORD	ValueCount;
	DWORD	MaxValueNameLen;
	DWORD	MaxValueLen;
	DWORD	SDLen;
	LPTSTR	pSubKeyName;
	LPTSTR	pValueName;
	LPTSTR	pValue;
    } REGKEYINFO, *PREGKEYINFO;

//*************************************************************
//
//  PrepForEnumRegistryTree()
//
//  Purpose:    prepare to duplicate a source bunch of keys into the destination.
//
//  Parameters: hkSourceTree   - source registry tree
//		pRegKeyInfo    - info block for use doing enumeration
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  History:    Date        Author     Comment
//              1/30/96     GregJen    Created
//
//*************************************************************
BOOL
PrepForEnumRegistryTree( 
    HKEY hkSourceTree, 
    PREGKEYINFO pRegKeyInfo
    )
{
    LPTSTR  pStringsBuffer;
    LONG    result;

    result = RegQueryInfoKey(hkSourceTree, 
			     NULL, 
			     NULL,
			     0,
			     &pRegKeyInfo->SubKeyCount,
			     &pRegKeyInfo->MaxSubKeyLen,
			     NULL,
			     &pRegKeyInfo->ValueCount,
			     &pRegKeyInfo->MaxValueNameLen,
			     &pRegKeyInfo->MaxValueLen,
			     &pRegKeyInfo->SDLen,
			     NULL);

    if ( result != ERROR_SUCCESS )
	return FALSE;

    // allocate a block of memory to use for enumerating subkeys and values
    pStringsBuffer = (LPTSTR) LocalAlloc( LPTR, 
					  (pRegKeyInfo->MaxSubKeyLen +
					      pRegKeyInfo->MaxValueNameLen +
					      pRegKeyInfo->MaxValueLen + 3)
					      * sizeof( TCHAR ) );
    if ( !pStringsBuffer )
	return FALSE;

    pRegKeyInfo->pSubKeyName	= pStringsBuffer;
    pRegKeyInfo->pValueName	= pStringsBuffer + pRegKeyInfo->MaxSubKeyLen + 1;
    pRegKeyInfo->pValue		= pRegKeyInfo->pValueName + 
					pRegKeyInfo->MaxValueNameLen + 1;

    return TRUE;
}

//*************************************************************
//
//  CleanupAfterEnumRegistryTree()
//
//  Purpose:    duplicate a source bunch of keys into the destination.
//
//  Parameters: hkSourceTree   - source registry tree
//		pRegKeyInfo    - info block for use doing enumeration
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  History:    Date        Author     Comment
//              1/30/96     GregJen    Created
//
//*************************************************************
void
CleanupAfterEnumRegistryTree(
    HKEY hkSourceTree, 
    PREGKEYINFO pRegKeyInfo)
{
    LocalFree( pRegKeyInfo->pSubKeyName );
}
BOOL
DeleteRegistrySubtree (
    HKEY    hkTree )
{
    HKEY hkCurrentSourceKey;
    DWORD idx;
    DWORD   NameLen;
    LONG    result  = ERROR_SUCCESS;
    REGKEYINFO RegKeyInfo;
    BOOL    Success = FALSE;

    if ( !PrepForEnumRegistryTree( hkTree, &RegKeyInfo ) )
	return FALSE;  // nothing to clean up here yet

    // enumerate all the source subkeys
    // for each: if dest subkey is older than source, delete it
    //		 if dest subkey does not exist (or was deleted) clone the subkey
    //
    // Clone all the subkeys
    //
    for ( idx = 0; 
          (result == ERROR_SUCCESS) && 
	      ( result != ERROR_MORE_DATA ) && 
	      ( idx < RegKeyInfo.SubKeyCount ); 
	  idx++ ) {
	NameLen = RegKeyInfo.MaxSubKeyLen + sizeof( TCHAR );
	result = RegEnumKeyEx( hkTree, 
			       idx,
			       RegKeyInfo.pSubKeyName,
			       &NameLen,
			       NULL,
			       NULL,
			       NULL,
			       NULL );
        
        if ( ( result != ERROR_SUCCESS ) && ( result != ERROR_MORE_DATA ) )
	    goto cleanup;

	// TBD: open the subkey in the source tree AS CurrentSourceKey
	result = RegOpenKeyEx(hkTree,
			      RegKeyInfo.pSubKeyName,
			      0,
			      KEY_ALL_ACCESS,
			      &hkCurrentSourceKey);

	DeleteRegistrySubtree( hkCurrentSourceKey );
	
	RegCloseKey( hkCurrentSourceKey );

	RegDeleteKey( hkTree, RegKeyInfo.pSubKeyName );
	
	result = ERROR_SUCCESS;
    }

cleanup:
    CleanupAfterEnumRegistryTree( hkTree, &RegKeyInfo );

    return TRUE;
}

//*************************************************************
//
//  CloneRegistryValues()
//
//  Purpose:    copy the values from under the source key to the dest key
//
//  Parameters: SourceTree   -  source registry tree
//              DestinationTree -  destintation registry tree
//              RegKeyInfo -  handy information from the RegEnumKeyEx call.
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  History:    Date        Author     Comment
//              1/14/96     GregJen    Created
//
//*************************************************************
BOOL
CloneRegistryValues( 
    HKEY hkSourceTree, 
    HKEY hkDestinationTree,
    REGKEYINFO RegKeyInfo )
{
    LONG    result  = ERROR_SUCCESS;
    DWORD   idx;
    DWORD   ValueLen;
    DWORD   ValueType;
    DWORD   DataLen;

    for ( idx = 0; 
          (result == ERROR_SUCCESS) && 
	      ( result != ERROR_MORE_DATA ) && 
	      ( idx < RegKeyInfo.ValueCount ); 
	  idx++ ) 
    {
	DataLen	    = RegKeyInfo.MaxValueLen + sizeof( TCHAR );
	ValueLen    = RegKeyInfo.MaxValueNameLen + sizeof( TCHAR );

        result = RegEnumValue( hkSourceTree,
			       idx,
			       RegKeyInfo.pValueName,
			       &ValueLen,
			       NULL,
			       &ValueType,
			       (BYTE*) RegKeyInfo.pValue,
			       &DataLen);

	// TBD: check errors

    	// now add the value to the destination key

	result = RegSetValueEx( hkDestinationTree,
				RegKeyInfo.pValueName,
				0,
				ValueType,
				(BYTE*) RegKeyInfo.pValue,
				DataLen );
	// TBD: check errors
    }
    return TRUE;
}
//*************************************************************
//
//  CloneRegistryTree()
//
//  Purpose:    duplicate a source bunch of keys into the destination.
//
//  Parameters: SourceTree   -  source registry tree
//              DestinationTree -  destintation registry tree
//              lpSubKeyName -  if present this is a subkey name that
//                              corresponds to the SourceTree HKEY.
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  History:    Date        Author     Comment
//              1/14/96     GregJen    Created
//
//*************************************************************
BOOL
CloneRegistryTree( 
    HKEY hkSourceTree, 
    HKEY hkDestinationTree, 
    LPTSTR lpDestTreeName )
{
    HKEY hkCurrentSourceKey;
    DWORD idx;
    DWORD   NameLen;
    LONG    result  = ERROR_SUCCESS;
    REGKEYINFO RegKeyInfo;
    BOOL    Success = FALSE;

    if ( !PrepForEnumRegistryTree( hkSourceTree, &RegKeyInfo ) )
	return FALSE;  // nothing to clean up here yet

    if ( lpDestTreeName ) {
	HKEY			hkNewKey = NULL;
	DWORD			dwSDLen = RegKeyInfo.SDLen;
	DWORD			dwDisp;
	SECURITY_INFORMATION	SI  = DACL_SECURITY_INFORMATION; // for now...
	PSECURITY_DESCRIPTOR	pSD = (PSECURITY_DESCRIPTOR)
					LocalAlloc( LPTR, dwSDLen );
	// TBD: check for NULL;

        // Get the registry security information from the old key
	result = RegGetKeySecurity( hkSourceTree,
				    SI,
				    pSD,
				    &dwSDLen);
	// TBD: check for errors, free pSD
        // create a key with the given name, and registry info
	result = RegCreateKeyEx( hkDestinationTree,
				 lpDestTreeName,
				 0,
				 NULL,
				 REG_OPTION_NON_VOLATILE,
				 KEY_ALL_ACCESS,
				 pSD,
				 &hkNewKey,
				 &dwDisp );
	// TBD: check for errors, free pSD

	// TBD: and update the hkDestinationTree variable to point to it
	
	hkDestinationTree = hkNewKey;
	LocalFree( pSD );
	if (ERROR_SUCCESS != result) 
	{
	   goto cleanup;
	}
    }
    
    //
    // clone the values
    //
    
    if ( ! CloneRegistryValues( hkSourceTree, hkDestinationTree, RegKeyInfo ) )
	goto cleanup;

    //
    // Clone all the subkeys
    //
    for ( idx = 0; 
          (result == ERROR_SUCCESS) && 
	      ( result != ERROR_MORE_DATA ) && 
	      ( idx < RegKeyInfo.SubKeyCount ); 
	  idx++ ) {
	NameLen = RegKeyInfo.MaxSubKeyLen + sizeof( TCHAR );
	result = RegEnumKeyEx( hkSourceTree, 
			       idx,
			       RegKeyInfo.pSubKeyName,
			       &NameLen,
			       NULL,
			       NULL,
			       NULL,
			       NULL );
        
        if ( ( result != ERROR_SUCCESS ) && ( result != ERROR_MORE_DATA ) )
	    goto cleanup;

	// TBD: open the subkey in the source tree AS CurrentSourceKey
	result = RegOpenKeyEx(hkSourceTree,
			      RegKeyInfo.pSubKeyName,
			      0,
			      KEY_READ,
			      &hkCurrentSourceKey);
	
	//
	// recurse passing the subkey name
	//
        CloneRegistryTree( hkCurrentSourceKey, 
	                   hkDestinationTree, 
			   RegKeyInfo.pSubKeyName );

	//
	// close our open key
	//

	RegCloseKey( hkCurrentSourceKey );
    }

    Success = TRUE;

cleanup:
    if ( lpDestTreeName ) 
    {
	RegCloseKey( hkDestinationTree );
    }
    
    CleanupAfterEnumRegistryTree( hkSourceTree, &RegKeyInfo );

    return Success;
}

void SaveChangesToUser( )
{
}

void SaveChangesToCommon( )
{
}

BOOL
AddSharedValuesToSubkeys( HKEY hkShared, LPTSTR pszSubtree )
{
    HKEY    hkSourceKey;
    HKEY    hkCurrentSourceKey;
    DWORD idx;
    DWORD   NameLen;
    LONG    result  = ERROR_SUCCESS;
    REGKEYINFO RegKeyInfo;
    BOOL    Success = FALSE;


    // for every subkey, set "Shared" value 
    result = RegOpenKeyEx( hkShared,
			   pszSubtree,
			   0,
			   KEY_READ,
			   &hkSourceKey );

    // TBD: if no subtree in source, skip ahead to next special subtree
    if ( result == ERROR_FILE_NOT_FOUND )
	return TRUE;
	
    // If any other errors occurred, return FALSE
    if ( result != ERROR_SUCCESS)
        return FALSE;

    if ( !PrepForEnumRegistryTree( hkSourceKey, &RegKeyInfo ) )
	goto cleanup2;

    // enumerate all the source subkeys
    // for each: if dest subkey is older than source, delete it
    //		 if dest subkey does not exist (or was deleted) clone the subkey
    //
    // Clone all the subkeys
    //
    for ( idx = 0; 
          (result == ERROR_SUCCESS) && 
	      ( result != ERROR_MORE_DATA ) && 
	      ( idx < RegKeyInfo.SubKeyCount ); 
	  idx++ ) 
    {
	NameLen = RegKeyInfo.MaxSubKeyLen + sizeof( TCHAR );
	result = RegEnumKeyEx( hkSourceKey, 
			       idx,
			       RegKeyInfo.pSubKeyName,
			       &NameLen,
			       NULL,
			       NULL,
			       NULL,
			       NULL );
        
        if ( ( result != ERROR_SUCCESS ) && ( result != ERROR_MORE_DATA ) )
	    goto cleanup;

	result = RegOpenKeyEx( hkSourceKey,
			       RegKeyInfo.pSubKeyName,
			       0,
			       KEY_ALL_ACCESS,
			       &hkCurrentSourceKey );
	// Bug 45994
	if (result != ERROR_SUCCESS )
	   goto cleanup;
	
	result = RegSetValueEx( hkCurrentSourceKey,
				L"Shared",
				0,
				REG_SZ,
				(LPBYTE) L"Y",
				sizeof( L"Y" ) );

	RegCloseKey( hkCurrentSourceKey );
    
    }

    Success = TRUE;

cleanup:
    CleanupAfterEnumRegistryTree( hkSourceKey, &RegKeyInfo );

cleanup2:
    RegCloseKey( hkSourceKey );

    return Success;
    
}

BOOL
AddSharedValues( HKEY hkShared )
{
    // for each of the special subtrees, add "Shared" values
    int		idx;

    // now, for each of the special top-level keys, process the level below them
    // these keys are: CLSID, Interface, TypeLib, Licenses, FileType

    for ( idx = 0; idx < MAX_SPECIAL_SUBTREE; idx++ )
    {
	AddSharedValuesToSubkeys( hkShared, SpecialSubtrees[idx] );
    }

    // now do all the top level keys (file extensions and progids)
    AddSharedValuesToSubkeys( hkShared, NULL  );

    return TRUE;
}


//*************************************************************
//
//  MergeUserClasses()
//
//  Purpose:    Merges the user's class information with the
//              common class information.
//
//  Parameters: UserClassStore   -  Per-user class information
//              CommonClassStore -  Machine-wide class information
//              MergedClassStore -  Destination for merged information.
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:   UserClassStore may be a null HKEY, implying
//              just copy the information from the common
//              portion into the merged portion
//
//  History:    Date        Author     Comment
//              1/14/96     GregJen    Created
//
//*************************************************************
BOOL
MergeRegistrySubKeys (
    HKEY    hkSourceTree,
    HKEY    hkDestTree )
{
    HKEY hkCurrentSourceKey = NULL;
    HKEY hkCurrentDestKey = NULL;
    DWORD idx;
    DWORD   NameLen;
    LONG    result  = ERROR_SUCCESS;
    REGKEYINFO RegKeyInfo;
    BOOL    Success = FALSE;
    FILETIME	SourceFileTime;
    FILETIME	DestFileTime;
	LONG cmp;

    if ( !PrepForEnumRegistryTree( hkSourceTree, &RegKeyInfo ) )
	return FALSE;  // nothing to clean up here yet

    // enumerate all the source subkeys
    // for each: if dest subkey is older than source, delete it
    //		 if dest subkey does not exist (or was deleted) clone the subkey
    //
    // Clone all the subkeys
    //
    for ( idx = 0; 
          (result == ERROR_SUCCESS) && 
	      ( result != ERROR_MORE_DATA ) && 
	      ( idx < RegKeyInfo.SubKeyCount ); 
	  idx++ ) {
	NameLen = RegKeyInfo.MaxSubKeyLen + sizeof( TCHAR );
	result = RegEnumKeyEx( hkSourceTree, 
			       idx,
			       RegKeyInfo.pSubKeyName,
			       &NameLen,
			       NULL,
			       NULL,
			       NULL,
			       &SourceFileTime );
        
        if ( ( result != ERROR_SUCCESS ) && ( result != ERROR_MORE_DATA ) )
	    goto cleanup;

	// TBD: open the subkey in the source tree AS CurrentSourceKey
	result = RegOpenKeyEx(hkSourceTree,
			      RegKeyInfo.pSubKeyName,
			      0,
			      KEY_READ,
			      &hkCurrentSourceKey);
	if (result != ERROR_SUCCESS)
		goto cleanup;
	
	result = RegOpenKeyEx(hkDestTree,
			      RegKeyInfo.pSubKeyName,
			      0,
			      KEY_READ,
			      &hkCurrentDestKey);

	// if current dest key does not exist,  
	if ( result == ERROR_FILE_NOT_FOUND )
	{
	    //
	    // recurse passing the subkey name
	    //
	    CloneRegistryTree( hkCurrentSourceKey, 
			       hkDestTree, 
			       RegKeyInfo.pSubKeyName );
	}
	// if current dest key is older than current source key, delete dest
	// then recreate new
	else if (result == ERROR_SUCCESS)
	{
	    RegQueryInfoKey( hkCurrentDestKey,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     &DestFileTime );


	    cmp = CompareFileTime( &SourceFileTime, &DestFileTime );
	    if ( cmp > 0 )
	    {
		// delete dest
		//
		DeleteRegistrySubtree( hkCurrentDestKey );

		//
		// recurse passing the subkey name
		//
		CloneRegistryTree( hkCurrentSourceKey, 
				   hkDestTree, 
				   RegKeyInfo.pSubKeyName );
	    }
		
	    RegCloseKey(hkCurrentDestKey);
	}

	//
	// close our open key
	//

	RegCloseKey( hkCurrentSourceKey );

	result = ERROR_SUCCESS;
    }

cleanup:
    CleanupAfterEnumRegistryTree( hkSourceTree, &RegKeyInfo );

    return TRUE;
}




//*************************************************************
//
//  MergeUserClasses()
//
//  Purpose:    Merges the user's class information with the
//              common class information.
//
//  Parameters: UserClassStore   -  Per-user class information
//              CommonClassStore -  Machine-wide class information
//              MergedClassStore -  Destination for merged information.
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:   UserClassStore may be a null HKEY, implying
//              just copy the information from the common
//              portion into the merged portion
//
//  History:    Date        Author     Comment
//              1/14/96     GregJen    Created
//
//*************************************************************
BOOL
MergeRegistrySubtree (
    HKEY    hkSourceParent,
    HKEY    hkDestParent,
    LPTSTR  pszSubtree )
{
    HKEY    hkCurrentSourceKey = NULL;
    HKEY    hkCurrentDestKey = NULL;
    LONG    result;
    DWORD   dummy   = 0;

    // open the special subtree in the source tree
    result = RegOpenKeyEx( hkSourceParent,
                           pszSubtree,
                           0,
                           KEY_READ,
                           &hkCurrentSourceKey );

    // TBD: if no subtree in source, skip ahead to next special subtree
    if ( result == ERROR_FILE_NOT_FOUND )
        return TRUE;

    // If any other errors occurred, return FALSE
    if ( result != ERROR_SUCCESS)
        return FALSE;

    result = RegOpenKeyEx( hkDestParent,
                           pszSubtree,
                           0,
                           KEY_ALL_ACCESS,
                           &hkCurrentDestKey );
    // TBD: if no such subtree in dest, do CloneRegistry etc
    if ( result == ERROR_FILE_NOT_FOUND )
    {
        //
        // recurse passing the subkey name
        //
        CloneRegistryTree( hkCurrentSourceKey, 
                   hkDestParent, 
                   pszSubtree );
    }
    // TBD:if timestamp on source is newer than timestamp on dest, 
    //     delete dest and recreate??
    
    // Bug 45995
    if (hkCurrentDestKey) 
    {
        MergeRegistrySubKeys( hkCurrentSourceKey,
                              hkCurrentDestKey );

        // make sure the timestamp on the special trees is updated
        result = RegSetValueEx( hkCurrentDestKey,
                                TEXT("Updated"),
                                0,
                                REG_DWORD,
                                (BYTE*) &dummy,
                                sizeof( DWORD ) );
        RegCloseKey( hkCurrentDestKey );
    }


    // close special subtrees
    RegCloseKey( hkCurrentSourceKey );

    return TRUE;
}

long 
CompareRegistryTimes(
    HKEY hkLHS,
    HKEY hkRHS )
{
    FILETIME	LHSTime;
    FILETIME	RHSTime;

    	    RegQueryInfoKey( hkLHS,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     &LHSTime );

    	    RegQueryInfoKey( hkRHS,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     &RHSTime );

    return CompareFileTime( &LHSTime, &RHSTime );
}

//*************************************************************
//
//  MergeUserClasses()
//
//  Purpose:    Merges the user's class information with the
//              common class information.
//
//  Parameters: UserClassStore   -  Per-user class information
//              CommonClassStore -  Machine-wide class information
//              MergedClassStore -  Destination for merged information.
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:   UserClassStore may be a null HKEY, implying
//              just copy the information from the common
//              portion into the merged portion
//
//  History:    Date        Author     Comment
//              1/14/96     GregJen    Created
//
//*************************************************************
BOOL
MergeUserClasses( 
    HKEY UserClassStore, 
    HKEY CommonClassStore,
    HKEY MergedClassStore,
    BOOL ForceNew )
{
    BOOL	fNotCorrectUser = FALSE;
    HKEY	hkOverridingSubtree = CommonClassStore;
    HKEY	hkMergingSubtree    = UserClassStore;
    int		idx;

    //TBD: check time stamps on source and destination
    // if same user, and timestamps are in sync, do nothing

    // if destination does not belong to the current user, then
    // delete everything under it
 
    if ( fNotCorrectUser ) {
        DeleteRegistrySubtree( MergedClassStore );
    }

    
    if ( !ForceNew &&
	 ( CompareRegistryTimes( MergedClassStore, CommonClassStore ) > 0 ) &&
	 ( CompareRegistryTimes( MergedClassStore, UserClassStore ) > 0 ) )
    {
	return TRUE;
    }

    // TBD: copy everything from the overriding store into the
    // destination store
    // At this moment, the common store overrides the user store;
    // this will eventually reverse.

    CloneRegistryTree( hkOverridingSubtree, MergedClassStore, NULL );

    // now, for each of the special top-level keys, process the level below them
    // these keys are: CLSID, Interface, TypeLib, Licenses, FileType

    for ( idx = 0; idx < MAX_SPECIAL_SUBTREE; idx++ )
    {
	MergeRegistrySubtree( hkMergingSubtree, 
			      MergedClassStore, 
			      SpecialSubtrees[idx] );
    }

    // now do all the top level keys (file extensions and progids)
    // TBD: MergeRegistrySubtree( UserClassStore, MergedClassStore );
    MergeRegistrySubtree( hkMergingSubtree, 
			  MergedClassStore, 
			  NULL  );

return TRUE;
}