diff --git a/lib/src/kdbx_entry.dart b/lib/src/kdbx_entry.dart index 93db068..5d2f85e 100644 --- a/lib/src/kdbx_entry.dart +++ b/lib/src/kdbx_entry.dart @@ -286,6 +286,8 @@ class KdbxEntry extends KdbxObject { Iterable> get binaryEntries => _binaries.entries; + KdbxBinary getBinary(KdbxKey key) => _binaries[key]; + // Map get strings => UnmodifiableMapView(_strings); Iterable> get stringEntries => diff --git a/lib/src/kdbx_meta.dart b/lib/src/kdbx_meta.dart index 3fcb7c3..02e4dac 100644 --- a/lib/src/kdbx_meta.dart +++ b/lib/src/kdbx_meta.dart @@ -43,18 +43,29 @@ class KdbxMeta extends KdbxNode implements KdbxNodeContext { .singleElement('CustomData') ?.let((e) => KdbxCustomData.read(e)) ?? KdbxCustomData.create(), - binaries = node.singleElement(KdbxXml.NODE_BINARIES)?.let((el) sync* { - var i = 0; - for (final binaryNode in el.findElements(KdbxXml.NODE_BINARY)) { - final id = int.parse(binaryNode.getAttribute(KdbxXml.ATTR_ID)); - if (id != i) { - throw KdbxCorruptedFileException( - 'Invalid ID for binary. expected $i, but was $id'); - } - i++; - yield KdbxBinary.readBinaryXml(binaryNode, isInline: false); - } - })?.toList(), + binaries = node + .singleElement(KdbxXml.NODE_BINARIES) + ?.let((el) sync* { + for (final binaryNode in el.findElements(KdbxXml.NODE_BINARY)) { + final id = int.parse(binaryNode.getAttribute(KdbxXml.ATTR_ID)); + yield MapEntry( + id, + KdbxBinary.readBinaryXml(binaryNode, isInline: false), + ); + } + }) + ?.toList() + ?.let((binaries) { + binaries.sort((a, b) => a.key.compareTo(b.key)); + for (var i = 0; i < binaries.length; i++) { + if (i != binaries[i].key) { + throw KdbxCorruptedFileException( + 'Invalid ID for binary. expected $i,' + ' but was ${binaries[i].key}'); + } + } + return binaries.map((e) => e.value).toList(); + }), _customIcons = node .singleElement(KdbxXml.NODE_CUSTOM_ICONS) ?.let((el) sync* { diff --git a/test/kdbx_binaries_test.dart b/test/kdbx_binaries_test.dart index e1556a4..87746e9 100644 --- a/test/kdbx_binaries_test.dart +++ b/test/kdbx_binaries_test.dart @@ -122,6 +122,18 @@ void main() { // make sure the file can still be saved. await file.save(); }); + test('keepassxc compatibility', () async { + // keepass has files in arbitrary sort order. + final file = await TestUtil.readKdbxFile( + 'test/test_files/binarytest-keepassxc.kdbx'); + final entry = file.body.rootGroup.entries.first; + for (final name in ['a', 'b', 'c', 'd', 'e']) { + expect( + utf8.decode(entry.getBinary(KdbxKey('$name.txt')).value).trim(), + name, + ); + } + }); }, tags: ['kdbx3']); group('kdbx4 attachment', () { test('read binary', () async { diff --git a/test/test_files/binarytest-keepassxc.kdbx b/test/test_files/binarytest-keepassxc.kdbx new file mode 100644 index 0000000..59d29c6 Binary files /dev/null and b/test/test_files/binarytest-keepassxc.kdbx differ