/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    renregfn.c

Abstract:

    Implements code-based registry rename.

Author:

    Jim Schmidt (jimschm) 15-Sep-2000

Revision History:

    <alias> <date> <comments>

--*/

//
// Includes
//

#include "pch.h"
#include "v1p.h"

#define DBG_RENREGFN    "RenRegFn"

//
// Strings
//

// None

//
// Constants
//

// None

//
// Macros
//

// None

//
// Types
//

typedef VOID(RENREGFNINIT)(MIG_PLATFORMTYPEID);
typedef RENREGFNINIT *PRENREGFNINIT;

typedef BOOL (STDMETHODCALLTYPE RENAMERULE)(
    IN      PCTSTR OldNode,
    IN      PCTSTR NewNode,
    IN      PCTSTR Leaf,
    IN      BOOL NoRestoreObject,
    OUT     PMIG_FILTEROUTPUT OutputData
    );
typedef RENAMERULE FAR *LPRENAMERULE;

//
// Globals
//

PTSTR g_DestIdentityGUID = NULL;
BOOL g_OERulesMigrated = FALSE;

//
// Macro expansion list
//

//
// DEFMAC(<script tag>, <enum callback>, <operation name>, <op init>, <operation callback>)
//
// It is assumed that <operation callback> is a tree filter (it can modify part of a path).
// The code does not currently support the contrary.
//

#define DEFAULT_ENUM        pDefaultRenRegFnQueueCallback
#define DEFAULT_INIT        pNulInit

#define RENAME_FUNCTIONS        \
    DEFMAC(ConvertOE4,          DEFAULT_ENUM, MOVE.ConvertOE4,          pConvertOE4Init,    pConvertOE4Move    ) \
    DEFMAC(ConvertOE4IAM,       DEFAULT_ENUM, MOVE.ConvertOE4IAM,       pConvertOE4Init,    pConvertOEIAMMove  ) \
    DEFMAC(ConvertOE5IAM,       DEFAULT_ENUM, MOVE.ConvertOE5IAM,       pConvertOE5IAMInit, pConvertOEIAMMove  ) \
    DEFMAC(ConvertOE5IdIAM,     DEFAULT_ENUM, MOVE.ConvertOE5IdIAM,     DEFAULT_INIT,       pConvertOEIdIAMMove) \
    DEFMAC(ConvertOE5MailRules, DEFAULT_ENUM, MOVE.ConvertOE5MailRules, DEFAULT_INIT,       pConvertOE5MailRulesMove) \
    DEFMAC(ConvertOE5NewsRules, DEFAULT_ENUM, MOVE.ConvertOE5NewsRules, DEFAULT_INIT,       pConvertOE5NewsRulesMove) \
    DEFMAC(ConvertOE5Block,     DEFAULT_ENUM, MOVE.ConvertOE5Block,     DEFAULT_INIT,       pConvertOE5BlockMove) \

//
// Private function prototypes
//

// None

//
// Macro expansion definition
//

//
// Declare special rename operation apply callback functions
//
#define DEFMAC(ifn,ec,opn,opi,opc) SGMENUMERATIONCALLBACK ec; RENREGFNINIT opi; OPMFILTERCALLBACK opc;
RENAME_FUNCTIONS
#undef DEFMAC

//
// This is the structure used for handling action functions
//
typedef struct {
    PCTSTR InfFunctionName;
    PSGMENUMERATIONCALLBACK EnumerationCallback;
    PCTSTR OperationName;
    MIG_OPERATIONID OperationId;
    PRENREGFNINIT OperationInit;
    POPMFILTERCALLBACK OperationCallback;
} RENAME_STRUCT, *PRENAME_STRUCT;

//
// Declare a global array of rename functions
//
#define DEFMAC(ifn,ec,opn,opi,opc) {TEXT("\\")TEXT(#ifn),ec,TEXT(#opn),0,opi,opc},
static RENAME_STRUCT g_RenameFunctions[] = {
                              RENAME_FUNCTIONS
                              {NULL, NULL, NULL, 0, NULL, NULL}
                              };
#undef DEFMAC

//
// Code
//

VOID
pNulInit (
    IN      MIG_PLATFORMTYPEID Platform
    )
{
}

UINT
pDefaultRenRegFnQueueCallback (
    IN      PCMIG_OBJECTENUMDATA Data,
    IN      ULONG_PTR CallerArg
    )
{
    PRENAME_STRUCT p = (PRENAME_STRUCT)CallerArg;

    IsmSetOperationOnObject (Data->ObjectTypeId, Data->ObjectName, p->OperationId, NULL, NULL);

    return CALLBACK_ENUM_CONTINUE;
}

PRENAME_STRUCT
pGetRenameStruct (
    IN      PCTSTR FunctionName
    )
{
    PRENAME_STRUCT p = g_RenameFunctions;
    INT i = 0;
    while (p->InfFunctionName != NULL) {
        if (StringIMatch (p->InfFunctionName, FunctionName)) {
            return p;
        }
        p++;
        i++;
    }
    return NULL;
}

VOID
InitSpecialRename (
    IN      MIG_PLATFORMTYPEID Platform
    )
{
    PRENAME_STRUCT p = g_RenameFunctions;

    while (p->InfFunctionName) {
        p->OperationId = IsmRegisterOperation (p->OperationName, FALSE);
        if (Platform == PLATFORM_DESTINATION) {
            IsmRegisterOperationFilterCallback (p->OperationId, p->OperationCallback, TRUE, TRUE, FALSE);
        }

        p->OperationInit(Platform);
        p++;
    }
}

VOID
TerminateSpecialRename (
    VOID
    )
{
    if (g_DestIdentityGUID) {
        FreeText (g_DestIdentityGUID);
    }
}

BOOL
AddSpecialRenameRule (
    IN      PCTSTR Pattern,
    IN      PCTSTR Function
)
{
    PRENAME_STRUCT functionStruct = NULL;
    BOOL result = FALSE;

    functionStruct = pGetRenameStruct (Function);

    if (functionStruct) {
        result = IsmHookEnumeration (
            g_RegType,
            Pattern,
            functionStruct->EnumerationCallback?
                functionStruct->EnumerationCallback:
                pDefaultRenRegFnQueueCallback,
            (ULONG_PTR)functionStruct,
            functionStruct->InfFunctionName
            );
    } else {
        LOG ((
            LOG_ERROR,
            (PCSTR) MSG_DATA_RENAME_BAD_FN,
            Function,
            Pattern
            ));
    }

    return result;
}

BOOL
pProcessDataRenameSection (
    IN      PINFSTRUCT InfStruct,
    IN      HINF InfHandle,
    IN      PCTSTR Section
    )
{
    PCTSTR pattern;
    ENCODEDSTRHANDLE encodedPattern = NULL;
    PCTSTR functionName;
    BOOL result = FALSE;

    __try {
        if (InfFindFirstLine (InfHandle, Section, NULL, InfStruct)) {
            do {

                if (IsmCheckCancel()) {
                    __leave;
                }

                pattern = InfGetStringField (InfStruct, 0);

                if (!pattern) {
                    continue;
                }
                encodedPattern = TurnRegStringIntoHandle (pattern, TRUE, NULL);

                functionName = InfGetStringField (InfStruct, 1);

                if (functionName) {
                    AddSpecialRenameRule(encodedPattern, functionName);
                } else {
                    LOG ((LOG_ERROR, (PCSTR) MSG_DATA_RENAME_NO_FN, pattern));
                }

                IsmDestroyObjectHandle (encodedPattern);
                encodedPattern = NULL;
            } while (InfFindNextLine (InfStruct));
        }

        result = TRUE;
    }
    __finally {
        InfCleanUpInfStruct (InfStruct);
    }

    return result;
}

BOOL
DoRegistrySpecialRename (
    IN      HINF InfHandle,
    IN      PCTSTR Section
    )
{
    PCTSTR osSpecificSection;
    BOOL b;
    INFSTRUCT is = INITINFSTRUCT_PMHANDLE;

    b = pProcessDataRenameSection (&is, InfHandle, Section);

    if (b) {
        osSpecificSection = GetMostSpecificSection (&is, InfHandle, Section);

        if (osSpecificSection) {
            b = pProcessDataRenameSection (&is, InfHandle, osSpecificSection);
            FreeText (osSpecificSection);
        }
    }

    InfCleanUpInfStruct (&is);
    return b;
}


//
// Helpers below
//

VOID
pConvertOE4Init (
    IN      MIG_PLATFORMTYPEID Platform
    )
{
    if (Platform == PLATFORM_DESTINATION &&
        IsmGetRealPlatform() == PLATFORM_DESTINATION &&
        IsmIsComponentSelected (S_OE_COMPONENT, 0) &&
        IsmIsEnvironmentFlagSet (PLATFORM_SOURCE, NULL, S_OE4_APPDETECT))
    {

        if (g_DestIdentityGUID != NULL) {
            // Already got it.. punt
            return;
        }

        // pull out the GUID from dest
        g_DestIdentityGUID = OEGetDefaultId (PLATFORM_DESTINATION);

        if (g_DestIdentityGUID == NULL)
        {
            // This is when we created a new user
            g_DestIdentityGUID = OECreateFirstIdentity();
        } else {
            // This is when applying to a user who never ran OE
            OEInitializeIdentity();
        }
    }
}

BOOL
WINAPI
pConvertOE4Move (
    IN      PCMIG_FILTERINPUT InputData,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL NoRestoreObject,
    IN      PCMIG_BLOB SourceOperationData,             OPTIONAL
    IN      PCMIG_BLOB DestinationOperationData         OPTIONAL
    )
{
    PCTSTR srcNode = NULL;
    PCTSTR srcLeaf = NULL;
    PTSTR newNode = NULL;
    PTSTR ptr = NULL;
    MIG_OBJECTSTRINGHANDLE newName;

    if (g_DestIdentityGUID == NULL) {
        return FALSE;
    }

    IsmCreateObjectStringsFromHandle (InputData->CurrentObject.ObjectName, &srcNode, &srcLeaf);

    // srcNode should be "HKCU\Software\Microsoft\Outlook Express\..."

    if (!srcNode) {
        return FALSE;
    }

    ptr = (PTSTR)_tcsistr (srcNode, TEXT("\\Outlook Express"));
    if (!ptr) {
        return FALSE;
    }
    ptr += 16;

    newNode = AllocText (CharCount (srcNode) + CharCount (g_DestIdentityGUID) + 17);
    StringCopy (newNode, TEXT("HKCU\\Identities\\"));                          // +12
    StringCat (newNode, g_DestIdentityGUID);
    StringCat (newNode, TEXT("\\Software\\Microsoft\\Outlook Express\\5.0"));  // +4
    StringCat (newNode, ptr);

    // newNode should be "HKCU\Identities\{GUID}\Software\Microsoft\Outlook Express\5.0\..."

    newName = IsmCreateObjectHandle (newNode, srcLeaf);
    FreeText (newNode);
    IsmDestroyObjectString (srcLeaf);
    IsmDestroyObjectString (srcNode);

    OutputData->NewObject.ObjectName = newName;

    return TRUE;
}

VOID
pConvertOE5IAMInit (
    IN      MIG_PLATFORMTYPEID Platform
    )
{
    BOOL remap = TRUE;
    PTSTR srcAssocId;
    PTSTR newIdentity;
    PTSTR srcDefaultId;
    PTSTR destDefaultId;

    if (Platform == PLATFORM_DESTINATION &&
        IsmGetRealPlatform() == PLATFORM_DESTINATION &&
        IsmIsComponentSelected (S_OE_COMPONENT, 0) &&
        IsmIsEnvironmentFlagSet (PLATFORM_SOURCE, NULL, S_OE5_APPDETECT))
    {
        // g_DestIdentityGUID should remain NULL if we do not want to remap the IAM tree
        // This is true when the destination user profile has not been created yet.  We also
        // want to leave it alone when the IAM has not yet been initialized (assume that [AssociatedID]
        // has not yet been written.. if this is not a valid assumption, compare to source's AssociatedID)

        if (g_DestIdentityGUID != NULL) {
            // Already got it.. punt
            return;
        }

        srcAssocId = OEGetAssociatedId (PLATFORM_SOURCE);
        if (srcAssocId) {
            newIdentity = OEGetRemappedId (srcAssocId);
            if (newIdentity) {
               if (OEIsIdentityAssociated(newIdentity)) {
                   FreeText(newIdentity);
               } else {
                   g_DestIdentityGUID = newIdentity;
               }
            }
            FreeText(srcAssocId);
        }

        OEInitializeIdentity();
    }
}

BOOL
WINAPI
pConvertOE5IAMMove (
    IN      PCMIG_FILTERINPUT InputData,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL NoRestoreObject,
    IN      PCMIG_BLOB SourceOperationData,             OPTIONAL
    IN      PCMIG_BLOB DestinationOperationData         OPTIONAL
    )
{
    PCTSTR srcNode = NULL;
    PCTSTR srcLeaf = NULL;
    PTSTR newNode = NULL;
    PTSTR ptr = NULL;
    BOOL retval = FALSE;

    if (g_DestIdentityGUID == NULL ||
        OEIsIdentityAssociated (g_DestIdentityGUID)) {
        // Do Nothing
        return TRUE;
    }

    IsmCreateObjectStringsFromHandle (InputData->CurrentObject.ObjectName, &srcNode, &srcLeaf);
    if (srcNode) {
        // srcNode should be "HKCU\Software\Microsoft\Internet Account Manager\..."

        ptr = _tcschr (srcNode, TEXT('\\'));
        if (ptr) {

            newNode = AllocText (CharCount (srcNode) + CharCount (g_DestIdentityGUID) + 13);
            StringCopy (newNode, TEXT("HKCU\\Identities\\"));    // +12
            StringCat (newNode, g_DestIdentityGUID);
            StringCat (newNode, ptr);

            // newNode should be "HKCU\Identities\{GUID}\Software\Microsoft\Internet Account Manager\..."

            OutputData->NewObject.ObjectName = IsmCreateObjectHandle (newNode, srcLeaf);
            FreeText (newNode);
            retval = TRUE;
        }
    }

    IsmDestroyObjectString (srcNode);
    IsmDestroyObjectString (srcLeaf);

    return retval;
}

BOOL
pConcatRuleIndex (
    IN      PCTSTR Node,
    IN      PCTSTR SearchStr
    )
{
    MIG_OBJECTSTRINGHANDLE objectName;
    MIG_CONTENT objectContent;
    PTSTR tmpNode;
    PTSTR ptr;
    TCHAR number[5];
    PTSTR newStr;

    tmpNode = DuplicateText(Node);
    if (tmpNode) {
        ptr = (PTSTR)_tcsistr (tmpNode, SearchStr);
        if (ptr) {
            ptr += CharCount(SearchStr);
            StringCopyCharCount(number, ptr, 4);
            number[4] = 0;
            *ptr = 0;
            objectName = IsmCreateObjectHandle (tmpNode, TEXT("Order"));
            if (IsmAcquireObject(g_RegType | PLATFORM_DESTINATION,
                                 objectName,
                                 &objectContent)) {
                if (IsValidRegSz(&objectContent)) {
                    if (!_tcsistr ((PCTSTR)objectContent.MemoryContent.ContentBytes, number)) {
                        newStr = IsmGetMemory(objectContent.MemoryContent.ContentSize + SizeOfString(number) + 1);
                        StringCopy(newStr, (PCTSTR)objectContent.MemoryContent.ContentBytes);
                        StringCat(newStr, TEXT(" "));
                        StringCat(newStr, number);

                        IsmReleaseMemory(objectContent.MemoryContent.ContentBytes);
                        objectContent.MemoryContent.ContentSize = SizeOfString(newStr);
                        objectContent.MemoryContent.ContentBytes = (PCBYTE)newStr;

                        IsmReplacePhysicalObject (g_RegType, objectName, &objectContent);
                    }
                }
                IsmReleaseObject(&objectContent);
            }
            IsmDestroyObjectHandle(objectName);
        }
        FreeText(tmpNode);
    }
    return TRUE;
}

BOOL
pRenameEx
(
    IN      PCTSTR OldNode,
    IN      PCTSTR NewNode,
    IN      PCTSTR Leaf,
    IN      BOOL NoRestoreObject,
    IN      PCTSTR PrevKey,
    IN      PCTSTR FormatStr,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL ZeroBase
    )
{
    BOOL result = FALSE;
    PTSTR patternNode;
    PTSTR tmpNode;
    PTSTR ptr;
    PTSTR searchStr;
    DWORD prevCount;
    DWORD keySize = 1;
    MIG_FILTERINPUT filterInput;
    MIG_FILTEROUTPUT filterOutput;
    MIG_BLOB migBlob;
    MIG_BLOB zeroBaseBlob;
    PTSTR filteredNode = NULL;

    tmpNode = DuplicateText(NewNode);
    if (tmpNode) {
        prevCount = CharCount(PrevKey);
        searchStr = AllocText(prevCount + 3);
        if (searchStr) {
            _stprintf(searchStr, TEXT("\\%s\\"), PrevKey);

            ptr = (PTSTR)_tcsistr (tmpNode, searchStr);
            if (ptr) {
                ptr += (prevCount + 2); // Advance to next portion
                *ptr = 0;
                ptr = _tcsinc(ptr);
                while (*ptr && *ptr != TEXT('\\')) {
                    ptr = _tcsinc(ptr);
                    keySize++;
                }

                patternNode = AllocText(CharCount(NewNode) + (CharCount(FormatStr) - keySize));
                if (patternNode) {
                    StringCopy(patternNode, tmpNode);
                    StringCat(patternNode, FormatStr);
                    StringCat(patternNode, ptr);

                    filterInput.OriginalObject.ObjectTypeId = g_RegType;
                    filterInput.OriginalObject.ObjectName = IsmCreateObjectHandle (OldNode, NULL);
                    filterInput.CurrentObject.ObjectTypeId = g_RegType;
                    filterInput.CurrentObject.ObjectName = IsmCreateObjectHandle (OldNode, NULL);
                    migBlob.Type = BLOBTYPE_STRING;
                    migBlob.String = IsmCreateObjectHandle (patternNode, NULL);

                    if (ZeroBase) {
                        zeroBaseBlob.Type = BLOBTYPE_BINARY;
                        zeroBaseBlob.BinarySize = sizeof(PCBYTE);
                        zeroBaseBlob.BinaryData = (PCBYTE)TRUE;

                        FilterRenameExFilter (&filterInput, &filterOutput, NoRestoreObject, &zeroBaseBlob, &migBlob);
                    } else {
                        FilterRenameExFilter (&filterInput, &filterOutput, NoRestoreObject, NULL, &migBlob);
                    }

                    IsmDestroyObjectHandle (migBlob.String);
                    IsmDestroyObjectHandle (filterInput.CurrentObject.ObjectName);
                    IsmDestroyObjectHandle (filterInput.OriginalObject.ObjectName);

                    IsmCreateObjectStringsFromHandle (filterOutput.NewObject.ObjectName, &filteredNode, NULL);
                    IsmDestroyObjectHandle (filterOutput.NewObject.ObjectName);

                    OutputData->NewObject.ObjectName = IsmCreateObjectHandle (filteredNode, Leaf);
                    if (0 == *ptr) {
                        pConcatRuleIndex(filteredNode, searchStr);
                    }
                    FreeText (filteredNode);

                    FreeText(patternNode);
                }
            } else {
                OutputData->NewObject.ObjectName = IsmCreateObjectHandle (tmpNode, Leaf);
            }
            FreeText(searchStr);
        } else {
            OutputData->NewObject.ObjectName = IsmCreateObjectHandle (tmpNode, Leaf);
        }
        FreeText(tmpNode);
    }
    return TRUE;
}

BOOL
pRenameNewsRule
(
    IN      PCTSTR OldNode,
    IN      PCTSTR NewNode,
    IN      PCTSTR Leaf,
    IN      BOOL NoRestoreObject,
    OUT     PMIG_FILTEROUTPUT OutputData
    )
{
    return pRenameEx(OldNode, NewNode, Leaf, NoRestoreObject, TEXT("News"), TEXT("<%03d>"), OutputData, TRUE);
}

BOOL
pRenameMailRule
(
    IN      PCTSTR OldNode,
    IN      PCTSTR NewNode,
    IN      PCTSTR Leaf,
    IN      BOOL NoRestoreObject,
    OUT     PMIG_FILTEROUTPUT OutputData
    )
{
    return pRenameEx(OldNode, NewNode, Leaf, NoRestoreObject, TEXT("Mail"), TEXT("<%03d>"), OutputData, TRUE);
}

BOOL
pRenameBlockRule
(
    IN      PCTSTR OldNode,
    IN      PCTSTR NewNode,
    IN      PCTSTR Leaf,
    IN      BOOL NoRestoreObject,
    OUT     PMIG_FILTEROUTPUT OutputData
    )
{
    return pRenameEx(OldNode, NewNode, Leaf, NoRestoreObject, TEXT("Criteria"), TEXT("<%03d>"), OutputData, TRUE);
}



BOOL
pRenameAccount
(
    IN      PCTSTR OldNode,
    IN      PCTSTR NewNode,
    IN      PCTSTR Leaf,
    IN      BOOL NoRestoreObject,
    OUT     PMIG_FILTEROUTPUT OutputData
    )
{
    return pRenameEx(OldNode, NewNode, Leaf, NoRestoreObject, TEXT("Accounts"), TEXT("<%08d>"), OutputData, FALSE);
}

BOOL
WINAPI
pConvertOE5RulesMove (
    IN      PCMIG_FILTERINPUT InputData,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL NoRestoreObject,
    IN      LPRENAMERULE fnRename
    )
{
    PCTSTR srcNode = NULL;
    PCTSTR srcLeaf = NULL;
    PTSTR newNode = NULL;
    PTSTR tmpText;
    TCHAR *endId;
    TCHAR *srcIdentity;
    PTSTR newIdentity;

    // Move tree and Merge account name

    IsmCreateObjectStringsFromHandle (InputData->CurrentObject.ObjectName, &srcNode, &srcLeaf);
    if (srcNode) {
        // srcNode should be "HKCU\Identities\{GUID}\Software\Microsoft\Outlook Express\Rules\Mail\..."
        tmpText = DuplicateText(srcNode);
        if (tmpText) {
            srcIdentity = _tcschr(tmpText, TEXT('{'));
            if (srcIdentity) {
                endId = _tcschr(srcIdentity, TEXT('\\'));
                if (endId) {
                    *endId = 0;
                    endId = _tcsinc(endId);

                    // endId should be "Software\Microsoft\Outlook Express\Rules\Mail\..."
                    // srcIdentity should be "{GUID}"

                    newIdentity = OEGetRemappedId (srcIdentity);
                    if (newIdentity) {
                        newNode = AllocText (CharCount(srcNode) + 1);
                        StringCopy (newNode, TEXT("HKCU\\Identities\\"));
                        StringCat (newNode, newIdentity);
                        StringCat (newNode, TEXT("\\"));
                        StringCat (newNode, endId);

                        if (newNode) {
                            if (srcLeaf &&
                                !g_OERulesMigrated &&
                                !StringIMatch(srcLeaf, TEXT("Version"))) {
                                g_OERulesMigrated = TRUE;
                            }
                            fnRename(srcNode, newNode, srcLeaf, NoRestoreObject, OutputData);
                            FreeText(newNode);
                        }
                        FreeText(newIdentity);
                    }
                }
            }
            FreeText(tmpText);
        }
        IsmDestroyObjectString (srcNode);
    }
    IsmDestroyObjectString (srcLeaf);

    return TRUE;
}

BOOL
WINAPI
pConvertOE5NewsRulesMove (
    IN      PCMIG_FILTERINPUT InputData,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL NoRestoreObject,
    IN      PCMIG_BLOB SourceOperationData,             OPTIONAL
    IN      PCMIG_BLOB DestinationOperationData         OPTIONAL
    )
{
    return pConvertOE5RulesMove(InputData, OutputData, NoRestoreObject, pRenameNewsRule);
}

BOOL
WINAPI
pConvertOE5MailRulesMove (
    IN      PCMIG_FILTERINPUT InputData,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL NoRestoreObject,
    IN      PCMIG_BLOB SourceOperationData,             OPTIONAL
    IN      PCMIG_BLOB DestinationOperationData         OPTIONAL
    )
{
    return pConvertOE5RulesMove(InputData, OutputData, NoRestoreObject, pRenameMailRule);
}

BOOL
WINAPI
pConvertOE5BlockMove (
    IN      PCMIG_FILTERINPUT InputData,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL NoRestoreObject,
    IN      PCMIG_BLOB SourceOperationData,             OPTIONAL
    IN      PCMIG_BLOB DestinationOperationData         OPTIONAL
    )
{
    return pConvertOE5RulesMove(InputData, OutputData, NoRestoreObject, pRenameBlockRule);
}

BOOL
WINAPI
pConvertOEIdIAMMove (
    IN      PCMIG_FILTERINPUT InputData,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL NoRestoreObject,
    IN      PCMIG_BLOB SourceOperationData,             OPTIONAL
    IN      PCMIG_BLOB DestinationOperationData         OPTIONAL
    )
{
    PCTSTR srcNode = NULL;
    PCTSTR srcLeaf = NULL;
    PTSTR newNode = NULL;
    PTSTR tmpText;
    TCHAR *endId;
    TCHAR *srcIdentity;
    PTSTR newIdentity;

    // Move tree and Merge account name

    IsmCreateObjectStringsFromHandle (InputData->CurrentObject.ObjectName, &srcNode, &srcLeaf);
    if (srcNode) {
        // srcNode should be "HKCU\Identities\{GUID}\Software\Microsoft\Internet Account Manager\..."
        tmpText = DuplicateText(srcNode);
        if (tmpText) {
            srcIdentity = _tcschr(tmpText, TEXT('{'));
            if (srcIdentity) {
                endId = _tcschr(srcIdentity, TEXT('\\'));
                if (endId) {
                    *endId = 0;
                    endId = _tcsinc(endId);

                    // endId should be "Software\Microsoft\Internet Account Manager\..."
                    // srcIdentity should be "{GUID}"

                    newIdentity = OEGetRemappedId (srcIdentity);
                    if (newIdentity) {
                        if (OEIsIdentityAssociated (newIdentity)) {
                            // allocText below does include 1 extra for Null
                            newNode = AllocText (CharCount(endId) + 6);
                            StringCopy (newNode, TEXT("HKCU\\"));  // +5
                            StringCat (newNode, endId);
                            // newNode should be "HKCU\Software\Microsoft\Internet Account Manager\..."
                        } else {
                            newNode = AllocText (CharCount(srcNode) + 1);
                            StringCopy (newNode, TEXT("HKCU\\Identities\\"));
                            StringCat (newNode, newIdentity);
                            StringCat (newNode, TEXT("\\"));
                            StringCat (newNode, endId);
                        }

                        if (newNode) {
                            pRenameAccount(srcNode,
                                           newNode,
                                           srcLeaf,
                                           NoRestoreObject,
                                           OutputData);
                            FreeText(newNode);
                        }
                        FreeText(newIdentity);
                    }
                }
            }
            FreeText(tmpText);
        }
        IsmDestroyObjectString (srcNode);
    }
    IsmDestroyObjectString (srcLeaf);

    return TRUE;
}

BOOL
WINAPI
pConvertOEIAMMove (
    IN      PCMIG_FILTERINPUT InputData,
    OUT     PMIG_FILTEROUTPUT OutputData,
    IN      BOOL NoRestoreObject,
    IN      PCMIG_BLOB SourceOperationData,             OPTIONAL
    IN      PCMIG_BLOB DestinationOperationData         OPTIONAL
    )
{
    PCTSTR srcNode = NULL;
    PCTSTR srcLeaf = NULL;
    PTSTR newNode = NULL;
    PTSTR filteredNode = NULL;
    PTSTR ptr = NULL;

    // Move tree and Merge account name

    IsmCreateObjectStringsFromHandle (InputData->CurrentObject.ObjectName, &srcNode, &srcLeaf);
    if (srcNode) {
        // srcNode should be "HKCU\Software\Microsoft\Internet Account Manager\..."
        if (g_DestIdentityGUID != NULL &&
            !OEIsIdentityAssociated (g_DestIdentityGUID)) {

            ptr = _tcschr (srcNode, TEXT('\\'));
            if (ptr) {
                newNode = AllocText (TcharCount (srcNode) + TcharCount (g_DestIdentityGUID) + 13);
                StringCopy (newNode, TEXT("HKCU\\Identities\\"));    // +12
                StringCat (newNode, g_DestIdentityGUID);
                StringCat (newNode, ptr);

                // newNode should be "HKCU\Identities\{GUID}\Software\Microsoft\Internet Account Manager\..."
            }
        } else {
            newNode = DuplicateText (srcNode);
        }

        if (newNode) {
            pRenameAccount(srcNode,
                           newNode,
                           srcLeaf,
                           NoRestoreObject,
                           OutputData);
            FreeText (newNode);
        }
        IsmDestroyObjectString (srcNode);
    }
    IsmDestroyObjectString (srcLeaf);

    return TRUE;
}