Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stricter lints #1597

Merged
merged 6 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 34 additions & 11 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,49 @@ include: package:lints/recommended.yaml

linter:
rules:
camel_case_types: true
avoid_print: true
constant_identifier_names: true
prefer_final_locals: true
# We enable a few additional rules not in the default and recommended sets.
# Those in general have low impact on correct code apart from possibly requiring an additional keyword (like async,
# await, final) or asking you to move a statement to a different line. However many of these linter rules make the
# code either more uniform or avoid bug prone behaviour. For example not awaiting a future usually is a mistake.
# You can always disable a linter warning with a comment like `// ignore: unawaited_futures`. Please add reasoning,
# why you disable the rule in such cases. This helps others understand, that you explicitly want a behaviour, that
# usually would be considered a mistake.

# Performance (or potential bugs)
# Fixing these warnings makes code easier to optimize for the compiler or prevents leaks.
cancel_subscriptions: true
prefer_final_in_for_each: true
sort_pub_dependencies: true
always_use_package_imports: true
prefer_final_locals: true

# Warn about possible bugs
# Usually code with these warnings indicates a bug.
# Please document if your code explicitly wants such a behaviour.
always_declare_return_types: true
prefer_single_quotes: true
sort_child_properties_last: true
discarded_futures: true
no_adjacent_strings_in_list: true
test_types_in_equals: true
throw_in_finally: true
unawaited_futures: true
unnecessary_statements: true
unsafe_html: true
avoid_function_literals_in_foreach_calls: false

# Readability & Style
# These are opinionated choices, where Dart gives us 2 ways to express the same thing.
# This avoids mental overhead by not having to make a choice and making code more uniform to read.
always_use_package_imports: true
avoid_bool_literals_in_conditional_expressions: true
prefer_single_quotes: true
sort_child_properties_last: true
sort_pub_dependencies: true

# Be nice to our users and allow them to configure what gets logged.
avoid_print: true

non_constant_identifier_names: false # seems to wrongly diagnose static const variables

analyzer:
errors:
todo: ignore
exclude:
- example/main.dart
# needed until crypto packages upgrade
- lib/src/database/database.g.dart
plugins:
14 changes: 4 additions & 10 deletions lib/encryption/encryption.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,12 @@ class Encryption {

void handleDeviceOneTimeKeysCount(
Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) {
runInRoot(() => olmManager.handleDeviceOneTimeKeysCount(
runInRoot(() async => olmManager.handleDeviceOneTimeKeysCount(
krille-chan marked this conversation as resolved.
Show resolved Hide resolved
countJson, unusedFallbackKeyTypes));
}

void onSync() {
// ignore: discarded_futures
keyVerificationManager.cleanup();
}

Expand All @@ -118,30 +119,25 @@ class Encryption {
.contains(event.type)) {
// "just" room key request things. We don't need these asap, so we handle
// them in the background
// ignore: unawaited_futures
runInRoot(() => keyManager.handleToDeviceEvent(event));
}
if (event.type == EventTypes.Dummy) {
// the previous device just had to create a new olm session, due to olm session
// corruption. We want to try to send it the last message we just sent it, if possible
// ignore: unawaited_futures
runInRoot(() => olmManager.handleToDeviceEvent(event));
}
if (event.type.startsWith('m.key.verification.')) {
// some key verification event. No need to handle it now, we can easily
// do this in the background

// ignore: unawaited_futures
runInRoot(() => keyVerificationManager.handleToDeviceEvent(event));
}
if (event.type.startsWith('m.secret.')) {
// some ssss thing. We can do this in the background
// ignore: unawaited_futures
runInRoot(() => ssss.handleToDeviceEvent(event));
}
if (event.sender == client.userID) {
// maybe we need to re-try SSSS secrets
// ignore: unawaited_futures
runInRoot(() => ssss.periodicallyRequestMissingCache());
}
}
Expand All @@ -157,14 +153,11 @@ class Encryption {
update.content['content']['msgtype']
.startsWith('m.key.verification.'))) {
// "just" key verification, no need to do this in sync

// ignore: unawaited_futures
runInRoot(() => keyVerificationManager.handleEventUpdate(update));
}
if (update.content['sender'] == client.userID &&
update.content['unsigned']?['transaction_id'] == null) {
// maybe we need to re-try SSSS secrets
// ignore: unawaited_futures
runInRoot(() => ssss.periodicallyRequestMissingCache());
}
}
Expand Down Expand Up @@ -239,6 +232,7 @@ class Encryption {
// the entry should always exist. In the case it doesn't, the following
// line *could* throw an error. As that is a future, though, and we call
// it un-awaited here, nothing happens, which is exactly the result we want
// ignore: discarded_futures
client.database?.updateInboundGroupSessionIndexes(
json.encode(inboundGroupSession.indexes), roomId, sessionId);
}
Expand All @@ -252,7 +246,7 @@ class Encryption {
?.session_id() ??
'') ==
content.sessionId) {
runInRoot(() =>
runInRoot(() async =>
keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true));
}
if (canRequestSession) {
Expand Down
8 changes: 5 additions & 3 deletions lib/encryption/key_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ class KeyManager {
!client.isUnknownSession) {
// do e2ee recovery
_requestedSessionIds.add(requestIdent);
runInRoot(() => request(

runInRoot(() async => request(
room,
sessionId,
senderKey,
Expand Down Expand Up @@ -775,8 +776,8 @@ class KeyManager {
Future<void>? _uploadingFuture;

void startAutoUploadKeys() {
_uploadKeysOnSync = encryption.client.onSync.stream
.listen((_) => uploadInboundGroupSessions(skipIfInProgress: true));
_uploadKeysOnSync = encryption.client.onSync.stream.listen(
(_) async => uploadInboundGroupSessions(skipIfInProgress: true));
}

/// This task should be performed after sync processing but should not block
Expand Down Expand Up @@ -1064,6 +1065,7 @@ class KeyManager {
StreamSubscription<SyncUpdate>? _uploadKeysOnSync;

void dispose() {
// ignore: discarded_futures
_uploadKeysOnSync?.cancel();
for (final sess in _outboundGroupSessions.values) {
sess.dispose();
Expand Down
70 changes: 39 additions & 31 deletions lib/encryption/olm_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -396,20 +396,24 @@ class OlmManager {
final device = client.userDeviceKeys[event.sender]?.deviceKeys.values
.firstWhereOrNull((d) => d.curve25519Key == senderKey);
final existingSessions = olmSessions[senderKey];
Future<void> updateSessionUsage([OlmSession? session]) =>
runInRoot(() async {
if (session != null) {
session.lastReceived = DateTime.now();
await storeOlmSession(session);
}
if (device != null) {
device.lastActive = DateTime.now();
await encryption.olmDatabase?.setLastActiveUserDeviceKey(
device.lastActive.millisecondsSinceEpoch,
device.userId,
device.deviceId!);
}
});
Future<void> updateSessionUsage([OlmSession? session]) async {
try {
if (session != null) {
session.lastReceived = DateTime.now();
await storeOlmSession(session);
}
if (device != null) {
device.lastActive = DateTime.now();
await encryption.olmDatabase?.setLastActiveUserDeviceKey(
device.lastActive.millisecondsSinceEpoch,
device.userId,
device.deviceId!);
}
} catch (e, s) {
Logs().e('Error while updating olm session timestamp', e, s);
}
}

if (existingSessions != null) {
for (final session in existingSessions) {
if (session.session == null) {
Expand Down Expand Up @@ -446,14 +450,16 @@ class OlmManager {
newSession.create_inbound_from(_olmAccount!, senderKey, body);
_olmAccount!.remove_one_time_keys(newSession);
await encryption.olmDatabase?.updateClientKeys(pickledOlmAccount!);

plaintext = newSession.decrypt(type, body);
await runInRoot(() => storeOlmSession(OlmSession(
key: client.userID!,
identityKey: senderKey,
sessionId: newSession.session_id(),
session: newSession,
lastReceived: DateTime.now(),
)));

await storeOlmSession(OlmSession(
key: client.userID!,
identityKey: senderKey,
sessionId: newSession.session_id(),
session: newSession,
lastReceived: DateTime.now(),
));
await updateSessionUsage();
} catch (e) {
newSession.free();
Expand Down Expand Up @@ -570,8 +576,6 @@ class OlmManager {
return _decryptToDeviceEvent(event);
} catch (_) {
// okay, the thing errored while decrypting. It is safe to assume that the olm session is corrupt and we should generate a new one

// ignore: unawaited_futures
runInRoot(() => restoreOlmSession(event.senderId, senderKey));

rethrow;
Expand Down Expand Up @@ -658,14 +662,18 @@ class OlmManager {
final encryptResult = sess.first.session!.encrypt(json.encode(fullPayload));
await storeOlmSession(sess.first);
if (encryption.olmDatabase != null) {
await runInRoot(
() async => encryption.olmDatabase?.setLastSentMessageUserDeviceKey(
json.encode({
'type': type,
'content': payload,
}),
device.userId,
device.deviceId!));
try {
await encryption.olmDatabase?.setLastSentMessageUserDeviceKey(
json.encode({
'type': type,
'content': payload,
}),
device.userId,
device.deviceId!);
} catch (e, s) {
// we can ignore this error, since it would just make us use a different olm session possibly
Logs().w('Error while updating olm usage timestamp', e, s);
}
}
final encryptedBody = <String, dynamic>{
'algorithm': AlgorithmTypes.olmV1Curve25519AesSha2,
Expand Down
7 changes: 5 additions & 2 deletions lib/encryption/ssss.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import 'package:matrix/encryption/utils/ssss_cache.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix/src/utils/cached_stream_controller.dart';
import 'package:matrix/src/utils/crypto/crypto.dart' as uc;
import 'package:matrix/src/utils/run_in_root.dart';

const cacheTypes = <String>{
EventTypes.CrossSigningSelfSigning,
Expand Down Expand Up @@ -722,7 +721,11 @@ class OpenSSSS {
throw InvalidPassphraseException('Inalid key');
}
if (postUnlock) {
await runInRoot(() => _postUnlock());
try {
await _postUnlock();
} catch (e, s) {
Logs().e('Error during post unlock', e, s);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/encryption/utils/base64_unpadded.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:typed_data';

/// decodes base64
///
/// Dart's native [base64.decode] requires a padded base64 input String.
/// Dart's native [base64.decode()] requires a padded base64 input String.
/// This function allows unpadded base64 too.
///
/// See: https://github.com/dart-lang/sdk/issues/39510
Expand Down
7 changes: 2 additions & 5 deletions lib/encryption/utils/key_verification.dart
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,8 @@ class KeyVerification {
}

/// `qrCanWork` - qr cannot work if we are verifying another master key but our own is unverified
final qrCanWork = (userId != client.userID)
? ((client.userDeviceKeys[client.userID]?.masterKey?.verified ?? false)
? true
: false)
: true;
final qrCanWork = (userId == client.userID) ||
((client.userDeviceKeys[client.userID]?.masterKey?.verified ?? false));

if (client.verificationMethods.contains(KeyVerificationMethod.qrShow) &&
qrCanWork) {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1636,7 +1636,7 @@ class Client extends MatrixApi {
set backgroundSync(bool enabled) {
_backgroundSync = enabled;
if (_backgroundSync) {
_sync();
runInRoot(() async => _sync());
}
}

Expand Down Expand Up @@ -2232,7 +2232,7 @@ class Client extends MatrixApi {
requestHistoryOnLimitedTimeline) {
Logs().v(
'Limited timeline for ${rooms[roomIndex].id} request history now');
unawaited(runInRoot(rooms[roomIndex].requestHistory));
runInRoot(rooms[roomIndex].requestHistory);
}
}
return room;
Expand Down
8 changes: 4 additions & 4 deletions lib/src/database/hive_collections_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ class HiveCollectionsDatabase extends DatabaseApi {
// post-load the heroes
final heroes = room.summary.mHeroes;
if (heroes != null) {
heroes.forEach((hero) => membersToPostload.add(hero));
membersToPostload.addAll(heroes);
}
}
// Load members
Expand Down Expand Up @@ -771,9 +771,9 @@ class HiveCollectionsDatabase extends DatabaseApi {
.toList();
final states = await _roomMembersBox.getAll(keys);
states.removeWhere((state) => state == null);
states.forEach(
(state) => users.add(Event.fromJson(copyMap(state!), room).asUser),
);
for (final state in states) {
users.add(Event.fromJson(copyMap(state!), room).asUser);
}

return users;
}
Expand Down
2 changes: 2 additions & 0 deletions lib/src/event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class Event extends MatrixEvent {
final json = toJson();
json['unsigned'] ??= <String, dynamic>{};
json['unsigned'][messageSendingStatusKey] = EventStatus.error.intValue;
// ignore: discarded_futures
room.client.handleSync(
SyncUpdate(
nextBatch: '',
Expand All @@ -154,6 +155,7 @@ class Event extends MatrixEvent {
MessageTypes.File,
}.contains(messageType) &&
!room.sendingFilePlaceholders.containsKey(eventId)) {
// ignore: discarded_futures
remove();
}
}
Expand Down
5 changes: 3 additions & 2 deletions lib/src/room.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1620,6 +1620,7 @@ class Room {
return user.asUser;
} else {
if (mxID.isValidMatrixId) {
// ignore: discarded_futures
requestUser(
mxID,
ignoreErrors: true,
Expand Down Expand Up @@ -2280,15 +2281,15 @@ class Room {
}

final Map<String, int> servers = {};
users.forEach((user) {
for (final user in users) {
if (user.id.domain != null) {
if (servers.containsKey(user.id.domain!)) {
servers[user.id.domain!] = servers[user.id.domain!]! + 1;
} else {
servers[user.id.domain!] = 1;
}
}
});
}
final sortedServers = Map.fromEntries(servers.entries.toList()
..sort((e1, e2) => e1.value.compareTo(e2.value)));
for (var i = 0; i <= 2; i++) {
Expand Down
Loading