|
|
@ -16,6 +16,7 @@ import 'package:kdbx/src/internal/crypto_utils.dart'; |
|
|
|
import 'package:kdbx/src/internal/extension_utils.dart'; |
|
|
|
import 'package:kdbx/src/internal/extension_utils.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_deleted_object.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_deleted_object.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_entry.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_entry.dart'; |
|
|
|
|
|
|
|
import 'package:kdbx/src/kdbx_group.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_header.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_header.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_xml.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_xml.dart'; |
|
|
|
import 'package:kdbx/src/utils/byte_utils.dart'; |
|
|
|
import 'package:kdbx/src/utils/byte_utils.dart'; |
|
|
@ -338,7 +339,25 @@ class KdbxBody extends KdbxNode { |
|
|
|
|
|
|
|
|
|
|
|
// remove deleted objects |
|
|
|
// remove deleted objects |
|
|
|
for (final incomingDelete in incomingDeleted.values) { |
|
|
|
for (final incomingDelete in incomingDeleted.values) { |
|
|
|
final object = mergeContext.objectIndex![incomingDelete.uuid!]; |
|
|
|
final object = mergeContext.objectIndex[incomingDelete.uuid]; |
|
|
|
|
|
|
|
if (object == null) { |
|
|
|
|
|
|
|
mergeContext.trackWarning( |
|
|
|
|
|
|
|
'Incoming deleted object not found locally ${incomingDelete.uuid}'); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
final parent = object.parent; |
|
|
|
|
|
|
|
if (parent == null) { |
|
|
|
|
|
|
|
mergeContext.trackWarning('Unable to delete object $object - ' |
|
|
|
|
|
|
|
'already deleted? (${incomingDelete.uuid})'); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (object is KdbxGroup) { |
|
|
|
|
|
|
|
parent.internalRemoveGroup(object); |
|
|
|
|
|
|
|
} else if (object is KdbxEntry) { |
|
|
|
|
|
|
|
parent.internalRemoveEntry(object); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
throw StateError('Invalid object type $object'); |
|
|
|
|
|
|
|
} |
|
|
|
mergeContext.trackChange(object, debug: 'was deleted.'); |
|
|
|
mergeContext.trackChange(object, debug: 'was deleted.'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -347,11 +366,11 @@ class KdbxBody extends KdbxNode { |
|
|
|
_logger.info('Finished merging:\n${mergeContext.debugChanges()}'); |
|
|
|
_logger.info('Finished merging:\n${mergeContext.debugChanges()}'); |
|
|
|
final incomingObjects = other._createObjectIndex(); |
|
|
|
final incomingObjects = other._createObjectIndex(); |
|
|
|
_logger.info('Merged: ${mergeContext.merged} vs. ' |
|
|
|
_logger.info('Merged: ${mergeContext.merged} vs. ' |
|
|
|
'(local objects: ${mergeContext.objectIndex!.length}, ' |
|
|
|
'(local objects: ${mergeContext.objectIndex.length}, ' |
|
|
|
'incoming objects: ${incomingObjects.length})'); |
|
|
|
'incoming objects: ${incomingObjects.length})'); |
|
|
|
|
|
|
|
|
|
|
|
// sanity checks |
|
|
|
// sanity checks |
|
|
|
if (mergeContext.merged.keys.length != mergeContext.objectIndex!.length) { |
|
|
|
if (mergeContext.merged.keys.length != mergeContext.objectIndex.length) { |
|
|
|
// TODO figure out what went wrong. |
|
|
|
// TODO figure out what went wrong. |
|
|
|
} |
|
|
|
} |
|
|
|
return mergeContext; |
|
|
|
return mergeContext; |
|
|
@ -429,12 +448,24 @@ class MergeChange { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MergeWarning { |
|
|
|
|
|
|
|
MergeWarning(this.debug); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final String debug; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
|
|
String toString() { |
|
|
|
|
|
|
|
return debug; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class MergeContext implements OverwriteContext { |
|
|
|
class MergeContext implements OverwriteContext { |
|
|
|
MergeContext({this.objectIndex, this.deletedObjects}); |
|
|
|
MergeContext({required this.objectIndex, required this.deletedObjects}); |
|
|
|
final Map<KdbxUuid, KdbxObject>? objectIndex; |
|
|
|
final Map<KdbxUuid, KdbxObject> objectIndex; |
|
|
|
final Map<KdbxUuid?, KdbxDeletedObject>? deletedObjects; |
|
|
|
final Map<KdbxUuid?, KdbxDeletedObject> deletedObjects; |
|
|
|
final Map<KdbxUuid, KdbxObject> merged = {}; |
|
|
|
final Map<KdbxUuid, KdbxObject> merged = {}; |
|
|
|
final List<MergeChange> changes = []; |
|
|
|
final List<MergeChange> changes = []; |
|
|
|
|
|
|
|
final List<MergeWarning> warnings = []; |
|
|
|
|
|
|
|
|
|
|
|
void markAsMerged(KdbxObject object) { |
|
|
|
void markAsMerged(KdbxObject object) { |
|
|
|
if (merged.containsKey(object.uuid)) { |
|
|
|
if (merged.containsKey(object.uuid)) { |
|
|
@ -453,6 +484,11 @@ class MergeContext implements OverwriteContext { |
|
|
|
)); |
|
|
|
)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void trackWarning(String warning) { |
|
|
|
|
|
|
|
_logger.warning(warning, StackTrace.current); |
|
|
|
|
|
|
|
warnings.add(MergeWarning(warning)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
String debugChanges() { |
|
|
|
String debugChanges() { |
|
|
|
final group = |
|
|
|
final group = |
|
|
|
changes.groupBy((element) => element.object, valueTransform: (x) => x); |
|
|
|
changes.groupBy((element) => element.object, valueTransform: (x) => x); |
|
|
|