mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'develop' into release/4.2.0
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.CustomData
|
||||
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.DatabaseKDBX
|
||||
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.NodeIdInt
|
||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||
import com.kunzisoft.keepass.database.element.node.NodeVersioned
|
||||
import com.kunzisoft.keepass.utils.readAllBytes
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
|
||||
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
|
||||
* To make a merge as accurate as possible
|
||||
*/
|
||||
@@ -302,32 +304,113 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
|
||||
}
|
||||
|
||||
// Manage deleted objects
|
||||
databaseToMerge.deletedObjects.forEach { deletedObject ->
|
||||
val deletedObjectId = deletedObject.uuid
|
||||
val databaseEntry = database.getEntryById(deletedObjectId)
|
||||
val databaseGroup = database.getGroupById(deletedObjectId)
|
||||
val databaseIcon = database.iconsManager.getIcon(deletedObjectId)
|
||||
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)
|
||||
}
|
||||
val deletedObjects = databaseToMerge.deletedObjects
|
||||
deletedObjects.forEach { deletedObject ->
|
||||
deleteEntry(deletedObject)
|
||||
deleteGroup(deletedObject, deletedObjects)
|
||||
deleteIcon(deletedObject)
|
||||
// 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]
|
||||
*/
|
||||
|
||||
@@ -154,7 +154,7 @@ class SearchHelper {
|
||||
if (searchParameters.searchByDomain) {
|
||||
try {
|
||||
stringToCheck.inTheSameDomainAs(word, sameSubDomain = true)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
} else null
|
||||
@@ -220,10 +220,18 @@ class SearchHelper {
|
||||
regex.matches(stringToCheck)
|
||||
} else {
|
||||
specialComparison?.invoke(stringToCheck, searchParameters.searchQuery)
|
||||
?: stringToCheck.contains(
|
||||
searchParameters.searchQuery,
|
||||
!searchParameters.caseSensitive
|
||||
)
|
||||
?: run {
|
||||
// Search with space separator #175
|
||||
var searchFound = true
|
||||
searchParameters.searchQuery.split(" ").forEach { word ->
|
||||
searchFound = searchFound
|
||||
&& stringToCheck.contains(
|
||||
word,
|
||||
!searchParameters.caseSensitive
|
||||
)
|
||||
}
|
||||
searchFound
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,19 +119,19 @@ fun <K : Parcelable, V : Parcelable> Parcel.writeParcelableMap(map: Map<K, V>, f
|
||||
inline fun <reified K : Parcelable, reified V : Parcelable> Parcel.readParcelableMap(): Map<K, V> {
|
||||
val size = readInt()
|
||||
val map = HashMap<K, V>(size)
|
||||
for (i in 0 until size) {
|
||||
(0 until size).forEach { i ->
|
||||
val key: K? = try {
|
||||
when {
|
||||
SDK_INT >= 33 -> readParcelable(K::class.java.classLoader, K::class.java)
|
||||
else -> @Suppress("DEPRECATION") readParcelable(K::class.java.classLoader)
|
||||
}
|
||||
} catch (e: Exception) { null }
|
||||
} catch (_: Exception) { null }
|
||||
val value: V? = try {
|
||||
when {
|
||||
SDK_INT >= 33 -> readParcelable(V::class.java.classLoader, V::class.java)
|
||||
else -> @Suppress("DEPRECATION") readParcelable(V::class.java.classLoader)
|
||||
}
|
||||
} catch (e: Exception) { null }
|
||||
} catch (_: Exception) { null }
|
||||
if (key != null && value != null)
|
||||
map[key] = value
|
||||
}
|
||||
@@ -151,14 +151,14 @@ fun <V : Parcelable> Parcel.writeStringParcelableMap(map: HashMap<String, V>, fl
|
||||
inline fun <reified V : Parcelable> Parcel.readStringParcelableMap(): LinkedHashMap<String, V> {
|
||||
val size = readInt()
|
||||
val map = LinkedHashMap<String, V>(size)
|
||||
for (i in 0 until size) {
|
||||
(0 until size).forEach { i ->
|
||||
val key: String? = readString()
|
||||
val value: V? = try {
|
||||
when {
|
||||
SDK_INT >= 33 -> readParcelable(V::class.java.classLoader, V::class.java)
|
||||
else -> @Suppress("DEPRECATION") readParcelable(V::class.java.classLoader)
|
||||
}
|
||||
} catch (e: Exception) { null }
|
||||
} catch (_: Exception) { null }
|
||||
if (key != null && value != null)
|
||||
map[key] = value
|
||||
}
|
||||
@@ -178,7 +178,7 @@ fun Parcel.writeStringIntMap(map: LinkedHashMap<String, Int>) {
|
||||
fun Parcel.readStringIntMap(): LinkedHashMap<String, Int> {
|
||||
val size = readInt()
|
||||
val map = LinkedHashMap<String, Int>(size)
|
||||
for (i in 0 until size) {
|
||||
(0 until size).forEach { i ->
|
||||
val key: String? = readString()
|
||||
val value: Int = readInt()
|
||||
if (key != null)
|
||||
@@ -200,7 +200,7 @@ fun Parcel.writeStringStringMap(map: MutableMap<String, String>) {
|
||||
fun Parcel.readStringStringMap(): LinkedHashMap<String, String> {
|
||||
val size = readInt()
|
||||
val map = LinkedHashMap<String, String>(size)
|
||||
for (i in 0 until size) {
|
||||
(0 until size).forEach { i ->
|
||||
val key: String? = readString()
|
||||
val value: String? = readString()
|
||||
if (key != null && value != null)
|
||||
|
||||
Reference in New Issue
Block a user