mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: Delete algo during merge #1516
This commit is contained in:
@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.database.merge
|
|||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
import com.kunzisoft.keepass.database.element.CustomData
|
import com.kunzisoft.keepass.database.element.CustomData
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
|
import com.kunzisoft.keepass.database.element.DeletedObject
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDB
|
import com.kunzisoft.keepass.database.element.database.DatabaseKDB
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||||
import com.kunzisoft.keepass.database.element.entry.EntryKDB
|
import com.kunzisoft.keepass.database.element.entry.EntryKDB
|
||||||
@@ -32,9 +33,10 @@ import com.kunzisoft.keepass.database.element.node.NodeHandler
|
|||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeIdInt
|
import com.kunzisoft.keepass.database.element.node.NodeIdInt
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||||
|
import com.kunzisoft.keepass.database.element.node.NodeVersioned
|
||||||
import com.kunzisoft.keepass.utils.readAllBytes
|
import com.kunzisoft.keepass.utils.readAllBytes
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
|
||||||
class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
|
class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
|
||||||
|
|
||||||
@@ -180,7 +182,7 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge a KDB> database in a KDBX database,
|
* Merge a KDBX database in a KDBX database,
|
||||||
* Try to take into account the modification date of each element
|
* Try to take into account the modification date of each element
|
||||||
* To make a merge as accurate as possible
|
* To make a merge as accurate as possible
|
||||||
*/
|
*/
|
||||||
@@ -302,32 +304,113 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Manage deleted objects
|
// Manage deleted objects
|
||||||
databaseToMerge.deletedObjects.forEach { deletedObject ->
|
val deletedObjects = databaseToMerge.deletedObjects
|
||||||
val deletedObjectId = deletedObject.uuid
|
deletedObjects.forEach { deletedObject ->
|
||||||
val databaseEntry = database.getEntryById(deletedObjectId)
|
deleteEntry(deletedObject)
|
||||||
val databaseGroup = database.getGroupById(deletedObjectId)
|
deleteGroup(deletedObject, deletedObjects)
|
||||||
val databaseIcon = database.iconsManager.getIcon(deletedObjectId)
|
deleteIcon(deletedObject)
|
||||||
val databaseIconModificationTime = databaseIcon?.lastModificationTime
|
|
||||||
if (databaseEntry != null
|
|
||||||
&& deletedObject.deletionTime.isAfter(databaseEntry.lastModificationTime)) {
|
|
||||||
database.removeEntryFrom(databaseEntry, databaseEntry.parent)
|
|
||||||
}
|
|
||||||
if (databaseGroup != null
|
|
||||||
&& deletedObject.deletionTime.isAfter(databaseGroup.lastModificationTime)) {
|
|
||||||
database.removeGroupFrom(databaseGroup, databaseGroup.parent)
|
|
||||||
}
|
|
||||||
if (databaseIcon != null
|
|
||||||
&& (
|
|
||||||
databaseIconModificationTime == null
|
|
||||||
|| (deletedObject.deletionTime.isAfter(databaseIconModificationTime))
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
database.removeCustomIcon(deletedObjectId)
|
|
||||||
}
|
|
||||||
// Attachments are removed and optimized during the database save
|
// Attachments are removed and optimized during the database save
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an entry from the database with the [deletedEntry] id
|
||||||
|
*/
|
||||||
|
private fun deleteEntry(deletedEntry: DeletedObject) {
|
||||||
|
val databaseEntry = database.getEntryById(deletedEntry.uuid)
|
||||||
|
if (databaseEntry != null
|
||||||
|
&& deletedEntry.deletionTime.isAfter(databaseEntry.lastModificationTime)) {
|
||||||
|
database.removeEntryFrom(databaseEntry, databaseEntry.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a node is in the list of deleted objects
|
||||||
|
*/
|
||||||
|
private fun Set<DeletedObject>.containsNode(node: NodeVersioned<UUID, GroupKDBX, EntryKDBX>): Boolean {
|
||||||
|
return this.any { it.uuid == node.nodeId.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a node is not in the list of deleted objects
|
||||||
|
*/
|
||||||
|
private fun Set<DeletedObject>.notContainsNode(node: NodeVersioned<UUID, GroupKDBX, EntryKDBX>): Boolean {
|
||||||
|
return !this.containsNode(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first parent not deleted
|
||||||
|
*/
|
||||||
|
private fun firstNotDeletedParent(
|
||||||
|
node: NodeVersioned<UUID, GroupKDBX, EntryKDBX>,
|
||||||
|
deletedObjects: Set<DeletedObject>
|
||||||
|
): GroupKDBX? {
|
||||||
|
var parent = node.parent
|
||||||
|
while (parent != null && deletedObjects.containsNode(parent)) {
|
||||||
|
parent = node.parent
|
||||||
|
}
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a group from the database with the [deletedGroup] id
|
||||||
|
* Recursively check whether a group to be deleted contains a node not to be deleted with [deletedObjects]
|
||||||
|
* and move it to the first parent that has not been deleted.
|
||||||
|
*/
|
||||||
|
private fun deleteGroup(deletedGroup: DeletedObject, deletedObjects: Set<DeletedObject>) {
|
||||||
|
val databaseGroup = database.getGroupById(deletedGroup.uuid)
|
||||||
|
if (databaseGroup != null
|
||||||
|
&& deletedGroup.deletionTime.isAfter(databaseGroup.lastModificationTime)) {
|
||||||
|
// Must be in dedicated list to prevent modification collision
|
||||||
|
val entriesToMove = mutableListOf<EntryKDBX>()
|
||||||
|
databaseGroup.getChildEntries().forEach { child ->
|
||||||
|
// If the child entry is not a deleted object,
|
||||||
|
if (deletedObjects.notContainsNode(child)) {
|
||||||
|
entriesToMove.add(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val groupsToMove = mutableListOf<GroupKDBX>()
|
||||||
|
databaseGroup.getChildGroups().forEach { child ->
|
||||||
|
// Move the group to the first parent not deleted
|
||||||
|
// the deleted objects will take care of remove it later
|
||||||
|
groupsToMove.add(child)
|
||||||
|
}
|
||||||
|
// For each node to move, move it
|
||||||
|
// try to move the child entry in the first parent not deleted
|
||||||
|
entriesToMove.forEach { child ->
|
||||||
|
database.removeEntryFrom(child, child.parent)
|
||||||
|
database.addEntryTo(
|
||||||
|
child,
|
||||||
|
firstNotDeletedParent(databaseGroup, deletedObjects)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
groupsToMove.forEach { child ->
|
||||||
|
database.removeGroupFrom(child, child.parent)
|
||||||
|
database.addGroupTo(
|
||||||
|
child,
|
||||||
|
firstNotDeletedParent(databaseGroup, deletedObjects)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Then delete the group
|
||||||
|
database.removeGroupFrom(databaseGroup, databaseGroup.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an icon from the database with the [deletedIcon] id
|
||||||
|
*/
|
||||||
|
private fun deleteIcon(deletedIcon: DeletedObject) {
|
||||||
|
val deletedObjectId = deletedIcon.uuid
|
||||||
|
val databaseIcon = database.iconsManager.getIcon(deletedObjectId)
|
||||||
|
val databaseIconModificationTime = databaseIcon?.lastModificationTime
|
||||||
|
if (databaseIcon != null
|
||||||
|
&& (databaseIconModificationTime == null
|
||||||
|
|| (deletedIcon.deletionTime.isAfter(databaseIconModificationTime)))
|
||||||
|
) {
|
||||||
|
database.removeCustomIcon(deletedObjectId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge [customDataToMerge] in [customData]
|
* Merge [customDataToMerge] in [customData]
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user