From a5c4d6a0e7d442981ca6dc48c9244b2105d020f1 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Fri, 21 May 2021 22:48:32 +0200 Subject: [PATCH] migrate to nnbd --- bin/kdbx.dart | 14 +-- lib/src/crypto/key_encrypter_kdf.dart | 28 ++--- lib/src/crypto/protected_salt_generator.dart | 4 +- lib/src/crypto/protected_value.dart | 8 +- lib/src/internal/async_utils.dart | 2 +- lib/src/internal/consts.dart | 2 +- lib/src/internal/crypto_utils.dart | 2 +- lib/src/internal/extension_utils.dart | 19 ++-- lib/src/kdbx_binary.dart | 26 ++--- lib/src/kdbx_custom_data.dart | 2 +- lib/src/kdbx_dao.dart | 14 +-- lib/src/kdbx_deleted_object.dart | 4 +- lib/src/kdbx_entry.dart | 62 +++++------ lib/src/kdbx_file.dart | 10 +- lib/src/kdbx_format.dart | 110 +++++++++---------- lib/src/kdbx_group.dart | 14 +-- lib/src/kdbx_header.dart | 80 +++++++------- lib/src/kdbx_meta.dart | 32 +++--- lib/src/kdbx_object.dart | 33 +++--- lib/src/kdbx_var_dictionary.dart | 8 +- lib/src/kdbx_xml.dart | 49 +++++---- lib/src/utils/byte_utils.dart | 38 +++---- lib/src/utils/print_utils.dart | 12 +- pubspec.yaml | 4 +- test/deleted_objects_test.dart | 2 +- test/icon/kdbx_customicon_test.dart | 2 +- test/internal/test_utils.dart | 2 +- test/kdbx4_test.dart | 4 +- test/kdbx_binaries_test.dart | 8 +- test/kdbx_history_test.dart | 20 ++-- test/kdbx_test.dart | 6 +- test/merge/kdbx_merge_test.dart | 2 +- 32 files changed, 312 insertions(+), 311 deletions(-) diff --git a/bin/kdbx.dart b/bin/kdbx.dart index 402e6e4..b3724e8 100644 --- a/bin/kdbx.dart +++ b/bin/kdbx.dart @@ -16,7 +16,7 @@ final _logger = Logger('kdbx'); void main(List arguments) { exitCode = 0; final runner = KdbxCommandRunner('kdbx', 'Kdbx Utility'); - runner.run(arguments).catchError((dynamic error, StackTrace stackTrace) { + runner.run(arguments).catchError((Object error, StackTrace stackTrace) { if (error is! UsageException) { return Future.error(error, stackTrace); } @@ -76,14 +76,14 @@ abstract class KdbxFileCommand extends Command { @override FutureOr run() async { - final inputFile = argResults['input'] as String; + final inputFile = argResults!['input'] as String?; if (inputFile == null) { usageException('Required argument: --input'); } final bytes = await File(inputFile).readAsBytes(); - final password = argResults['password'] as String ?? + final password = argResults!['password'] as String? ?? _readPassword('Password for $inputFile: '); - final keyFile = argResults['keyfile'] as String; + final keyFile = argResults!['keyfile'] as String?; final keyFileData = keyFile == null ? null : await File(keyFile).readAsBytes(); @@ -103,7 +103,7 @@ String _readPassword(String prompt) { stdin.echoMode = false; stdout.write(prompt); while (true) { - final input = stdin.readLineSync(); + final input = stdin.readLineSync()!; if (input.isNotEmpty) { return input; } @@ -127,9 +127,9 @@ class CatCommand extends KdbxFileCommand { @override String get name => 'cat'; - bool get forceDecrypt => argResults['decrypt'] as bool; + bool? get forceDecrypt => argResults!['decrypt'] as bool?; - bool get allFields => argResults['all-fields'] as bool; + bool? get allFields => argResults!['all-fields'] as bool?; @override Future runWithFile(KdbxFile file) async { diff --git a/lib/src/crypto/key_encrypter_kdf.dart b/lib/src/crypto/key_encrypter_kdf.dart index 85ee06b..3d60420 100644 --- a/lib/src/crypto/key_encrypter_kdf.dart +++ b/lib/src/crypto/key_encrypter_kdf.dart @@ -49,7 +49,7 @@ class KdfField { .fine('VarDictionary{\n${fields.map((f) => f.debug(dict)).join('\n')}'); } - T read(VarDictionary dict) => dict.get(type, field); + T? read(VarDictionary dict) => dict.get(type, field); void write(VarDictionary dict, T value) => dict.set(type, field, value); VarDictionaryItem item(T value) => VarDictionaryItem(field, type, value); @@ -57,7 +57,7 @@ class KdfField { String debug(VarDictionary dict) { final value = dict.get(type, field); final strValue = type == ValueType.typeBytes - ? ByteUtils.toHexList(value as Uint8List) + ? ByteUtils.toHexList(value as Uint8List?) : value; return '$field=$strValue'; } @@ -76,7 +76,7 @@ class KeyEncrypterKdf { return KdbxUuid(uuid); } - static KdfType kdfTypeFor(VarDictionary kdfParameters) { + static KdfType? kdfTypeFor(VarDictionary kdfParameters) { final uuid = KdfField.uuid.read(kdfParameters); if (uuid == null) { throw KdbxCorruptedFileException('No Kdf UUID'); @@ -110,20 +110,20 @@ class KeyEncrypterKdf { Uint8List key, VarDictionary kdfParameters) async { return await argon2.argon2Async(Argon2Arguments( key, - KdfField.salt.read(kdfParameters), + KdfField.salt.read(kdfParameters)!, // 65536, //KdfField.memory.read(kdfParameters), - KdfField.memory.read(kdfParameters) ~/ 1024, - KdfField.iterations.read(kdfParameters), + KdfField.memory.read(kdfParameters)! ~/ 1024, + KdfField.iterations.read(kdfParameters)!, 32, - KdfField.parallelism.read(kdfParameters), + KdfField.parallelism.read(kdfParameters)!, 0, - KdfField.version.read(kdfParameters), + KdfField.version.read(kdfParameters)!, )); } Future encryptAes( Uint8List key, VarDictionary kdfParameters) async { - final encryptionKey = KdfField.salt.read(kdfParameters); + final encryptionKey = KdfField.salt.read(kdfParameters)!; final rounds = KdfField.rounds.read(kdfParameters); assert(encryptionKey.length == 32); return await encryptAesAsync(EncryptAesArgs(encryptionKey, key, rounds)); @@ -137,7 +137,7 @@ class KeyEncrypterKdf { final s = Stopwatch()..start(); try { _logger.finest('Starting encryptAes for ${args.rounds} ' - 'rounds in isolate. ${args.encryptionKey.length} ${args.key.length}'); + 'rounds in isolate. ${args.encryptionKey!.length} ${args.key.length}'); return await runner.run(_encryptAesSync, args); } finally { _logger.finest('Done aes encrypt. ${s.elapsed}'); @@ -147,11 +147,11 @@ class KeyEncrypterKdf { static Uint8List _encryptAesSync(EncryptAesArgs args) { final cipher = ECBBlockCipher(AESFastEngine()) - ..init(true, KeyParameter(args.encryptionKey)); + ..init(true, KeyParameter(args.encryptionKey!)); var out1 = Uint8List.fromList(args.key); var out2 = Uint8List(args.key.length); - final rounds = args.rounds; + final rounds = args.rounds!; for (var i = 0; i < rounds; i++) { for (var j = 0; j < out1.lengthInBytes;) { j += cipher.processBlock(out1, j, out2, j); @@ -167,7 +167,7 @@ class KeyEncrypterKdf { class EncryptAesArgs { EncryptAesArgs(this.encryptionKey, this.key, this.rounds); - final Uint8List encryptionKey; + final Uint8List? encryptionKey; final Uint8List key; - final int rounds; + final int? rounds; } diff --git a/lib/src/crypto/protected_salt_generator.dart b/lib/src/crypto/protected_salt_generator.dart index 43331a8..36aa785 100644 --- a/lib/src/crypto/protected_salt_generator.dart +++ b/lib/src/crypto/protected_salt_generator.dart @@ -24,7 +24,7 @@ class ProtectedSaltGenerator { Uint8List.fromList([0xE8, 0x30, 0x09, 0x4B, 0x97, 0x20, 0x5D, 0x2A]); final StreamCipher _cipher; - String decryptBase64(String protectedValue) { + String? decryptBase64(String protectedValue) { final bytes = base64.decode(protectedValue); if (bytes.isEmpty) { _logger.warning('decoded base64 data has length 0'); @@ -70,7 +70,7 @@ class ChachaProtectedSaltGenerator implements ProtectedSaltGenerator { StreamCipher get _cipher => throw UnimplementedError(); @override - String decryptBase64(String protectedValue) { + String? decryptBase64(String protectedValue) { final bytes = base64.decode(protectedValue); if (bytes.isEmpty) { _logger.warning('decoded base64 data has length 0'); diff --git a/lib/src/crypto/protected_value.dart b/lib/src/crypto/protected_value.dart index 83bfbb7..1bfee3f 100644 --- a/lib/src/crypto/protected_value.dart +++ b/lib/src/crypto/protected_value.dart @@ -6,16 +6,16 @@ import 'package:crypto/crypto.dart'; abstract class StringValue { /// retrieves the (decrypted) stored value. - String getText(); + String? getText(); } class PlainValue implements StringValue { PlainValue(this.text); - final String text; + final String? text; @override - String getText() { + String? getText() { return text; } @@ -78,7 +78,7 @@ class ProtectedValue implements StringValue { bool operator ==(dynamic other) => other is ProtectedValue && other.getText() == getText(); - int _hashCodeCached; + int? _hashCodeCached; @override int get hashCode => _hashCodeCached ??= getText().hashCode; diff --git a/lib/src/internal/async_utils.dart b/lib/src/internal/async_utils.dart index 19c6650..9171458 100644 --- a/lib/src/internal/async_utils.dart +++ b/lib/src/internal/async_utils.dart @@ -8,7 +8,7 @@ mixin StreamSubscriberBase { /// Listens to a stream and saves it to the list of subscriptions. void listen(Stream stream, void Function(dynamic data) onData, - {Function onError}) { + {Function? onError}) { if (stream != null) { _subscriptions.add(stream.listen(onData, onError: onError)); } diff --git a/lib/src/internal/consts.dart b/lib/src/internal/consts.dart index 2b7fe24..5e307d9 100644 --- a/lib/src/internal/consts.dart +++ b/lib/src/internal/consts.dart @@ -18,6 +18,6 @@ class CryptoConsts { static final cipherByUuid = CIPHER_IDS.map((key, value) => MapEntry(value, key)); - static Cipher cipherFromBytes(Uint8List bytes) => + static Cipher? cipherFromBytes(Uint8List bytes) => cipherByUuid[KdbxUuid.fromBytes(bytes)]; } diff --git a/lib/src/internal/crypto_utils.dart b/lib/src/internal/crypto_utils.dart index 6923120..a6c51e2 100644 --- a/lib/src/internal/crypto_utils.dart +++ b/lib/src/internal/crypto_utils.dart @@ -15,7 +15,7 @@ class AesHelper { static Uint8List deriveKey( Uint8List password, { - Uint8List salt, + required Uint8List salt, int iterationCount = ITERATION_COUNT, int derivedKeyLength = KEY_SIZE, }) { diff --git a/lib/src/internal/extension_utils.dart b/lib/src/internal/extension_utils.dart index c6ae88f..ea14669 100644 --- a/lib/src/internal/extension_utils.dart +++ b/lib/src/internal/extension_utils.dart @@ -2,8 +2,8 @@ import 'package:kdbx/src/kdbx_xml.dart'; import 'package:xml/xml.dart' as xml; extension XmlElementExt on xml.XmlElement { - xml.XmlElement singleElement(String nodeName, - {xml.XmlElement Function() orElse}) { + xml.XmlElement? singleElement(String nodeName, + {xml.XmlElement Function()? orElse}) { final elements = findElements(nodeName); if (elements.isEmpty) { if (orElse != null) { @@ -42,17 +42,18 @@ extension ObjectExt on T { R let(R Function(T that) op) => op(this); } -extension StringExt on String { - String takeUnlessBlank() => nullIfBlank(); +extension StringExt on String? { + String? takeUnlessBlank() => nullIfBlank(); - String nullIfBlank() { - if (this == null || isEmpty) { + String? nullIfBlank() { + final t = this; + if (t == null || t.isEmpty) { return null; } return this; } } -extension IterableExt on Iterable { - T get singleOrNull => singleWhere((element) => true, orElse: () => null); -} +// extension IterableExt on Iterable { +// T? get singleOrNull => singleWhereOrNull((element) => true); +// } diff --git a/lib/src/kdbx_binary.dart b/lib/src/kdbx_binary.dart index ff68866..5c37e60 100644 --- a/lib/src/kdbx_binary.dart +++ b/lib/src/kdbx_binary.dart @@ -11,15 +11,15 @@ import 'package:xml/xml.dart'; class KdbxBinary { KdbxBinary({this.isInline, this.isProtected, this.value}); - final bool isInline; - final bool isProtected; - final Uint8List value; - int _valueHashCode; + final bool? isInline; + final bool? isProtected; + final Uint8List? value; + int? _valueHashCode; static KdbxBinary readBinaryInnerHeader(InnerHeaderField field) { - final flags = field.bytes[0]; + final flags = field.bytes![0]; final isProtected = flags & 0x01 == 0x01; - final value = Uint8List.sublistView(field.bytes, 1); + final value = Uint8List.sublistView(field.bytes!, 1); return KdbxBinary( isInline: false, isProtected: isProtected, @@ -27,22 +27,22 @@ class KdbxBinary { ); } - int get valueHashCode => _valueHashCode ??= hashObjects(value); + int get valueHashCode => _valueHashCode ??= hashObjects(value!); bool valueEqual(KdbxBinary other) => - valueHashCode == other.valueHashCode && ByteUtils.eq(value, value); + valueHashCode == other.valueHashCode && ByteUtils.eq(value!, value!); InnerHeaderField writeToInnerHeader() { final writer = WriterHelper(); - final flags = isProtected ? 0x01 : 0x00; + final flags = isProtected! ? 0x01 : 0x00; writer.writeUint8(flags); - writer.writeBytes(value); + writer.writeBytes(value!); return InnerHeaderField( InnerHeaderFields.Binary, writer.output.takeBytes()); } static KdbxBinary readBinaryXml(XmlElement valueNode, - {@required bool isInline}) { + {required bool isInline}) { assert(isInline != null); final isProtected = valueNode.getAttributeBool(KdbxXml.ATTR_PROTECTED); final isCompressed = valueNode.getAttributeBool(KdbxXml.ATTR_COMPRESSED); @@ -58,8 +58,8 @@ class KdbxBinary { } void saveToXml(XmlElement valueNode) { - final content = base64.encode(gzip.encode(value)); - valueNode.addAttributeBool(KdbxXml.ATTR_PROTECTED, isProtected); + final content = base64.encode(gzip.encode(value!)); + valueNode.addAttributeBool(KdbxXml.ATTR_PROTECTED, isProtected!); valueNode.addAttributeBool(KdbxXml.ATTR_COMPRESSED, true); valueNode.children.add(XmlText(content)); } diff --git a/lib/src/kdbx_custom_data.dart b/lib/src/kdbx_custom_data.dart index 77b2f74..d325b44 100644 --- a/lib/src/kdbx_custom_data.dart +++ b/lib/src/kdbx_custom_data.dart @@ -23,7 +23,7 @@ class KdbxCustomData extends KdbxNode { Iterable> get entries => _data.entries; - String operator [](String key) => _data[key]; + String? operator [](String key) => _data[key]; void operator []=(String key, String value) { modify(() => _data[key] = value); } diff --git a/lib/src/kdbx_dao.dart b/lib/src/kdbx_dao.dart index a3cc190..a0f8dac 100644 --- a/lib/src/kdbx_dao.dart +++ b/lib/src/kdbx_dao.dart @@ -8,8 +8,8 @@ import 'package:meta/meta.dart'; /// a kdbx file. extension KdbxDao on KdbxFile { KdbxGroup createGroup({ - @required KdbxGroup parent, - @required String name, + required KdbxGroup parent, + required String name, }) { assert(parent != null, name != null); final newGroup = KdbxGroup.create(ctx: ctx, parent: parent, name: name); @@ -17,10 +17,10 @@ extension KdbxDao on KdbxFile { return newGroup; } - KdbxGroup findGroupByUuid(KdbxUuid uuid) => + KdbxGroup findGroupByUuid(KdbxUuid? uuid) => body.rootGroup.getAllGroups().firstWhere((group) => group.uuid == uuid, - orElse: () => - throw StateError('Unable to find group with uuid $uuid')); + orElse: (() => + throw StateError('Unable to find group with uuid $uuid')) as KdbxGroup Function()?); void deleteGroup(KdbxGroup group) { move(group, getRecycleBinOrCreate()); @@ -34,11 +34,11 @@ extension KdbxDao on KdbxFile { assert(toGroup != null); kdbxObject.times.locationChanged.setToNow(); if (kdbxObject is KdbxGroup) { - kdbxObject.parent.internalRemoveGroup(kdbxObject); + kdbxObject.parent!.internalRemoveGroup(kdbxObject); kdbxObject.internalChangeParent(toGroup); toGroup.addGroup(kdbxObject); } else if (kdbxObject is KdbxEntry) { - kdbxObject.parent.internalRemoveEntry(kdbxObject); + kdbxObject.parent!.internalRemoveEntry(kdbxObject); kdbxObject.internalChangeParent(toGroup); toGroup.addEntry(kdbxObject); } diff --git a/lib/src/kdbx_deleted_object.dart b/lib/src/kdbx_deleted_object.dart index b88ed98..37f355f 100644 --- a/lib/src/kdbx_deleted_object.dart +++ b/lib/src/kdbx_deleted_object.dart @@ -4,7 +4,7 @@ import 'package:kdbx/src/kdbx_xml.dart'; import 'package:xml/xml.dart'; class KdbxDeletedObject extends KdbxNode implements KdbxNodeContext { - KdbxDeletedObject.create(this.ctx, KdbxUuid uuid) : super.create(NODE_NAME) { + KdbxDeletedObject.create(this.ctx, KdbxUuid? uuid) : super.create(NODE_NAME) { _uuid.set(uuid); deletionTime.setToNow(); } @@ -16,7 +16,7 @@ class KdbxDeletedObject extends KdbxNode implements KdbxNodeContext { @override final KdbxReadWriteContext ctx; - KdbxUuid get uuid => _uuid.get(); + KdbxUuid? get uuid => _uuid.get(); UuidNode get _uuid => UuidNode(this, KdbxXml.NODE_UUID); DateTimeUtcNode get deletionTime => DateTimeUtcNode(this, 'DeletionTime'); } diff --git a/lib/src/kdbx_entry.dart b/lib/src/kdbx_entry.dart index 5d2f85e..276dc29 100644 --- a/lib/src/kdbx_entry.dart +++ b/lib/src/kdbx_entry.dart @@ -1,5 +1,6 @@ import 'dart:typed_data'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:kdbx/src/crypto/protected_value.dart'; import 'package:kdbx/src/internal/extension_utils.dart'; import 'package:kdbx/src/kdbx_binary.dart'; @@ -11,7 +12,6 @@ import 'package:kdbx/src/kdbx_header.dart'; import 'package:kdbx/src/kdbx_object.dart'; import 'package:kdbx/src/kdbx_xml.dart'; import 'package:logging/logging.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; import 'package:quiver/check.dart'; import 'package:xml/xml.dart'; @@ -76,7 +76,7 @@ class KdbxKey { extension KdbxEntryInternal on KdbxEntry { KdbxEntry cloneInto(KdbxGroup otherGroup, {bool toHistoryEntry = false}) => KdbxEntry.create( - otherGroup.file, + otherGroup.file!, otherGroup, isHistoryEntry: toHistoryEntry, ) @@ -130,12 +130,12 @@ extension KdbxEntryInternal on KdbxEntry { times.overwriteFrom(other.times); if (includeHistory) { for (final historyEntry in other.history) { - history.add(historyEntry.cloneInto(parent, toHistoryEntry: false)); + history.add(historyEntry.cloneInto(parent!, toHistoryEntry: false)); } } } - List _diffMap(Map a, Map b) { + List _diffMap(Map a, Map b) { final keys = {...a.keys, ...b.keys}; final ret = []; for (final key in keys) { @@ -161,7 +161,7 @@ class KdbxEntry extends KdbxObject { icon.set(KdbxIcon.Key); } - KdbxEntry.read(KdbxReadWriteContext ctx, KdbxGroup parent, XmlElement node, + KdbxEntry.read(KdbxReadWriteContext ctx, KdbxGroup? parent, XmlElement node, {this.isHistoryEntry = false}) : history = [], super.read(ctx, parent, node) { @@ -195,9 +195,9 @@ class KdbxEntry extends KdbxObject { .findElements(KdbxXml.NODE_HISTORY) .singleOrNull ?.findElements('Entry') - ?.map((entry) => + .map((entry) => KdbxEntry.read(ctx, parent, entry, isHistoryEntry: true)) - ?.toList() ?? + .toList() ?? []); } @@ -211,7 +211,7 @@ class KdbxEntry extends KdbxObject { StringNode get tags => StringNode(this, 'Tags'); @override - set file(KdbxFile file) { + set file(KdbxFile? file) { super.file = file; // TODO this looks like some weird workaround, get rid of the // `file` reference. @@ -243,9 +243,9 @@ class KdbxEntry extends KdbxObject { value.attributes.add( XmlAttribute(XmlName(KdbxXml.ATTR_PROTECTED), KdbxXml.VALUE_TRUE)); KdbxFile.setProtectedValueForNode( - value, stringEntry.value as ProtectedValue); + value, stringEntry.value as ProtectedValue?); } else if (stringEntry.value is StringValue) { - value.children.add(XmlText(stringEntry.value.getText())); + value.children.add(XmlText(stringEntry.value!.getText()!)); } return XmlElement(XmlName(KdbxXml.NODE_STRING)) ..children.addAll([ @@ -258,7 +258,7 @@ class KdbxEntry extends KdbxObject { final key = binaryEntry.key; final binary = binaryEntry.value; final value = XmlElement(XmlName(KdbxXml.NODE_VALUE)); - if (binary.isInline) { + if (binary.isInline!) { binary.saveToXml(value); } else { final binaryIndex = ctx.findBinaryId(binary); @@ -279,23 +279,23 @@ class KdbxEntry extends KdbxObject { return el; } - final Map _strings = {}; + final Map _strings = {}; final Map _binaries = {}; Iterable> get binaryEntries => _binaries.entries; - KdbxBinary getBinary(KdbxKey key) => _binaries[key]; + KdbxBinary? getBinary(KdbxKey key) => _binaries[key]; // Map get strings => UnmodifiableMapView(_strings); - Iterable> get stringEntries => + Iterable> get stringEntries => _strings.entries; - StringValue getString(KdbxKey key) => _strings[key]; + StringValue? getString(KdbxKey key) => _strings[key]; - void setString(KdbxKey key, StringValue value) { + void setString(KdbxKey key, StringValue? value) { assert(key != null); if (_strings[key] == value) { _logger.finest('Value did not change for $key'); @@ -318,7 +318,7 @@ class KdbxEntry extends KdbxObject { void removeString(KdbxKey key) => setString(key, null); - String _plainValue(KdbxKey key) { + String? _plainValue(KdbxKey key) { final value = _strings[key]; if (value is PlainValue) { return value.getText(); @@ -326,17 +326,17 @@ class KdbxEntry extends KdbxObject { return value?.toString(); } - String get label => + String? get label => _plainValue(KdbxKeyCommon.TITLE)?.takeUnlessBlank() ?? _plainValue(KdbxKeyCommon.URL)?.takeUnlessBlank(); - set label(String label) => setString(KdbxKeyCommon.TITLE, PlainValue(label)); + set label(String? label) => setString(KdbxKeyCommon.TITLE, PlainValue(label)); /// Creates a new binary and adds it to this entry. KdbxBinary createBinary({ - @required bool isProtected, - @required String name, - @required Uint8List bytes, + required bool isProtected, + required String name, + required Uint8List bytes, }) { assert(isProtected != null); assert(bytes != null); @@ -349,7 +349,7 @@ class KdbxEntry extends KdbxObject { value: bytes, ); modify(() { - file.ctx.addBinary(binary); + file!.ctx.addBinary(binary); _binaries[key] = binary; }); return binary; @@ -381,12 +381,10 @@ class KdbxEntry extends KdbxObject { throw StateError('Unable to find unique name for $fileName'); } - static KdbxEntry _findHistoryEntry( - List history, DateTime lastModificationTime) => - history.firstWhere( - (history) => - history.times.lastModificationTime.get() == lastModificationTime, - orElse: () => null); + static KdbxEntry? _findHistoryEntry( + List history, DateTime? lastModificationTime) => + history.firstWhereOrNull((history) => + history.times.lastModificationTime.get() == lastModificationTime); @override void merge(MergeContext mergeContext, KdbxEntry other) { @@ -403,7 +401,7 @@ class KdbxEntry extends KdbxObject { if (historyEntry == null) { // it seems like we don't know about that state, so we have to add // it to history. - history.add(other.cloneInto(parent, toHistoryEntry: true)); + history.add(other.cloneInto(parent!, toHistoryEntry: true)); } } else { _logger.finest('$this has no changes.'); @@ -418,13 +416,13 @@ class KdbxEntry extends KdbxObject { debug: 'merge in history ' '${otherHistoryEntry.times.lastModificationTime.get()}', ); - history.add(otherHistoryEntry.cloneInto(parent, toHistoryEntry: true)); + history.add(otherHistoryEntry.cloneInto(parent!, toHistoryEntry: true)); } } mergeContext.markAsMerged(this); } - String debugLabel() => label ?? _plainValue(KdbxKeyCommon.USER_NAME); + String? debugLabel() => label ?? _plainValue(KdbxKeyCommon.USER_NAME); @override String toString() { diff --git a/lib/src/kdbx_file.dart b/lib/src/kdbx_file.dart index 1292e54..078bcba 100644 --- a/lib/src/kdbx_file.dart +++ b/lib/src/kdbx_file.dart @@ -26,12 +26,12 @@ class KdbxFile { static final protectedValues = Expando(); - static ProtectedValue protectedValueForNode(xml.XmlElement node) { + static ProtectedValue? protectedValueForNode(xml.XmlElement node) { return protectedValues[node]; } static void setProtectedValueForNode( - xml.XmlElement node, ProtectedValue value) { + xml.XmlElement node, ProtectedValue? value) { protectedValues[node] = value; } @@ -77,10 +77,10 @@ class KdbxFile { _dirtyObjectsChanged.close(); } - CachedValue _recycleBin; + CachedValue? _recycleBin; /// Returns the recycle bin, if it exists, null otherwise. - KdbxGroup get recycleBin => (_recycleBin ??= _findRecycleBin()).value; + KdbxGroup? get recycleBin => (_recycleBin ??= _findRecycleBin()).value; CachedValue _findRecycleBin() { final uuid = body.meta.recycleBinUUID.get(); @@ -146,5 +146,5 @@ class CachedValue { CachedValue.withNull() : value = null; CachedValue.withValue(this.value) : assert(value != null); - final T value; + final T? value; } diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index a4ec630..670586a 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:archive/archive.dart'; import 'package:argon2_ffi_base/argon2_ffi_base.dart'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:convert/convert.dart' as convert; import 'package:crypto/crypto.dart' as crypto; import 'package:kdbx/kdbx.dart'; @@ -36,7 +37,7 @@ final _logger = Logger('kdbx.format'); abstract class Credentials { factory Credentials(ProtectedValue password) => Credentials.composite(password, null); //PasswordCredentials(password); - factory Credentials.composite(ProtectedValue password, Uint8List keyFile) => + factory Credentials.composite(ProtectedValue password, Uint8List? keyFile) => KeyFileComposite( password: password == null ? null : PasswordCredentials(password), keyFile: keyFile == null ? null : KeyFileCredentials(keyFile), @@ -48,10 +49,10 @@ abstract class Credentials { } class KeyFileComposite implements Credentials { - KeyFileComposite({@required this.password, @required this.keyFile}); + KeyFileComposite({required this.password, required this.keyFile}); - PasswordCredentials password; - KeyFileCredentials keyFile; + PasswordCredentials? password; + KeyFileCredentials? keyFile; @override Uint8List getHash() { @@ -70,8 +71,8 @@ class KeyFileComposite implements Credentials { /// Context used during reading and writing. class KdbxReadWriteContext { KdbxReadWriteContext({ - @required List binaries, - @required this.header, + required List binaries, + required this.header, }) : assert(binaries != null), assert(header != null), _binaries = binaries, @@ -80,7 +81,7 @@ class KdbxReadWriteContext { static final kdbxContext = Expando(); static KdbxReadWriteContext kdbxContextForNode(xml.XmlNode node) { - final ret = kdbxContext[node.document]; + final ret = kdbxContext[node.document!]; if (ret == null) { throw StateError('Unable to locate kdbx context for document.'); } @@ -89,7 +90,7 @@ class KdbxReadWriteContext { static void setKdbxContextForNode( xml.XmlNode node, KdbxReadWriteContext ctx) { - kdbxContext[node.document] = ctx; + kdbxContext[node.document!] = ctx; } // TODO make [_binaries] and [_deletedObjects] late init :-) @@ -109,7 +110,7 @@ class KdbxReadWriteContext { _deletedObjects.addAll(deletedObjects); } - KdbxBinary binaryById(int id) { + KdbxBinary? binaryById(int id) { if (id >= _binaries.length) { return null; } @@ -120,21 +121,20 @@ class KdbxReadWriteContext { _binaries.add(binary); } - KdbxBinary findBinaryByValue(KdbxBinary binary) { + KdbxBinary? findBinaryByValue(KdbxBinary binary) { // TODO create a hashset or map? - return _binaries.firstWhere((element) => element.valueEqual(binary), - orElse: () => null); + return _binaries.firstWhereOrNull((element) => element.valueEqual(binary)); } /// finds the ID of the given binary. /// if it can't be found, [KdbxCorruptedFileException] is thrown. int findBinaryId(KdbxBinary binary) { assert(binary != null); - assert(!binary.isInline); + assert(!binary.isInline!); final id = _binaries.indexOf(binary); if (id < 0) { throw KdbxCorruptedFileException('Unable to find binary.' - ' (${binary.value.length},${binary.isInline})'); + ' (${binary.value!.length},${binary.isInline})'); } return id; } @@ -244,7 +244,7 @@ class KdbxBody extends KdbxNode { final xmlBytes = utf8.encode(xml.toXmlString()); final compressedBytes = (kdbxFile.header.compression == Compression.gzip ? KdbxFormat._gzipEncode(xmlBytes as Uint8List) - : xmlBytes) as Uint8List; + : xmlBytes) as Uint8List?; final encrypted = await _encryptV3(kdbxFile, compressedBytes); writer.writeBytes(encrypted); @@ -271,34 +271,34 @@ class KdbxBody extends KdbxNode { } Future _encryptV3( - KdbxFile kdbxFile, Uint8List compressedBytes) async { + KdbxFile kdbxFile, Uint8List? compressedBytes) async { final byteWriter = WriterHelper(); byteWriter.writeBytes( - kdbxFile.header.fields[HeaderFields.StreamStartBytes].bytes); + kdbxFile.header.fields[HeaderFields.StreamStartBytes]!.bytes!); HashedBlockReader.writeBlocks(ReaderHelper(compressedBytes), byteWriter); final bytes = byteWriter.output.toBytes(); final masterKey = await KdbxFormat._generateMasterKeyV3( kdbxFile.header, kdbxFile.credentials); final encrypted = KdbxFormat._encryptDataAes(masterKey, bytes, - kdbxFile.header.fields[HeaderFields.EncryptionIV].bytes); + kdbxFile.header.fields[HeaderFields.EncryptionIV]!.bytes!); return encrypted; } Uint8List _encryptV4( - KdbxFile kdbxFile, Uint8List compressedBytes, Uint8List cipherKey) { + KdbxFile kdbxFile, Uint8List? compressedBytes, Uint8List cipherKey) { final header = kdbxFile.header; final cipher = header.cipher; if (cipher == Cipher.aes) { _logger.fine('We need AES'); final result = kdbxFile.kdbxFormat - ._encryptContentV4Aes(header, cipherKey, compressedBytes); + ._encryptContentV4Aes(header, cipherKey, compressedBytes!); // _logger.fine('Result: ${ByteUtils.toHexList(result)}'); return result; } else if (cipher == Cipher.chaCha20) { _logger.fine('We need chacha20'); return kdbxFile.kdbxFormat - .transformContentV4ChaCha20(header, compressedBytes, cipherKey); + .transformContentV4ChaCha20(header, compressedBytes!, cipherKey); } else { throw UnsupportedError('Unsupported cipherId $cipher'); } @@ -314,7 +314,7 @@ class KdbxBody extends KdbxNode { // sync deleted objects. final deleted = Map.fromEntries(ctx._deletedObjects.map((e) => MapEntry(e.uuid, e))); - final incomingDeleted = {}; + final incomingDeleted = {}; for (final obj in other.ctx._deletedObjects) { if (!deleted.containsKey(obj.uuid)) { @@ -335,7 +335,7 @@ class KdbxBody extends KdbxNode { if (ctx.findBinaryByValue(binary) == null) { ctx.addBinary(binary); mergeContext.trackChange(this, - debug: 'adding new binary ${binary.value.length}'); + debug: 'adding new binary ${binary.value!.length}'); } } meta.merge(other.meta); @@ -343,7 +343,7 @@ class KdbxBody extends KdbxNode { // remove deleted objects for (final incomingDelete in incomingDeleted.values) { - final object = mergeContext.objectIndex[incomingDelete.uuid]; + final object = mergeContext.objectIndex![incomingDelete.uuid!]; mergeContext.trackChange(object, debug: 'was deleted.'); } @@ -352,11 +352,11 @@ class KdbxBody extends KdbxNode { _logger.info('Finished merging:\n${mergeContext.debugChanges()}'); final incomingObjects = other._createObjectIndex(); _logger.info('Merged: ${mergeContext.merged} vs. ' - '(local objects: ${mergeContext.objectIndex.length}, ' + '(local objects: ${mergeContext.objectIndex!.length}, ' 'incoming objects: ${incomingObjects.length})'); // sanity checks - if (mergeContext.merged.keys.length != mergeContext.objectIndex.length) { + if (mergeContext.merged.keys.length != mergeContext.objectIndex!.length) { // TODO figure out what went wrong. } return mergeContext; @@ -411,23 +411,23 @@ class KdbxBody extends KdbxNode { abstract class OverwriteContext { const OverwriteContext(); static const noop = OverwriteContextNoop(); - void trackChange(KdbxObject object, {String node, String debug}); + void trackChange(KdbxObject object, {String? node, String? debug}); } class OverwriteContextNoop implements OverwriteContext { const OverwriteContextNoop(); @override - void trackChange(KdbxObject object, {String node, String debug}) {} + void trackChange(KdbxObject object, {String? node, String? debug}) {} } class MergeChange { MergeChange({this.object, this.node, this.debug}); - final KdbxNode object; + final KdbxNode? object; /// the name of the subnode of [object]. - final String node; - final String debug; + final String? node; + final String? debug; String debugString() { return [node, debug].where((e) => e != null).join(' '); @@ -436,8 +436,8 @@ class MergeChange { class MergeContext implements OverwriteContext { MergeContext({this.objectIndex, this.deletedObjects}); - final Map objectIndex; - final Map deletedObjects; + final Map? objectIndex; + final Map? deletedObjects; final Map merged = {}; final List changes = []; @@ -450,7 +450,7 @@ class MergeContext implements OverwriteContext { } @override - void trackChange(KdbxNode object, {String node, String debug}) { + void trackChange(KdbxNode? object, {String? node, String? debug}) { changes.add(MergeChange( object: object, node: node, @@ -480,7 +480,7 @@ class _KeysV4 { class KdbxFormat { KdbxFormat([this.argon2]) : assert(kdbxKeyCommonAssertConsistency()); - final Argon2 argon2; + final Argon2? argon2; static bool dartWebWorkaround = false; /// Creates a new, empty [KdbxFile] with default settings. @@ -488,8 +488,8 @@ class KdbxFormat { KdbxFile create( Credentials credentials, String name, { - String generator, - KdbxHeader header, + String? generator, + KdbxHeader? header, }) { header ??= argon2 == null ? KdbxHeader.createV3() : KdbxHeader.createV4(); final ctx = KdbxReadWriteContext(binaries: [], header: header); @@ -546,7 +546,7 @@ class KdbxFormat { throw UnsupportedError('Unsupported version ${header.version}'); } else if (file.header.version < KdbxVersion.V4) { final streamKey = - file.header.fields[HeaderFields.ProtectedStreamKey].bytes; + file.header.fields[HeaderFields.ProtectedStreamKey]!.bytes!; final gen = ProtectedSaltGenerator(streamKey); body.meta.headerHash.set(headerHash.buffer); @@ -712,7 +712,7 @@ class KdbxFormat { Uint8List transformContentV4ChaCha20( KdbxHeader header, Uint8List encrypted, Uint8List cipherKey) { - final encryptionIv = header.fields[HeaderFields.EncryptionIV].bytes; + final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; final chaCha = ChaCha7539Engine() ..init(true, ParametersWithIV(KeyParameter(cipherKey), encryptionIv)); return chaCha.process(encrypted); @@ -735,7 +735,7 @@ class KdbxFormat { Future<_KeysV4> _computeKeysV4( KdbxHeader header, Credentials credentials) async { - final masterSeed = header.fields[HeaderFields.MasterSeed].bytes; + final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes!; final kdfParameters = header.readKdfParameters; if (masterSeed.length != 32) { throw const FormatException('Master seed must be 32 bytes.'); @@ -743,7 +743,7 @@ class KdbxFormat { final credentialHash = credentials.getHash(); final key = - await KeyEncrypterKdf(argon2).encrypt(credentialHash, kdfParameters); + await KeyEncrypterKdf(argon2!).encrypt(credentialHash, kdfParameters); // final keyWithSeed = Uint8List(65); // keyWithSeed.replaceRange(0, masterSeed.length, masterSeed); @@ -762,9 +762,9 @@ class KdbxFormat { final protectedValueEncryption = header.innerRandomStreamEncryption; final streamKey = header.protectedStreamKey; if (protectedValueEncryption == ProtectedValueEncryption.salsa20) { - return ProtectedSaltGenerator(streamKey); + return ProtectedSaltGenerator(streamKey!); } else if (protectedValueEncryption == ProtectedValueEncryption.chaCha20) { - return ProtectedSaltGenerator.chacha20(streamKey); + return ProtectedSaltGenerator.chacha20(streamKey!); } else { throw KdbxUnsupportedException( 'Inner encryption: $protectedValueEncryption'); @@ -789,7 +789,7 @@ class KdbxFormat { KdbxFile.protectedValues[el] = ProtectedValue.fromString(pw); } catch (e, stackTrace) { final stringKey = - el.parentElement.singleElement(KdbxXml.NODE_KEY)?.text; + el.parentElement!.singleElement(KdbxXml.NODE_KEY)?.text; final uuid = el.parentElement?.parentElement ?.singleElement(KdbxXml.NODE_UUID) ?.text; @@ -811,14 +811,14 @@ class KdbxFormat { final kdbxMeta = KdbxMeta.read(meta, ctx); // kdbx < 4 has binaries in the meta section, >= 4 in the binary header. final binaries = kdbxMeta.binaries?.isNotEmpty == true - ? kdbxMeta.binaries + ? kdbxMeta.binaries! : header.innerHeader.binaries .map((e) => KdbxBinary.readBinaryInnerHeader(e)); final deletedObjects = root .findElements(KdbxXml.NODE_DELETED_OBJECTS) .singleOrNull - ?.let((el) => el + ?.let((el) => el! .findElements(KdbxDeletedObject.NODE_NAME) .map((node) => KdbxDeletedObject.read(node, ctx))) ?? []; @@ -832,14 +832,14 @@ class KdbxFormat { Uint8List _decryptContent( KdbxHeader header, Uint8List masterKey, Uint8List encryptedPayload) { - final encryptionIv = header.fields[HeaderFields.EncryptionIV].bytes; + final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; final decryptCipher = CBCBlockCipher(AESFastEngine()); decryptCipher.init( false, ParametersWithIV(KeyParameter(masterKey), encryptionIv)); final paddedDecrypted = AesHelper.processBlocks(decryptCipher, encryptedPayload); - final streamStart = header.fields[HeaderFields.StreamStartBytes].bytes; + final streamStart = header.fields[HeaderFields.StreamStartBytes]!.bytes!; if (paddedDecrypted.lengthInBytes < streamStart.lengthInBytes) { _logger.warning( @@ -861,7 +861,7 @@ class KdbxFormat { Uint8List _decryptContentV4( KdbxHeader header, Uint8List cipherKey, Uint8List encryptedPayload) { - final encryptionIv = header.fields[HeaderFields.EncryptionIV].bytes; + final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; final decryptCipher = CBCBlockCipher(AESFastEngine()); decryptCipher.init( @@ -876,7 +876,7 @@ class KdbxFormat { /// TODO combine this with [_decryptContentV4] (or [_encryptDataAes]?) Uint8List _encryptContentV4Aes( KdbxHeader header, Uint8List cipherKey, Uint8List bytes) { - final encryptionIv = header.fields[HeaderFields.EncryptionIV].bytes; + final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; final encryptCypher = CBCBlockCipher(AESFastEngine()); encryptCypher.init( true, ParametersWithIV(KeyParameter(cipherKey), encryptionIv)); @@ -887,10 +887,10 @@ class KdbxFormat { static Future _generateMasterKeyV3( KdbxHeader header, Credentials credentials) async { final rounds = header.v3KdfTransformRounds; - final seed = header.fields[HeaderFields.TransformSeed].bytes; - final masterSeed = header.fields[HeaderFields.MasterSeed].bytes; + final seed = header.fields[HeaderFields.TransformSeed]!.bytes; + final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes!; _logger.finer( - 'Rounds: $rounds (${ByteUtils.toHexList(header.fields[HeaderFields.TransformRounds].bytes)})'); + 'Rounds: $rounds (${ByteUtils.toHexList(header.fields[HeaderFields.TransformRounds]!.bytes)})'); final transformedKey = await KeyEncrypterKdf.encryptAesAsync( EncryptAesArgs(seed, credentials.getHash(), rounds)); @@ -909,9 +909,9 @@ class KdbxFormat { encryptCipher, AesHelper.pad(payload, encryptCipher.blockSize)); } - static Uint8List _gzipEncode(Uint8List bytes) { + static Uint8List? _gzipEncode(Uint8List bytes) { if (dartWebWorkaround) { - return GZipEncoder().encode(bytes) as Uint8List; + return GZipEncoder().encode(bytes) as Uint8List?; } return GZipCodec().encode(bytes) as Uint8List; } diff --git a/lib/src/kdbx_group.dart b/lib/src/kdbx_group.dart index 3531cbb..23b7835 100644 --- a/lib/src/kdbx_group.dart +++ b/lib/src/kdbx_group.dart @@ -14,9 +14,9 @@ final _logger = Logger('kdbx_group'); class KdbxGroup extends KdbxObject { KdbxGroup.create({ - @required KdbxReadWriteContext ctx, - @required KdbxGroup parent, - @required String name, + required KdbxReadWriteContext ctx, + required KdbxGroup? parent, + required String? name, }) : super.create( ctx, parent?.file, @@ -28,7 +28,7 @@ class KdbxGroup extends KdbxObject { expanded.set(true); } - KdbxGroup.read(KdbxReadWriteContext ctx, KdbxGroup parent, XmlElement node) + KdbxGroup.read(KdbxReadWriteContext ctx, KdbxGroup? parent, XmlElement node) : super.read(ctx, parent, node) { node .findElements(KdbxXml.NODE_GROUP) @@ -131,7 +131,7 @@ class KdbxGroup extends KdbxObject { void _mergeSubObjects( MergeContext mergeContext, List me, List other, - {@required T Function(T obj) importToHere}) { + {required T Function(T obj) importToHere}) { // possibilities: // 1. Not changed at all 👍 // 2. Deleted in other @@ -146,7 +146,7 @@ class KdbxGroup extends KdbxObject { if (meObj == null) { // moved or deleted. - final movedObj = mergeContext.objectIndex[otherObj.uuid]; + final movedObj = mergeContext.objectIndex![otherObj.uuid]; if (movedObj == null) { // item was created in the other file. we have to import it final newMeObject = importToHere(otherObj); @@ -156,7 +156,7 @@ class KdbxGroup extends KdbxObject { // item was moved. if (otherObj.wasMovedAfter(movedObj)) { // item was moved in the other file, so we have to move it here. - file.move(movedObj, this); + file!.move(movedObj, this); mergeContext.trackChange(movedObj, debug: 'moved to another group'); } else { // item was moved in this file, so nothing to do. diff --git a/lib/src/kdbx_header.dart b/lib/src/kdbx_header.dart index e4fbba8..4d90840 100644 --- a/lib/src/kdbx_header.dart +++ b/lib/src/kdbx_header.dart @@ -42,7 +42,7 @@ final _compressionIdsById = _compressionIds.map((key, value) => MapEntry(value, key)); extension on Compression { - int get id => _compressionIds[this]; + int? get id => _compressionIds[this]; } /// how protected values are encrypted in the xml. @@ -134,7 +134,7 @@ class HeaderField implements HeaderFieldBase { @override final HeaderFields field; - final Uint8List bytes; + final Uint8List? bytes; String get name => field.toString(); } @@ -144,19 +144,19 @@ class InnerHeaderField implements HeaderFieldBase { @override final InnerHeaderFields field; - final Uint8List bytes; + final Uint8List? bytes; String get name => field.toString(); } class KdbxHeader { KdbxHeader({ - @required this.sig1, - @required this.sig2, - @required KdbxVersion version, - @required this.fields, - @required this.endPos, - Map innerFields, + required this.sig1, + required this.sig2, + required KdbxVersion version, + required this.fields, + required this.endPos, + Map? innerFields, }) : _version = version, innerHeader = InnerHeader(fields: innerFields ?? {}); @@ -322,10 +322,10 @@ class KdbxHeader { void _writeInnerField(WriterHelper writer, InnerHeaderField value) { final field = value.field; _logger.finer( - 'Writing header $field (${field.index}) (${value.bytes.lengthInBytes})'); + 'Writing header $field (${field.index}) (${value.bytes!.lengthInBytes})'); writer.writeUint8(field.index); - _writeFieldSize(writer, value.bytes.lengthInBytes); - writer.writeBytes(value.bytes); + _writeFieldSize(writer, value.bytes!.lengthInBytes); + writer.writeBytes(value.bytes!); } void _writeField(WriterHelper writer, HeaderFields field) { @@ -333,10 +333,10 @@ class KdbxHeader { if (value == null) { return; } - _logger.finer('Writing header $field (${value.bytes.lengthInBytes})'); + _logger.finer('Writing header $field (${value.bytes!.lengthInBytes})'); writer.writeUint8(field.index); - _writeFieldSize(writer, value.bytes.lengthInBytes); - writer.writeBytes(value.bytes); + _writeFieldSize(writer, value.bytes!.lengthInBytes); + writer.writeBytes(value.bytes!); } void _writeFieldSize(WriterHelper writer, int size) { @@ -348,9 +348,9 @@ class KdbxHeader { } static Map _defaultFieldValues() => _headerFields({ - HeaderFields.CipherID: CryptoConsts.CIPHER_IDS[Cipher.aes].toBytes(), + HeaderFields.CipherID: CryptoConsts.CIPHER_IDS[Cipher.aes]!.toBytes(), HeaderFields.CompressionFlags: - WriterHelper.singleUint32Bytes(Compression.gzip.id), + WriterHelper.singleUint32Bytes(Compression.gzip.id!), HeaderFields.TransformRounds: WriterHelper.singleUint64Bytes(6000), HeaderFields.InnerRandomStreamID: WriterHelper.singleUint32Bytes( ProtectedValueEncryption.values @@ -359,9 +359,9 @@ class KdbxHeader { static Map _defaultFieldValuesV4() => _headerFields({ - HeaderFields.CipherID: CryptoConsts.CIPHER_IDS[Cipher.aes].toBytes(), + HeaderFields.CipherID: CryptoConsts.CIPHER_IDS[Cipher.aes]!.toBytes(), HeaderFields.CompressionFlags: - WriterHelper.singleUint32Bytes(Compression.gzip.id), + WriterHelper.singleUint32Bytes(Compression.gzip.id!), HeaderFields.KdfParameters: _createKdfDefaultParameters().write(), // HeaderFields.InnerRandomStreamID: WriterHelper.singleUint32Bytes( // ProtectedValueEncryption.values @@ -428,7 +428,7 @@ class KdbxHeader { ReaderHelper reader, KdbxVersion version, List fields, - T Function(TE field, Uint8List bytes) createField) => + T Function(TE field, Uint8List? bytes) createField) => Map.fromEntries(readField(reader, version, fields, createField) .map((field) => MapEntry(field.field, field))); @@ -436,7 +436,7 @@ class KdbxHeader { ReaderHelper reader, KdbxVersion version, List fields, - T Function(TE field, Uint8List bytes) createField) sync* { + T Function(TE field, Uint8List? bytes) createField) sync* { while (true) { final headerId = reader.readUint8(); final bodySize = @@ -472,17 +472,17 @@ class KdbxHeader { final InnerHeader innerHeader; /// end position of the header, if we have been reading from a stream. - final int endPos; + final int? endPos; - Cipher get cipher { + Cipher? get cipher { if (version < KdbxVersion.V4) { assert( - CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID].bytes) == + CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID]!.bytes!) == Cipher.aes); return Cipher.aes; } try { - return CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID].bytes); + return CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID]!.bytes!); } catch (e, stackTrace) { _logger.warning( 'Unable to find cipher. ' @@ -496,39 +496,39 @@ class KdbxHeader { } } - set cipher(Cipher cipher) { + set cipher(Cipher? cipher) { checkArgument(version >= KdbxVersion.V4 || cipher == Cipher.aes, message: 'Kdbx 3 only supports aes, tried to set it to $cipher'); _setHeaderField( HeaderFields.CipherID, - CryptoConsts.CIPHER_IDS[cipher].toBytes(), + CryptoConsts.CIPHER_IDS[cipher!]!.toBytes(), ); } Compression get compression { final id = - ReaderHelper.singleUint32(fields[HeaderFields.CompressionFlags].bytes); + ReaderHelper.singleUint32(fields[HeaderFields.CompressionFlags]!.bytes); return _compressionIdsById[id] ?? - (() => throw KdbxUnsupportedException('invalid compression $id'))(); + (() => throw KdbxUnsupportedException('invalid compression $id'))()!; } ProtectedValueEncryption get innerRandomStreamEncryption => ProtectedValueEncryption .values[ReaderHelper.singleUint32(_innerRandomStreamEncryptionBytes)]; - Uint8List get _innerRandomStreamEncryptionBytes => version >= KdbxVersion.V4 - ? innerHeader.fields[InnerHeaderFields.InnerRandomStreamID].bytes - : fields[HeaderFields.InnerRandomStreamID].bytes; + Uint8List? get _innerRandomStreamEncryptionBytes => version >= KdbxVersion.V4 + ? innerHeader.fields[InnerHeaderFields.InnerRandomStreamID]!.bytes + : fields[HeaderFields.InnerRandomStreamID]!.bytes; - Uint8List get protectedStreamKey => version >= KdbxVersion.V4 - ? innerHeader.fields[InnerHeaderFields.InnerRandomStreamKey].bytes - : fields[HeaderFields.ProtectedStreamKey].bytes; + Uint8List? get protectedStreamKey => version >= KdbxVersion.V4 + ? innerHeader.fields[InnerHeaderFields.InnerRandomStreamKey]!.bytes + : fields[HeaderFields.ProtectedStreamKey]!.bytes; VarDictionary get readKdfParameters => VarDictionary.read( - ReaderHelper(fields[HeaderFields.KdfParameters].bytes)); + ReaderHelper(fields[HeaderFields.KdfParameters]!.bytes)); int get v3KdfTransformRounds => - ReaderHelper.singleUint64(fields[HeaderFields.TransformRounds].bytes); + ReaderHelper.singleUint64(fields[HeaderFields.TransformRounds]!.bytes); void writeKdfParameters(VarDictionary kdfParameters) => _setHeaderField(HeaderFields.KdfParameters, kdfParameters.write()); @@ -563,7 +563,7 @@ class KdbxInvalidKeyException implements KdbxException {} class KdbxCorruptedFileException implements KdbxException { KdbxCorruptedFileException([this.message]); - final String message; + final String? message; @override String toString() { @@ -636,8 +636,8 @@ class HashedBlockReader { class InnerHeader { InnerHeader({ - @required this.fields, - List binaries, + required this.fields, + List? binaries, }) : binaries = binaries ?? [], assert(fields != null); diff --git a/lib/src/kdbx_meta.dart b/lib/src/kdbx_meta.dart index 02e4dac..b53d26c 100644 --- a/lib/src/kdbx_meta.dart +++ b/lib/src/kdbx_meta.dart @@ -20,9 +20,9 @@ final _logger = Logger('kdbx_meta'); class KdbxMeta extends KdbxNode implements KdbxNodeContext { KdbxMeta.create({ - @required String databaseName, - @required this.ctx, - String generator, + required String databaseName, + required this.ctx, + String? generator, }) : customData = KdbxCustomData.create(), binaries = [], _customIcons = {}, @@ -41,13 +41,13 @@ class KdbxMeta extends KdbxNode implements KdbxNodeContext { KdbxMeta.read(xml.XmlElement node, this.ctx) : customData = node .singleElement('CustomData') - ?.let((e) => KdbxCustomData.read(e)) ?? + ?.let((e) => KdbxCustomData.read(e!)) ?? KdbxCustomData.create(), 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)); + 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), @@ -56,7 +56,7 @@ class KdbxMeta extends KdbxNode implements KdbxNodeContext { }) ?.toList() ?.let((binaries) { - binaries.sort((a, b) => a.key.compareTo(b.key)); + binaries!.sort((a, b) => a.key.compareTo(b.key)); for (var i = 0; i < binaries.length; i++) { if (i != binaries[i].key) { throw KdbxCorruptedFileException( @@ -69,7 +69,7 @@ class KdbxMeta extends KdbxNode implements KdbxNodeContext { _customIcons = node .singleElement(KdbxXml.NODE_CUSTOM_ICONS) ?.let((el) sync* { - for (final iconNode in el.findElements(KdbxXml.NODE_ICON)) { + for (final iconNode in el!.findElements(KdbxXml.NODE_ICON)) { yield KdbxCustomIcon( uuid: KdbxUuid( iconNode.singleTextNode(KdbxXml.NODE_UUID)), @@ -78,7 +78,7 @@ class KdbxMeta extends KdbxNode implements KdbxNodeContext { } }) ?.map((e) => MapEntry(e.uuid, e)) - ?.let((that) => Map.fromEntries(that)) ?? + ?.let((that) => Map.fromEntries(that!)) ?? {}, super.read(node); @@ -88,11 +88,11 @@ class KdbxMeta extends KdbxNode implements KdbxNodeContext { final KdbxCustomData customData; /// only used in Kdbx 3 - final List binaries; + final List? binaries; - final Map _customIcons; + final Map _customIcons; - Map get customIcons => + Map get customIcons => UnmodifiableMapView(_customIcons); void addCustomIcon(KdbxCustomIcon customIcon) { @@ -173,8 +173,8 @@ class KdbxMeta extends KdbxNode implements KdbxNodeContext { XmlElement(XmlName(KdbxXml.NODE_CUSTOM_ICONS)) ..children.addAll(customIcons.values.map( (e) => XmlUtils.createNode(KdbxXml.NODE_ICON, [ - XmlUtils.createTextNode(KdbxXml.NODE_UUID, e.uuid.uuid), - XmlUtils.createTextNode(KdbxXml.NODE_DATA, base64.encode(e.data)) + XmlUtils.createTextNode(KdbxXml.NODE_UUID, e.uuid!.uuid), + XmlUtils.createTextNode(KdbxXml.NODE_DATA, base64.encode(e.data!)) ]), )), ); @@ -227,8 +227,8 @@ class KdbxCustomIcon { KdbxCustomIcon({this.uuid, this.data}); /// uuid of the icon, must be unique within each file. - final KdbxUuid uuid; + final KdbxUuid? uuid; /// Encoded png data of the image. will be base64 encoded into the kdbx file. - final Uint8List data; + final Uint8List? data; } diff --git a/lib/src/kdbx_object.dart b/lib/src/kdbx_object.dart index 6f42c4c..3d9e21c 100644 --- a/lib/src/kdbx_object.dart +++ b/lib/src/kdbx_object.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:kdbx/src/internal/extension_utils.dart'; import 'package:kdbx/src/kdbx_file.dart'; import 'package:kdbx/src/kdbx_format.dart'; @@ -20,7 +21,7 @@ import 'package:xml/xml.dart'; final _logger = Logger('kdbx.kdbx_object'); class ChangeEvent { - ChangeEvent({this.object, this.isDirty}); + ChangeEvent({required this.object, required this.isDirty}); final T object; final bool isDirty; @@ -109,8 +110,8 @@ abstract class KdbxNode with Changeable { } extension IterableKdbxObject on Iterable { - T findByUuid(KdbxUuid uuid) => - firstWhere((element) => element.uuid == uuid, orElse: () => null); + T? findByUuid(KdbxUuid uuid) => + firstWhereOrNull((element) => element.uuid == uuid); } extension KdbxObjectInternal on KdbxObject { @@ -149,7 +150,7 @@ abstract class KdbxObject extends KdbxNode { this.ctx, this.file, String nodeName, - KdbxGroup parent, + KdbxGroup? parent, ) : assert(ctx != null), times = KdbxTimes.create(ctx), _parent = parent, @@ -157,7 +158,7 @@ abstract class KdbxObject extends KdbxNode { _uuid.set(KdbxUuid.random()); } - KdbxObject.read(this.ctx, KdbxGroup parent, XmlElement node) + KdbxObject.read(this.ctx, KdbxGroup? parent, XmlElement node) : assert(ctx != null), times = KdbxTimes.read(node.findElements('Times').single, ctx), _parent = parent, @@ -165,13 +166,13 @@ abstract class KdbxObject extends KdbxNode { /// the file this object is part of. will be set AFTER loading, etc. /// TODO: We should probably get rid of this `file` reference. - KdbxFile file; + KdbxFile? file; final KdbxReadWriteContext ctx; final KdbxTimes times; - KdbxUuid get uuid => _uuid.get(); + KdbxUuid get uuid => _uuid.get()!; UuidNode get _uuid => UuidNode(this, KdbxXml.NODE_UUID); @@ -179,16 +180,16 @@ abstract class KdbxObject extends KdbxNode { UuidNode get customIconUuid => UuidNode(this, 'CustomIconUUID'); - KdbxGroup get parent => _parent; + KdbxGroup? get parent => _parent; - KdbxGroup _parent; + KdbxGroup? _parent; - KdbxCustomIcon get customIcon => - customIconUuid.get()?.let((uuid) => file.body.meta.customIcons[uuid]); + KdbxCustomIcon? get customIcon => + customIconUuid.get()?.let((uuid) => file!.body.meta.customIcons[uuid]); - set customIcon(KdbxCustomIcon icon) { + set customIcon(KdbxCustomIcon? icon) { if (icon != null) { - file.body.meta.addCustomIcon(icon); + file!.body.meta.addCustomIcon(icon); customIconUuid.set(icon.uuid); } else { customIconUuid.set(null); @@ -204,11 +205,11 @@ abstract class KdbxObject extends KdbxNode { } bool wasModifiedAfter(KdbxObject other) => times.lastModificationTime - .get() - .isAfter(other.times.lastModificationTime.get()); + .get()! + .isAfter(other.times.lastModificationTime.get()!); bool wasMovedAfter(KdbxObject other) => - times.locationChanged.get().isAfter(other.times.locationChanged.get()); + times.locationChanged.get()!.isAfter(other.times.locationChanged.get()!); @override XmlElement toXml() { diff --git a/lib/src/kdbx_var_dictionary.dart b/lib/src/kdbx_var_dictionary.dart index 30a443a..f6c85c8 100644 --- a/lib/src/kdbx_var_dictionary.dart +++ b/lib/src/kdbx_var_dictionary.dart @@ -18,7 +18,7 @@ class ValueType { const ValueType(this.code, this.decoder, [this.encoder]); final int code; final Decoder decoder; - final Encoder encoder; + final Encoder? encoder; static final typeUInt32 = ValueType( 0x04, @@ -70,7 +70,7 @@ class ValueType { ]; void encode(WriterHelper writer, T value) { - encoder(writer, value); + encoder!(writer, value); } } @@ -125,11 +125,11 @@ class VarDictionary { return writer.output.toBytes(); } - T get(ValueType type, String key) => _dict[key]?._value as T; + T? get(ValueType type, String key) => _dict[key]?._value as T?; void set(ValueType type, String key, T value) => _dict[key] = VarDictionaryItem(key, type, value); - static VarDictionaryItem _readItem(ReaderHelper reader) { + static VarDictionaryItem? _readItem(ReaderHelper reader) { final type = reader.readUint8(); if (type == 0) { return null; diff --git a/lib/src/kdbx_xml.dart b/lib/src/kdbx_xml.dart index bcee3f5..2185345 100644 --- a/lib/src/kdbx_xml.dart +++ b/lib/src/kdbx_xml.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:clock/clock.dart'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:kdbx/src/kdbx_format.dart'; import 'package:kdbx/src/kdbx_header.dart'; import 'package:kdbx/src/kdbx_object.dart'; @@ -80,27 +81,27 @@ extension on List { } } -abstract class KdbxSubTextNode extends KdbxSubNode { +abstract class KdbxSubTextNode extends KdbxSubNode { KdbxSubTextNode(KdbxNode node, String name) : super(node, name); - void Function() _onModify; + void Function()? _onModify; @protected - String encode(T value); + String? encode(T value); @protected T decode(String value); - XmlElement _opt(String nodeName) => node.node + XmlElement? _opt(String nodeName) => node.node .findElements(nodeName) - .singleWhere((x) => true, orElse: () => null); + .singleWhereOrNull((x) => true); void setOnModifyListener(void Function() onModify) { _onModify = onModify; } @override - T get() { + T? get() { final textValue = _opt(name)?.text; if (textValue == null) { return null; @@ -109,7 +110,7 @@ abstract class KdbxSubTextNode extends KdbxSubNode { } @override - bool set(T value, {bool force = false}) { + bool set(T? value, {bool force = false}) { if (get() == value && force != true) { return false; } @@ -140,24 +141,24 @@ abstract class KdbxSubTextNode extends KdbxSubNode { } } -class IntNode extends KdbxSubTextNode { +class IntNode extends KdbxSubTextNode { IntNode(KdbxNode node, String name) : super(node, name); @override - int decode(String value) => int.tryParse(value); + int? decode(String value) => int.tryParse(value); @override - String encode(int value) => value.toString(); + String encode(int? value) => value.toString(); } -class StringNode extends KdbxSubTextNode { +class StringNode extends KdbxSubTextNode { StringNode(KdbxNode node, String name) : super(node, name); @override String decode(String value) => value; @override - String encode(String value) => value; + String? encode(String? value) => value; } class Base64Node extends KdbxSubTextNode { @@ -170,21 +171,21 @@ class Base64Node extends KdbxSubTextNode { String encode(ByteBuffer value) => base64.encode(value.asUint8List()); } -class UuidNode extends KdbxSubTextNode { +class UuidNode extends KdbxSubTextNode { UuidNode(KdbxNode node, String name) : super(node, name); @override KdbxUuid decode(String value) => KdbxUuid(value); @override - String encode(KdbxUuid value) => value.uuid; + String encode(KdbxUuid? value) => value!.uuid; } class IconNode extends KdbxSubTextNode { IconNode(KdbxNode node, String name) : super(node, name); @override - KdbxIcon decode(String value) => KdbxIcon.values[int.tryParse(value)]; + KdbxIcon decode(String value) => KdbxIcon.values[int.tryParse(value)!]; @override String encode(KdbxIcon value) => value.index.toString(); @@ -214,11 +215,11 @@ class ColorNode extends KdbxSubTextNode { String encode(KdbxColor value) => value.isNull ? '' : value._rgb; } -class BooleanNode extends KdbxSubTextNode { +class BooleanNode extends KdbxSubTextNode { BooleanNode(KdbxNode node, String name) : super(node, name); @override - bool decode(String value) { + bool? decode(String value) { switch (value?.toLowerCase()) { case 'null': return null; @@ -231,10 +232,10 @@ class BooleanNode extends KdbxSubTextNode { } @override - String encode(bool value) => value ? 'true' : 'false'; + String encode(bool? value) => value! ? 'true' : 'false'; } -class DateTimeUtcNode extends KdbxSubTextNode { +class DateTimeUtcNode extends KdbxSubTextNode { DateTimeUtcNode(KdbxNodeContext node, String name) : super(node, name); static const EpochSeconds = 62135596800; @@ -250,7 +251,7 @@ class DateTimeUtcNode extends KdbxSubTextNode { } @override - DateTime decode(String value) { + DateTime? decode(String value) { if (value == null) { return null; } @@ -278,17 +279,17 @@ class DateTimeUtcNode extends KdbxSubTextNode { } @override - String encode(DateTime value) { - assert(value.isUtc); + String encode(DateTime? value) { + assert(value!.isUtc); if (_ctx.versionMajor >= 4) { // for kdbx v4 we need to support binary/base64 final secondsFrom00 = - (value.millisecondsSinceEpoch ~/ 1000) + EpochSeconds; + (value!.millisecondsSinceEpoch ~/ 1000) + EpochSeconds; final encoded = base64.encode( (WriterHelper()..writeUint64(secondsFrom00)).output.toBytes()); return encoded; } - return DateTimeUtils.toIso8601StringSeconds(value); + return DateTimeUtils.toIso8601StringSeconds(value!); } } diff --git a/lib/src/utils/byte_utils.dart b/lib/src/utils/byte_utils.dart index 11793f0..58d3596 100644 --- a/lib/src/utils/byte_utils.dart +++ b/lib/src/utils/byte_utils.dart @@ -31,7 +31,7 @@ class ByteUtils { static String toHex(int val) => '0x${val.toRadixString(16).padLeft(2, '0')}'; - static String toHexList(List list) => + static String toHexList(List? list) => list?.map((val) => toHex(val))?.join(' ') ?? '(null)'; } @@ -40,9 +40,9 @@ extension Uint8ListExt on Uint8List { } class ReaderHelper { - factory ReaderHelper(Uint8List byteData) => KdbxFormat.dartWebWorkaround - ? ReaderHelperDartWeb(byteData) - : ReaderHelper._(byteData); + factory ReaderHelper(Uint8List? byteData) => KdbxFormat.dartWebWorkaround + ? ReaderHelperDartWeb(byteData!) + : ReaderHelper._(byteData!); ReaderHelper._(this.byteData) : lengthInBytes = byteData.lengthInBytes; final Uint8List byteData; @@ -101,8 +101,8 @@ class ReaderHelper { Uint8List readRemaining() => _nextBytes(lengthInBytes - pos); - static int singleUint32(Uint8List bytes) => ReaderHelper(bytes).readUint32(); - static int singleUint64(Uint8List bytes) => ReaderHelper(bytes).readUint64(); + static int singleUint32(Uint8List? bytes) => ReaderHelper(bytes).readUint32(); + static int singleUint64(Uint8List? bytes) => ReaderHelper(bytes).readUint64(); } class ReaderHelperDartWeb extends ReaderHelper { @@ -125,48 +125,48 @@ class ReaderHelperDartWeb extends ReaderHelper { typedef LengthWriter = void Function(int length); class WriterHelper { - factory WriterHelper([BytesBuilder output]) => KdbxFormat.dartWebWorkaround + factory WriterHelper([BytesBuilder? output]) => KdbxFormat.dartWebWorkaround ? WriterHelperDartWeb(output) : WriterHelper._(output); - WriterHelper._([BytesBuilder output]) : output = output ?? BytesBuilder(); + WriterHelper._([BytesBuilder? output]) : output = output ?? BytesBuilder(); final BytesBuilder output; void _write(ByteData byteData) => output.add(byteData.buffer.asUint8List()); - void writeBytes(Uint8List bytes, [LengthWriter lengthWriter]) { + void writeBytes(Uint8List bytes, [LengthWriter? lengthWriter]) { lengthWriter?.call(bytes.length); output.add(bytes); // output.asUint8List().addAll(bytes); } - void writeUint32(int value, [LengthWriter lengthWriter]) { + void writeUint32(int value, [LengthWriter? lengthWriter]) { lengthWriter?.call(4); _write(ByteData(4)..setUint32(0, value, Endian.little)); // output.asUint32List().add(value); } - void writeUint64(int value, [LengthWriter lengthWriter]) { + void writeUint64(int value, [LengthWriter? lengthWriter]) { lengthWriter?.call(8); _write(ByteData(8)..setUint64(0, value, Endian.little)); } - void writeUint16(int value, [LengthWriter lengthWriter]) { + void writeUint16(int value, [LengthWriter? lengthWriter]) { lengthWriter?.call(2); _write(ByteData(2)..setUint16(0, value, Endian.little)); } - void writeInt32(int value, [LengthWriter lengthWriter]) { + void writeInt32(int value, [LengthWriter? lengthWriter]) { lengthWriter?.call(4); _write(ByteData(4)..setInt32(0, value, Endian.little)); } - void writeInt64(int value, [LengthWriter lengthWriter]) { + void writeInt64(int value, [LengthWriter? lengthWriter]) { lengthWriter?.call(8); _write(ByteData(8)..setInt64(0, value, Endian.little)); } - void writeUint8(int value, [LengthWriter lengthWriter]) { + void writeUint8(int value, [LengthWriter? lengthWriter]) { lengthWriter?.call(1); output.addByte(value); } @@ -176,7 +176,7 @@ class WriterHelper { static Uint8List singleUint64Bytes(int val) => (WriterHelper()..writeUint64(val)).output.toBytes(); - int writeString(String value, [LengthWriter lengthWriter]) { + int writeString(String value, [LengthWriter? lengthWriter]) { final bytes = const Utf8Encoder().convert(value); lengthWriter?.call(bytes.length); writeBytes(bytes); @@ -185,10 +185,10 @@ class WriterHelper { } class WriterHelperDartWeb extends WriterHelper { - WriterHelperDartWeb([BytesBuilder output]) : super._(output); + WriterHelperDartWeb([BytesBuilder? output]) : super._(output); @override - void writeUint64(int value, [LengthWriter lengthWriter]) { + void writeUint64(int value, [LengthWriter? lengthWriter]) { lengthWriter?.call(8); final _endian = Endian.little; @@ -206,7 +206,7 @@ class WriterHelperDartWeb extends WriterHelper { } @override - void writeInt64(int value, [LengthWriter lengthWriter]) { + void writeInt64(int value, [LengthWriter? lengthWriter]) { writeUint64(value, lengthWriter); } } diff --git a/lib/src/utils/print_utils.dart b/lib/src/utils/print_utils.dart index 8d468ba..7f32e8b 100644 --- a/lib/src/utils/print_utils.dart +++ b/lib/src/utils/print_utils.dart @@ -8,8 +8,8 @@ class KdbxPrintUtils { this.forceDecrypt = false, this.allFields = false, }); - final bool forceDecrypt; - final bool allFields; + final bool? forceDecrypt; + final bool? allFields; String catGroupToString(KdbxGroup group) => (StringBuffer()..let((that) => catGroup(that, group))).toString(); @@ -20,21 +20,21 @@ class KdbxPrintUtils { for (final group in group.groups) { catGroup(buf, group, depth: depth + 1); } - final valueToSting = (StringValue value) => - forceDecrypt ? value?.getText() : value?.toString(); + final valueToSting = (StringValue? value) => + forceDecrypt! ? value?.getText() : value?.toString(); for (final entry in group.entries) { final value = entry.getString(KdbxKeyCommon.PASSWORD); buf.writeln('$indent `- ${entry.debugLabel()}: ' '${valueToSting(value)}'); - if (allFields) { + if (allFields!) { buf.writeln(entry.stringEntries .map((field) => '$indent ${field.key} = ${valueToSting(field.value)}') .join('\n')); } buf.writeln(entry.binaryEntries - .map((b) => '$indent `- file: ${b.key} - ${b.value.value.length}') + .map((b) => '$indent `- file: ${b.key} - ${b.value.value!.length}') .join('\n')); } } diff --git a/pubspec.yaml b/pubspec.yaml index f3fc520..88324b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0 homepage: https://github.com/authpass/kdbx.dart environment: - sdk: '>=2.8.0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: # flutter: @@ -27,7 +27,7 @@ dependencies: supercharged_dart: '>=1.2.0 <4.0.0' synchronized: '>=2.2.0 <4.0.0' - collection: '>=1.14.0 <2.0.0' + collection: ^1.15.0-nullsafety.4 # required for bin/ args: '>1.5.0 <3.0.0' diff --git a/test/deleted_objects_test.dart b/test/deleted_objects_test.dart index 35a223e..c10d4f0 100644 --- a/test/deleted_objects_test.dart +++ b/test/deleted_objects_test.dart @@ -15,7 +15,7 @@ void main() { final orig = await TestUtil.readKdbxFile('test/test_files/tombstonetest.kdbx'); expect(orig.body.deletedObjects, hasLength(1)); - final dt = orig.body.deletedObjects.first.deletionTime.get(); + final dt = orig.body.deletedObjects.first.deletionTime.get()!; expect([dt.year, dt.month, dt.day], [2020, 8, 30]); final reload = await TestUtil.saveAndRead(orig); expect(reload.body.deletedObjects, hasLength(1)); diff --git a/test/icon/kdbx_customicon_test.dart b/test/icon/kdbx_customicon_test.dart index c0c8582..f35f00b 100644 --- a/test/icon/kdbx_customicon_test.dart +++ b/test/icon/kdbx_customicon_test.dart @@ -7,6 +7,6 @@ void main() { test('load custom icons from file', () async { final file = await TestUtil.readKdbxFile('test/icon/icontest.kdbx'); final entry = file.body.rootGroup.entries.first; - expect(entry.customIcon.data, isNotNull); + expect(entry.customIcon!.data, isNotNull); }); } diff --git a/test/internal/test_utils.dart b/test/internal/test_utils.dart index 1db4ab9..821aded 100644 --- a/test/internal/test_utils.dart +++ b/test/internal/test_utils.dart @@ -38,7 +38,7 @@ class TestUtil { } static Future readKdbxFileBytes(Uint8List data, - {String password = 'asdf', Credentials credentials}) async { + {String password = 'asdf', Credentials? credentials}) async { final kdbxFormat = TestUtil.kdbxFormat(); final file = await kdbxFormat.read( data, credentials ?? Credentials(ProtectedValue.fromString(password))); diff --git a/test/kdbx4_test.dart b/test/kdbx4_test.dart index b853c2d..8e1253d 100644 --- a/test/kdbx4_test.dart +++ b/test/kdbx4_test.dart @@ -24,7 +24,7 @@ void main() { final file = await kdbxFormat.read( data, Credentials(ProtectedValue.fromString('asdf'))); final firstEntry = file.body.rootGroup.entries.first; - final pwd = firstEntry.getString(KdbxKeyCommon.PASSWORD).getText(); + final pwd = firstEntry.getString(KdbxKeyCommon.PASSWORD)!.getText(); expect(pwd, 'MyPassword'); }); test('Reading kdbx4_keeweb', () async { @@ -32,7 +32,7 @@ void main() { final file = await kdbxFormat.read( data, Credentials(ProtectedValue.fromString('asdf'))); final firstEntry = file.body.rootGroup.entries.first; - final pwd = firstEntry.getString(KdbxKeyCommon.PASSWORD).getText(); + final pwd = firstEntry.getString(KdbxKeyCommon.PASSWORD)!.getText(); expect(pwd, 'def'); }); test('Reading kdbx4_keeweb modification time', () async { diff --git a/test/kdbx_binaries_test.dart b/test/kdbx_binaries_test.dart index 87746e9..f87c413 100644 --- a/test/kdbx_binaries_test.dart +++ b/test/kdbx_binaries_test.dart @@ -58,10 +58,10 @@ void main() { for (final binary in binaries) { switch (binary.key.key) { case 'example1.txt': - expect(utf8.decode(binary.value.value), 'content1 example\n\n'); + expect(utf8.decode(binary.value.value!), 'content1 example\n\n'); break; case 'example2.txt': - expect(utf8.decode(binary.value.value), 'content2 example\n\n'); + expect(utf8.decode(binary.value.value!), 'content2 example\n\n'); break; case 'keepasslogo.jpeg': expect(binary.value.value, hasLength(7092)); @@ -129,7 +129,7 @@ void main() { 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(), + utf8.decode(entry.getBinary(KdbxKey('$name.txt'))!.value!).trim(), name, ); } @@ -190,7 +190,7 @@ class IsUtf8String extends CustomMatcher { IsUtf8String(dynamic matcher) : super('is utf8 string', 'utf8', matcher); @override - Object featureValueOf(dynamic actual) { + Object? featureValueOf(dynamic actual) { if (actual is Uint8List) { return utf8.decode(actual); } diff --git a/test/kdbx_history_test.dart b/test/kdbx_history_test.dart index 376c37d..7fd9c4b 100644 --- a/test/kdbx_history_test.dart +++ b/test/kdbx_history_test.dart @@ -12,7 +12,7 @@ class StreamExpect { if (_expectNext == null) { fail('Got event, but none was expected. $event'); } - expect(event, _expectNext.orNull); + expect(event, _expectNext!.orNull); _expectNext = null; }, onDone: () { expect(_expectNext, isNull); @@ -23,13 +23,13 @@ class StreamExpect { }); } - Future expectNext(T value, FutureOr Function() cb) async { + Future expectNext(T value, FutureOr? Function() cb) async { if (_expectNext != null) { fail('The last event was never received. last: $_expectNext'); } _expectNext = Optional.fromNullable(value); try { - return await cb(); + return await cb()!; } finally { await pumpEventQueue(); } @@ -42,7 +42,7 @@ class StreamExpect { final Stream stream; bool isDone = false; dynamic error; - Optional _expectNext; + Optional? _expectNext; } void main() { @@ -57,7 +57,7 @@ void main() { { final first = file.body.rootGroup.entries.first; expect(file.header.version.major, 3); - expect(first.getString(TestUtil.keyTitle).getText(), valueOrig); + expect(first.getString(TestUtil.keyTitle)!.getText(), valueOrig); await dirtyExpect.expectNext({first}, () { first.setString(TestUtil.keyTitle, PlainValue(value1)); }); @@ -68,8 +68,8 @@ void main() { expect(file.dirtyObjects, isEmpty); { final first = f2.body.rootGroup.entries.first; - expect(first.getString(TestUtil.keyTitle).getText(), value1); - expect(first.history.last.getString(TestUtil.keyTitle).getText(), + expect(first.getString(TestUtil.keyTitle)!.getText(), value1); + expect(first.history.last.getString(TestUtil.keyTitle)!.getText(), valueOrig); await dirtyExpect.expectNext({}, () => file.save()); } @@ -85,11 +85,11 @@ void main() { expect(file.dirtyObjects, isEmpty); { final first = f3.body.rootGroup.entries.first; - expect(first.getString(TestUtil.keyTitle).getText(), value2); + expect(first.getString(TestUtil.keyTitle)!.getText(), value2); expect(first.history, hasLength(2)); expect( - first.history.last.getString(TestUtil.keyTitle).getText(), value1); - expect(first.history.first.getString(TestUtil.keyTitle).getText(), + first.history.last.getString(TestUtil.keyTitle)!.getText(), value1); + expect(first.history.first.getString(TestUtil.keyTitle)!.getText(), valueOrig); await dirtyExpect.expectNext({}, () => file.save()); } diff --git a/test/kdbx_test.dart b/test/kdbx_test.dart index ff40104..8dded95 100644 --- a/test/kdbx_test.dart +++ b/test/kdbx_test.dart @@ -100,7 +100,7 @@ void main() { final file = await TestUtil.readKdbxFile('test/keepass2test.kdbx'); final first = file.body.rootGroup.entries.first; expect(file.header.version.major, 3); - expect(first.getString(KdbxKeyCommon.TITLE).getText(), 'Sample Entry'); + expect(first.getString(KdbxKeyCommon.TITLE)!.getText(), 'Sample Entry'); final modTime = first.times.lastModificationTime.get(); expect(modTime, DateTime.utc(2020, 5, 6, 7, 31, 48)); }); @@ -110,7 +110,7 @@ void main() { { final first = file.body.rootGroup.entries.first; expect(file.header.version.major, 3); - expect(first.getString(KdbxKeyCommon.TITLE).getText(), 'Sample Entry'); + expect(first.getString(KdbxKeyCommon.TITLE)!.getText(), 'Sample Entry'); first.times.lastModificationTime.set(newModDate); } final saved = await file.save(); @@ -141,7 +141,7 @@ void main() { final kdbx = await kdbxFormat.read(saved, credentials); expect( kdbx.body.rootGroup.entries.first - .getString(KdbxKeyCommon.PASSWORD) + .getString(KdbxKeyCommon.PASSWORD)! .getText(), 'LoremIpsum'); File('test.kdbx').writeAsBytesSync(saved); diff --git a/test/merge/kdbx_merge_test.dart b/test/merge/kdbx_merge_test.dart index 9a323b8..a731635 100644 --- a/test/merge/kdbx_merge_test.dart +++ b/test/merge/kdbx_merge_test.dart @@ -92,7 +92,7 @@ void main() { '${KdbxPrintUtils().catGroupToString(file.body.rootGroup)}'); final set = Set.from(merge.merged.keys); expect(set, hasLength(5)); - expect(Set.from(merge.changes.map((e) => e.object)), + expect(Set.from(merge.changes.map((e) => e.object)), hasLength(2)); }), );