feat: switch from pedantic to lints
This commit is contained in:
parent
543875e8cf
commit
6555f36d6d
|
|
@ -4,6 +4,7 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
*.swn
|
||||||
*.dylib
|
*.dylib
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.atom/
|
.atom/
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,21 @@
|
||||||
include: package:pedantic/analysis_options.yaml
|
include: package:lints/recommended.yaml
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
rules:
|
rules:
|
||||||
- camel_case_types
|
camel_case_types: true
|
||||||
- avoid_print
|
avoid_print: true
|
||||||
- constant_identifier_names
|
constant_identifier_names: true
|
||||||
- prefer_final_locals
|
prefer_final_locals: true
|
||||||
- prefer_final_in_for_each
|
prefer_final_in_for_each: true
|
||||||
|
sort_pub_dependencies: true
|
||||||
|
always_use_package_imports: true
|
||||||
|
always_declare_return_types: true
|
||||||
|
prefer_single_quotes: true
|
||||||
|
sort_child_properties_last: true
|
||||||
|
unawaited_futures: true
|
||||||
|
unsafe_html: true
|
||||||
|
avoid_function_literals_in_foreach_calls: false
|
||||||
|
non_constant_identifier_names: false # seems to wrongly diagnose static const variables
|
||||||
|
|
||||||
analyzer:
|
analyzer:
|
||||||
errors:
|
errors:
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
|
import 'package:matrix/encryption/encryption.dart';
|
||||||
|
import 'package:matrix/encryption/ssss.dart';
|
||||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||||
import '../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'encryption.dart';
|
|
||||||
import 'ssss.dart';
|
|
||||||
|
|
||||||
class CrossSigning {
|
class CrossSigning {
|
||||||
final Encryption encryption;
|
final Encryption encryption;
|
||||||
|
|
@ -131,14 +131,14 @@ class CrossSigning {
|
||||||
throw Exception('[sign] keys are not in cache but sign was called');
|
throw Exception('[sign] keys are not in cache but sign was called');
|
||||||
}
|
}
|
||||||
|
|
||||||
final addSignature =
|
void addSignature(
|
||||||
(SignableKey key, SignableKey signedWith, String signature) {
|
SignableKey key, SignableKey signedWith, String signature) {
|
||||||
final signedKey = key.cloneForSigning();
|
final signedKey = key.cloneForSigning();
|
||||||
((signedKey.signatures ??=
|
((signedKey.signatures ??=
|
||||||
<String, Map<String, String>>{})[signedWith.userId] ??=
|
<String, Map<String, String>>{})[signedWith.userId] ??=
|
||||||
<String, String>{})['ed25519:${signedWith.identifier}'] = signature;
|
<String, String>{})['ed25519:${signedWith.identifier}'] = signature;
|
||||||
signedKeys.add(signedKey);
|
signedKeys.add(signedKey);
|
||||||
};
|
}
|
||||||
|
|
||||||
for (final key in keys) {
|
for (final key in keys) {
|
||||||
if (key.userId == client.userID) {
|
if (key.userId == client.userID) {
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,14 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../matrix.dart';
|
import 'package:matrix/encryption/cross_signing.dart';
|
||||||
import '../src/utils/run_in_root.dart';
|
import 'package:matrix/encryption/key_manager.dart';
|
||||||
import 'cross_signing.dart';
|
import 'package:matrix/encryption/key_verification_manager.dart';
|
||||||
import 'key_manager.dart';
|
import 'package:matrix/encryption/olm_manager.dart';
|
||||||
import 'key_verification_manager.dart';
|
import 'package:matrix/encryption/ssss.dart';
|
||||||
import 'olm_manager.dart';
|
import 'package:matrix/encryption/utils/bootstrap.dart';
|
||||||
import 'ssss.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'utils/bootstrap.dart';
|
import 'package:matrix/src/utils/run_in_root.dart';
|
||||||
|
|
||||||
class Encryption {
|
class Encryption {
|
||||||
final Client client;
|
final Client client;
|
||||||
|
|
@ -206,10 +206,9 @@ class Encryption {
|
||||||
canRequestSession = false;
|
canRequestSession = false;
|
||||||
|
|
||||||
// we can't have the key be an int, else json-serializing will fail, thus we need it to be a string
|
// we can't have the key be an int, else json-serializing will fail, thus we need it to be a string
|
||||||
final messageIndexKey = 'key-' + decryptResult.message_index.toString();
|
final messageIndexKey = 'key-${decryptResult.message_index}';
|
||||||
final messageIndexValue = event.eventId +
|
final messageIndexValue =
|
||||||
'|' +
|
'${event.eventId}|${event.originServerTs.millisecondsSinceEpoch}';
|
||||||
event.originServerTs.millisecondsSinceEpoch.toString();
|
|
||||||
final haveIndex =
|
final haveIndex =
|
||||||
inboundGroupSession.indexes.containsKey(messageIndexKey);
|
inboundGroupSession.indexes.containsKey(messageIndexKey);
|
||||||
if (haveIndex &&
|
if (haveIndex &&
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,13 @@ import 'dart:convert';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
|
import 'package:matrix/encryption/encryption.dart';
|
||||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||||
|
import 'package:matrix/encryption/utils/outbound_group_session.dart';
|
||||||
|
import 'package:matrix/encryption/utils/session_key.dart';
|
||||||
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
||||||
import '../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import '../src/utils/run_in_root.dart';
|
import 'package:matrix/src/utils/run_in_root.dart';
|
||||||
import './encryption.dart';
|
|
||||||
import './utils/outbound_group_session.dart';
|
|
||||||
import './utils/session_key.dart';
|
|
||||||
|
|
||||||
const megolmKey = EventTypes.MegolmBackup;
|
const megolmKey = EventTypes.MegolmBackup;
|
||||||
|
|
||||||
|
|
@ -445,13 +445,17 @@ class KeyManager {
|
||||||
if (sess != null) {
|
if (sess != null) {
|
||||||
return sess;
|
return sess;
|
||||||
}
|
}
|
||||||
_pendingNewOutboundGroupSessions[roomId] =
|
final newSess = _pendingNewOutboundGroupSessions[roomId] =
|
||||||
_createOutboundGroupSession(roomId);
|
_createOutboundGroupSession(roomId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await _pendingNewOutboundGroupSessions[roomId];
|
await newSess;
|
||||||
} finally {
|
} finally {
|
||||||
return _pendingNewOutboundGroupSessions.remove(roomId)!;
|
_pendingNewOutboundGroupSessions
|
||||||
|
.removeWhere((_, value) => value == newSess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return newSess;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepares an outbound group session for a given room ID. That is, load it from
|
/// Prepares an outbound group session for a given room ID. That is, load it from
|
||||||
|
|
@ -794,7 +798,7 @@ class KeyManager {
|
||||||
/// Handle an incoming to_device event that is related to key sharing
|
/// Handle an incoming to_device event that is related to key sharing
|
||||||
Future<void> handleToDeviceEvent(ToDeviceEvent event) async {
|
Future<void> handleToDeviceEvent(ToDeviceEvent event) async {
|
||||||
if (event.type == EventTypes.RoomKeyRequest) {
|
if (event.type == EventTypes.RoomKeyRequest) {
|
||||||
if (!(event.content['request_id'] is String)) {
|
if (event.content['request_id'] is! String) {
|
||||||
return; // invalid event
|
return; // invalid event
|
||||||
}
|
}
|
||||||
if (event.content['action'] == 'request') {
|
if (event.content['action'] == 'request') {
|
||||||
|
|
@ -899,7 +903,7 @@ class KeyManager {
|
||||||
return; // someone we didn't send our request to replied....better ignore this
|
return; // someone we didn't send our request to replied....better ignore this
|
||||||
}
|
}
|
||||||
// we add the sender key to the forwarded key chain
|
// we add the sender key to the forwarded key chain
|
||||||
if (!(event.content['forwarding_curve25519_key_chain'] is List)) {
|
if (event.content['forwarding_curve25519_key_chain'] is! List) {
|
||||||
event.content['forwarding_curve25519_key_chain'] = <String>[];
|
event.content['forwarding_curve25519_key_chain'] = <String>[];
|
||||||
}
|
}
|
||||||
event.content['forwarding_curve25519_key_chain']
|
event.content['forwarding_curve25519_key_chain']
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../matrix.dart';
|
import 'package:matrix/encryption/encryption.dart';
|
||||||
import 'encryption.dart';
|
import 'package:matrix/encryption/utils/key_verification.dart';
|
||||||
import 'utils/key_verification.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
class KeyVerificationManager {
|
class KeyVerificationManager {
|
||||||
final Encryption encryption;
|
final Encryption encryption;
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ import 'package:canonical_json/canonical_json.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
|
import 'package:matrix/encryption/encryption.dart';
|
||||||
|
import 'package:matrix/encryption/utils/json_signature_check_extension.dart';
|
||||||
|
import 'package:matrix/encryption/utils/olm_session.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import '../encryption/utils/json_signature_check_extension.dart';
|
import 'package:matrix/src/utils/run_in_root.dart';
|
||||||
import '../src/utils/run_in_root.dart';
|
|
||||||
import 'encryption.dart';
|
|
||||||
import 'utils/olm_session.dart';
|
|
||||||
|
|
||||||
class OlmManager {
|
class OlmManager {
|
||||||
final Encryption encryption;
|
final Encryption encryption;
|
||||||
|
|
@ -119,8 +119,8 @@ class OlmManager {
|
||||||
bool updateDatabase = true,
|
bool updateDatabase = true,
|
||||||
bool? unusedFallbackKey = false,
|
bool? unusedFallbackKey = false,
|
||||||
}) async {
|
}) async {
|
||||||
final _olmAccount = this._olmAccount;
|
final olmAccount = _olmAccount;
|
||||||
if (_olmAccount == null) {
|
if (olmAccount == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,22 +136,22 @@ class OlmManager {
|
||||||
// check if we have OTKs that still need uploading. If we do, we don't try to generate new ones,
|
// check if we have OTKs that still need uploading. If we do, we don't try to generate new ones,
|
||||||
// instead we try to upload the old ones first
|
// instead we try to upload the old ones first
|
||||||
final oldOTKsNeedingUpload = json
|
final oldOTKsNeedingUpload = json
|
||||||
.decode(_olmAccount.one_time_keys())['curve25519']
|
.decode(olmAccount.one_time_keys())['curve25519']
|
||||||
.entries
|
.entries
|
||||||
.length as int;
|
.length as int;
|
||||||
// generate one-time keys
|
// generate one-time keys
|
||||||
// we generate 2/3rds of max, so that other keys people may still have can
|
// we generate 2/3rds of max, so that other keys people may still have can
|
||||||
// still be used
|
// still be used
|
||||||
final oneTimeKeysCount =
|
final oneTimeKeysCount =
|
||||||
(_olmAccount.max_number_of_one_time_keys() * 2 / 3).floor() -
|
(olmAccount.max_number_of_one_time_keys() * 2 / 3).floor() -
|
||||||
oldKeyCount -
|
oldKeyCount -
|
||||||
oldOTKsNeedingUpload;
|
oldOTKsNeedingUpload;
|
||||||
if (oneTimeKeysCount > 0) {
|
if (oneTimeKeysCount > 0) {
|
||||||
_olmAccount.generate_one_time_keys(oneTimeKeysCount);
|
olmAccount.generate_one_time_keys(oneTimeKeysCount);
|
||||||
}
|
}
|
||||||
uploadedOneTimeKeysCount = oneTimeKeysCount + oldOTKsNeedingUpload;
|
uploadedOneTimeKeysCount = oneTimeKeysCount + oldOTKsNeedingUpload;
|
||||||
final Map<String, dynamic> oneTimeKeys =
|
final Map<String, dynamic> oneTimeKeys =
|
||||||
json.decode(_olmAccount.one_time_keys());
|
json.decode(olmAccount.one_time_keys());
|
||||||
|
|
||||||
// now sign all the one-time keys
|
// now sign all the one-time keys
|
||||||
for (final entry in oneTimeKeys['curve25519'].entries) {
|
for (final entry in oneTimeKeys['curve25519'].entries) {
|
||||||
|
|
@ -166,8 +166,8 @@ class OlmManager {
|
||||||
final signedFallbackKeys = <String, dynamic>{};
|
final signedFallbackKeys = <String, dynamic>{};
|
||||||
if (encryption.isMinOlmVersion(3, 2, 0) && unusedFallbackKey == false) {
|
if (encryption.isMinOlmVersion(3, 2, 0) && unusedFallbackKey == false) {
|
||||||
// we don't have an unused fallback key uploaded....so let's change that!
|
// we don't have an unused fallback key uploaded....so let's change that!
|
||||||
_olmAccount.generate_fallback_key();
|
olmAccount.generate_fallback_key();
|
||||||
final fallbackKey = json.decode(_olmAccount.fallback_key());
|
final fallbackKey = json.decode(olmAccount.fallback_key());
|
||||||
// now sign all the fallback keys
|
// now sign all the fallback keys
|
||||||
for (final entry in fallbackKey['curve25519'].entries) {
|
for (final entry in fallbackKey['curve25519'].entries) {
|
||||||
final key = entry.key;
|
final key = entry.key;
|
||||||
|
|
@ -194,7 +194,7 @@ class OlmManager {
|
||||||
};
|
};
|
||||||
if (uploadDeviceKeys) {
|
if (uploadDeviceKeys) {
|
||||||
final Map<String, dynamic> keys =
|
final Map<String, dynamic> keys =
|
||||||
json.decode(_olmAccount.identity_keys());
|
json.decode(olmAccount.identity_keys());
|
||||||
for (final entry in keys.entries) {
|
for (final entry in keys.entries) {
|
||||||
final algorithm = entry.key;
|
final algorithm = entry.key;
|
||||||
final value = entry.value;
|
final value = entry.value;
|
||||||
|
|
@ -228,7 +228,7 @@ class OlmManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark the OTKs as published and save that to datbase
|
// mark the OTKs as published and save that to datbase
|
||||||
_olmAccount.mark_keys_as_published();
|
olmAccount.mark_keys_as_published();
|
||||||
if (updateDatabase) {
|
if (updateDatabase) {
|
||||||
await client.database?.updateClientKeys(pickledOlmAccount!);
|
await client.database?.updateClientKeys(pickledOlmAccount!);
|
||||||
}
|
}
|
||||||
|
|
@ -324,7 +324,8 @@ class OlmManager {
|
||||||
final device = client.userDeviceKeys[event.sender]?.deviceKeys.values
|
final device = client.userDeviceKeys[event.sender]?.deviceKeys.values
|
||||||
.firstWhereOrNull((d) => d.curve25519Key == senderKey);
|
.firstWhereOrNull((d) => d.curve25519Key == senderKey);
|
||||||
final existingSessions = olmSessions[senderKey];
|
final existingSessions = olmSessions[senderKey];
|
||||||
final updateSessionUsage = ([OlmSession? session]) => runInRoot(() async {
|
Future<void> updateSessionUsage([OlmSession? session]) =>
|
||||||
|
runInRoot(() async {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
session.lastReceived = DateTime.now();
|
session.lastReceived = DateTime.now();
|
||||||
await storeOlmSession(session);
|
await storeOlmSession(session);
|
||||||
|
|
@ -480,10 +481,11 @@ class OlmManager {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
final senderKey = event.parsedRoomEncryptedContent.senderKey;
|
final senderKey = event.parsedRoomEncryptedContent.senderKey;
|
||||||
final loadFromDb = () async {
|
Future<bool> loadFromDb() async {
|
||||||
final sessions = await getOlmSessions(senderKey);
|
final sessions = await getOlmSessions(senderKey);
|
||||||
return sessions.isNotEmpty;
|
return sessions.isNotEmpty;
|
||||||
};
|
}
|
||||||
|
|
||||||
if (!_olmSessions.containsKey(senderKey)) {
|
if (!_olmSessions.containsKey(senderKey)) {
|
||||||
await loadFromDb();
|
await loadFromDb();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ import 'package:base58check/base58.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
|
|
||||||
|
import 'package:matrix/encryption/encryption.dart';
|
||||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||||
import '../matrix.dart';
|
import 'package:matrix/encryption/utils/ssss_cache.dart';
|
||||||
import '../src/utils/crypto/crypto.dart' as uc;
|
import 'package:matrix/matrix.dart';
|
||||||
import '../src/utils/run_in_root.dart';
|
import 'package:matrix/src/utils/crypto/crypto.dart' as uc;
|
||||||
import 'encryption.dart';
|
import 'package:matrix/src/utils/run_in_root.dart';
|
||||||
import 'utils/ssss_cache.dart';
|
|
||||||
|
|
||||||
const cacheTypes = <String>{
|
const cacheTypes = <String>{
|
||||||
EventTypes.CrossSigningSelfSigning,
|
EventTypes.CrossSigningSelfSigning,
|
||||||
|
|
@ -286,7 +286,7 @@ class SSSS {
|
||||||
if (keys == null) {
|
if (keys == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final isValid = (dbEntry) =>
|
bool isValid(dbEntry) =>
|
||||||
keys.contains(dbEntry.keyId) &&
|
keys.contains(dbEntry.keyId) &&
|
||||||
dbEntry.ciphertext != null &&
|
dbEntry.ciphertext != null &&
|
||||||
client.accountData[type]?.content['encrypted'][dbEntry.keyId]
|
client.accountData[type]?.content['encrypted'][dbEntry.keyId]
|
||||||
|
|
@ -311,10 +311,10 @@ class SSSS {
|
||||||
if (secretInfo == null) {
|
if (secretInfo == null) {
|
||||||
throw Exception('Not found');
|
throw Exception('Not found');
|
||||||
}
|
}
|
||||||
if (!(secretInfo.content['encrypted'] is Map)) {
|
if (secretInfo.content['encrypted'] is! Map) {
|
||||||
throw Exception('Content is not encrypted');
|
throw Exception('Content is not encrypted');
|
||||||
}
|
}
|
||||||
if (!(secretInfo.content['encrypted'][keyId] is Map)) {
|
if (secretInfo.content['encrypted'][keyId] is! Map) {
|
||||||
throw Exception('Wrong / unknown key');
|
throw Exception('Wrong / unknown key');
|
||||||
}
|
}
|
||||||
final enc = secretInfo.content['encrypted'][keyId];
|
final enc = secretInfo.content['encrypted'][keyId];
|
||||||
|
|
@ -338,7 +338,7 @@ class SSSS {
|
||||||
Map<String, dynamic>? content;
|
Map<String, dynamic>? content;
|
||||||
if (add && client.accountData[type] != null) {
|
if (add && client.accountData[type] != null) {
|
||||||
content = client.accountData[type]!.content.copy();
|
content = client.accountData[type]!.content.copy();
|
||||||
if (!(content['encrypted'] is Map)) {
|
if (content['encrypted'] is! Map) {
|
||||||
content['encrypted'] = <String, dynamic>{};
|
content['encrypted'] = <String, dynamic>{};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -526,7 +526,7 @@ class SSSS {
|
||||||
return; // someone replied whom we didn't send the share request to
|
return; // someone replied whom we didn't send the share request to
|
||||||
}
|
}
|
||||||
final secret = event.content['secret'];
|
final secret = event.content['secret'];
|
||||||
if (!(event.content['secret'] is String)) {
|
if (event.content['secret'] is! String) {
|
||||||
Logs().i('[SSSS] Secret wasn\'t a string');
|
Logs().i('[SSSS] Secret wasn\'t a string');
|
||||||
return; // the secret wasn't a string....wut?
|
return; // the secret wasn't a string....wut?
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,11 @@ import 'dart:typed_data';
|
||||||
import 'package:canonical_json/canonical_json.dart';
|
import 'package:canonical_json/canonical_json.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/encryption/encryption.dart';
|
||||||
import '../encryption.dart';
|
import 'package:matrix/encryption/key_manager.dart';
|
||||||
import '../key_manager.dart';
|
import 'package:matrix/encryption/ssss.dart';
|
||||||
import '../ssss.dart';
|
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||||
import 'base64_unpadded.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
enum BootstrapState {
|
enum BootstrapState {
|
||||||
/// Is loading.
|
/// Is loading.
|
||||||
|
|
@ -104,7 +104,7 @@ class Bootstrap {
|
||||||
for (final entry in client.accountData.entries) {
|
for (final entry in client.accountData.entries) {
|
||||||
final type = entry.key;
|
final type = entry.key;
|
||||||
final event = entry.value;
|
final event = entry.value;
|
||||||
if (!(event.content['encrypted'] is Map)) {
|
if (event.content['encrypted'] is! Map) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final validKeys = <String>{};
|
final validKeys = <String>{};
|
||||||
|
|
@ -112,13 +112,13 @@ class Bootstrap {
|
||||||
for (final keyEntry in event.content['encrypted'].entries) {
|
for (final keyEntry in event.content['encrypted'].entries) {
|
||||||
final key = keyEntry.key;
|
final key = keyEntry.key;
|
||||||
final value = keyEntry.value;
|
final value = keyEntry.value;
|
||||||
if (!(value is Map)) {
|
if (value is! Map) {
|
||||||
// we don't add the key to invalidKeys as this was not a proper secret anyways!
|
// we don't add the key to invalidKeys as this was not a proper secret anyways!
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(value['iv'] is String) ||
|
if (value['iv'] is! String ||
|
||||||
!(value['ciphertext'] is String) ||
|
value['ciphertext'] is! String ||
|
||||||
!(value['mac'] is String)) {
|
value['mac'] is! String) {
|
||||||
invalidKeys.add(key);
|
invalidKeys.add(key);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -163,11 +163,12 @@ class Bootstrap {
|
||||||
(k, v) => v.isEmpty); // we don't care about the failed secrets here
|
(k, v) => v.isEmpty); // we don't care about the failed secrets here
|
||||||
final keys = <String>{};
|
final keys = <String>{};
|
||||||
final defaultKeyId = encryption.ssss.defaultKeyId;
|
final defaultKeyId = encryption.ssss.defaultKeyId;
|
||||||
final removeKey = (String key) {
|
int removeKey(String key) {
|
||||||
final sizeBefore = secrets.length;
|
final sizeBefore = secrets.length;
|
||||||
secrets.removeWhere((k, v) => v.contains(key));
|
secrets.removeWhere((k, v) => v.contains(key));
|
||||||
return sizeBefore - secrets.length;
|
return sizeBefore - secrets.length;
|
||||||
};
|
}
|
||||||
|
|
||||||
// first we want to try the default key id
|
// first we want to try the default key id
|
||||||
if (defaultKeyId != null) {
|
if (defaultKeyId != null) {
|
||||||
if (removeKey(defaultKeyId) > 0) {
|
if (removeKey(defaultKeyId) > 0) {
|
||||||
|
|
@ -264,14 +265,15 @@ class Bootstrap {
|
||||||
if (oldSsssKeys != null) {
|
if (oldSsssKeys != null) {
|
||||||
// alright, we have to re-encrypt old secrets with the new key
|
// alright, we have to re-encrypt old secrets with the new key
|
||||||
final secrets = analyzeSecrets();
|
final secrets = analyzeSecrets();
|
||||||
final removeKey = (String key) {
|
Set<String> removeKey(String key) {
|
||||||
final s = secrets.entries
|
final s = secrets.entries
|
||||||
.where((e) => e.value.contains(key))
|
.where((e) => e.value.contains(key))
|
||||||
.map((e) => e.key)
|
.map((e) => e.key)
|
||||||
.toSet();
|
.toSet();
|
||||||
secrets.removeWhere((k, v) => v.contains(key));
|
secrets.removeWhere((k, v) => v.contains(key));
|
||||||
return s;
|
return s;
|
||||||
};
|
}
|
||||||
|
|
||||||
secretMap = <String, String>{};
|
secretMap = <String, String>{};
|
||||||
for (final entry in oldSsssKeys!.entries) {
|
for (final entry in oldSsssKeys!.entries) {
|
||||||
final key = entry.value;
|
final key = entry.value;
|
||||||
|
|
@ -400,7 +402,7 @@ class Bootstrap {
|
||||||
master.free();
|
master.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final _sign = (Map<String, dynamic> object) {
|
String? sign(Map<String, dynamic> object) {
|
||||||
final keyObj = olm.PkSigning();
|
final keyObj = olm.PkSigning();
|
||||||
try {
|
try {
|
||||||
keyObj.init_with_seed(masterSigningKey);
|
keyObj.init_with_seed(masterSigningKey);
|
||||||
|
|
@ -409,7 +411,8 @@ class Bootstrap {
|
||||||
} finally {
|
} finally {
|
||||||
keyObj.free();
|
keyObj.free();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
if (setupSelfSigningKey) {
|
if (setupSelfSigningKey) {
|
||||||
final selfSigning = olm.PkSigning();
|
final selfSigning = olm.PkSigning();
|
||||||
try {
|
try {
|
||||||
|
|
@ -422,7 +425,7 @@ class Bootstrap {
|
||||||
'ed25519:$selfSigningPub': selfSigningPub,
|
'ed25519:$selfSigningPub': selfSigningPub,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
final signature = _sign(json);
|
final signature = sign(json);
|
||||||
json['signatures'] = <String, dynamic>{
|
json['signatures'] = <String, dynamic>{
|
||||||
userID: <String, dynamic>{
|
userID: <String, dynamic>{
|
||||||
'ed25519:$masterPub': signature,
|
'ed25519:$masterPub': signature,
|
||||||
|
|
@ -447,7 +450,7 @@ class Bootstrap {
|
||||||
'ed25519:$userSigningPub': userSigningPub,
|
'ed25519:$userSigningPub': userSigningPub,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
final signature = _sign(json);
|
final signature = sign(json);
|
||||||
json['signatures'] = <String, dynamic>{
|
json['signatures'] = <String, dynamic>{
|
||||||
userID: <String, dynamic>{
|
userID: <String, dynamic>{
|
||||||
'ed25519:$masterPub': signature,
|
'ed25519:$masterPub': signature,
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,14 @@
|
||||||
import 'package:canonical_json/canonical_json.dart';
|
import 'package:canonical_json/canonical_json.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
extension JsonSignatureCheckExtension on Map<String, dynamic> {
|
extension JsonSignatureCheckExtension on Map<String, dynamic> {
|
||||||
/// Checks the signature of a signed json object.
|
/// Checks the signature of a signed json object.
|
||||||
bool checkJsonSignature(String key, String userId, String deviceId) {
|
bool checkJsonSignature(String key, String userId, String deviceId) {
|
||||||
final signatures = this['signatures'];
|
final signatures = this['signatures'];
|
||||||
if (signatures == null ||
|
if (signatures == null ||
|
||||||
!(signatures is Map<String, dynamic>) ||
|
signatures is! Map<String, dynamic> ||
|
||||||
!signatures.containsKey(userId)) return false;
|
!signatures.containsKey(userId)) return false;
|
||||||
remove('unsigned');
|
remove('unsigned');
|
||||||
remove('signatures');
|
remove('signatures');
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import 'dart:typed_data';
|
||||||
import 'package:canonical_json/canonical_json.dart';
|
import 'package:canonical_json/canonical_json.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/encryption/encryption.dart';
|
||||||
import '../encryption.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
+-------------+ +-----------+
|
+-------------+ +-----------+
|
||||||
|
|
@ -112,7 +112,7 @@ class KeyVerification {
|
||||||
String? get deviceId => _deviceId;
|
String? get deviceId => _deviceId;
|
||||||
String? _deviceId;
|
String? _deviceId;
|
||||||
bool startedVerification = false;
|
bool startedVerification = false;
|
||||||
_KeyVerificationMethod? method;
|
_KeyVerificationMethod? _method;
|
||||||
List<String> possibleMethods = [];
|
List<String> possibleMethods = [];
|
||||||
Map<String, dynamic>? startPayload;
|
Map<String, dynamic>? startPayload;
|
||||||
String? _nextAction;
|
String? _nextAction;
|
||||||
|
|
@ -140,7 +140,7 @@ class KeyVerification {
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
Logs().i('[Key Verification] disposing object...');
|
Logs().i('[Key Verification] disposing object...');
|
||||||
method?.dispose();
|
_method?.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
static String? getTransactionId(Map<String, dynamic> payload) {
|
static String? getTransactionId(Map<String, dynamic> payload) {
|
||||||
|
|
@ -194,7 +194,7 @@ class KeyVerification {
|
||||||
await Future.delayed(Duration(milliseconds: 50));
|
await Future.delayed(Duration(milliseconds: 50));
|
||||||
}
|
}
|
||||||
_handlePayloadLock = true;
|
_handlePayloadLock = true;
|
||||||
Logs().i('[Key Verification] Received type $type: ' + payload.toString());
|
Logs().i('[Key Verification] Received type $type: $payload');
|
||||||
try {
|
try {
|
||||||
var thisLastStep = lastStep;
|
var thisLastStep = lastStep;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
@ -272,17 +272,17 @@ class KeyVerification {
|
||||||
// as such, we better set it *before* we send our start
|
// as such, we better set it *before* we send our start
|
||||||
lastStep = type;
|
lastStep = type;
|
||||||
// TODO: Pick method?
|
// TODO: Pick method?
|
||||||
final method = this.method =
|
final method =
|
||||||
_makeVerificationMethod(possibleMethods.first, this);
|
_method = _makeVerificationMethod(possibleMethods.first, this);
|
||||||
await method.sendStart();
|
await method.sendStart();
|
||||||
setState(KeyVerificationState.waitingAccept);
|
setState(KeyVerificationState.waitingAccept);
|
||||||
break;
|
break;
|
||||||
case EventTypes.KeyVerificationStart:
|
case EventTypes.KeyVerificationStart:
|
||||||
_deviceId ??= payload['from_device'];
|
_deviceId ??= payload['from_device'];
|
||||||
transactionId ??= eventId ?? payload['transaction_id'];
|
transactionId ??= eventId ?? payload['transaction_id'];
|
||||||
if (method != null) {
|
if (_method != null) {
|
||||||
// the other side sent us a start, even though we already sent one
|
// the other side sent us a start, even though we already sent one
|
||||||
if (payload['method'] == method!.type) {
|
if (payload['method'] == _method!.type) {
|
||||||
// same method. Determine priority
|
// same method. Determine priority
|
||||||
final ourEntry = '${client.userID}|${client.deviceID}';
|
final ourEntry = '${client.userID}|${client.deviceID}';
|
||||||
final entries = [ourEntry, '$userId|$deviceId'];
|
final entries = [ourEntry, '$userId|$deviceId'];
|
||||||
|
|
@ -295,7 +295,7 @@ class KeyVerification {
|
||||||
startedVerification = false; // it is now as if they started
|
startedVerification = false; // it is now as if they started
|
||||||
thisLastStep = lastStep =
|
thisLastStep = lastStep =
|
||||||
EventTypes.KeyVerificationRequest; // we fake the last step
|
EventTypes.KeyVerificationRequest; // we fake the last step
|
||||||
method!.dispose(); // in case anything got created already
|
_method!.dispose(); // in case anything got created already
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// methods don't match up, let's cancel this
|
// methods don't match up, let's cancel this
|
||||||
|
|
@ -321,7 +321,7 @@ class KeyVerification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
method = _makeVerificationMethod(payload['method'], this);
|
_method = _makeVerificationMethod(payload['method'], this);
|
||||||
if (lastStep == null) {
|
if (lastStep == null) {
|
||||||
// validate the start time
|
// validate the start time
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
|
|
@ -330,7 +330,7 @@ class KeyVerification {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// validate the specific payload
|
// validate the specific payload
|
||||||
if (!method!.validateStart(payload)) {
|
if (!_method!.validateStart(payload)) {
|
||||||
await cancel('m.unknown_method');
|
await cancel('m.unknown_method');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -338,7 +338,7 @@ class KeyVerification {
|
||||||
setState(KeyVerificationState.askAccept);
|
setState(KeyVerificationState.askAccept);
|
||||||
} else {
|
} else {
|
||||||
Logs().i('handling start in method.....');
|
Logs().i('handling start in method.....');
|
||||||
await method!.handlePayload(type, payload);
|
await _method!.handlePayload(type, payload);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EventTypes.KeyVerificationDone:
|
case EventTypes.KeyVerificationDone:
|
||||||
|
|
@ -351,7 +351,7 @@ class KeyVerification {
|
||||||
setState(KeyVerificationState.error);
|
setState(KeyVerificationState.error);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
final method = this.method;
|
final method = _method;
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
await method.handlePayload(type, payload);
|
await method.handlePayload(type, payload);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -382,7 +382,7 @@ class KeyVerification {
|
||||||
String? recoveryKey,
|
String? recoveryKey,
|
||||||
String? keyOrPassphrase,
|
String? keyOrPassphrase,
|
||||||
bool skip = false}) async {
|
bool skip = false}) async {
|
||||||
final next = () async {
|
Future<void> next() async {
|
||||||
if (_nextAction == 'request') {
|
if (_nextAction == 'request') {
|
||||||
await sendStart();
|
await sendStart();
|
||||||
} else if (_nextAction == 'done') {
|
} else if (_nextAction == 'done') {
|
||||||
|
|
@ -390,7 +390,8 @@ class KeyVerification {
|
||||||
unawaited(encryption.crossSigning.sign(_verifiedDevices));
|
unawaited(encryption.crossSigning.sign(_verifiedDevices));
|
||||||
setState(KeyVerificationState.done);
|
setState(KeyVerificationState.done);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
if (skip) {
|
if (skip) {
|
||||||
await next();
|
await next();
|
||||||
return;
|
return;
|
||||||
|
|
@ -420,7 +421,7 @@ class KeyVerification {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// we need to send an accept event
|
// we need to send an accept event
|
||||||
await method!
|
await _method!
|
||||||
.handlePayload(EventTypes.KeyVerificationStart, startPayload!);
|
.handlePayload(EventTypes.KeyVerificationStart, startPayload!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -440,20 +441,20 @@ class KeyVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> acceptSas() async {
|
Future<void> acceptSas() async {
|
||||||
if (method is _KeyVerificationMethodSas) {
|
if (_method is _KeyVerificationMethodSas) {
|
||||||
await (method as _KeyVerificationMethodSas).acceptSas();
|
await (_method as _KeyVerificationMethodSas).acceptSas();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> rejectSas() async {
|
Future<void> rejectSas() async {
|
||||||
if (method is _KeyVerificationMethodSas) {
|
if (_method is _KeyVerificationMethodSas) {
|
||||||
await (method as _KeyVerificationMethodSas).rejectSas();
|
await (_method as _KeyVerificationMethodSas).rejectSas();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<int> get sasNumbers {
|
List<int> get sasNumbers {
|
||||||
if (method is _KeyVerificationMethodSas) {
|
if (_method is _KeyVerificationMethodSas) {
|
||||||
return _bytesToInt((method as _KeyVerificationMethodSas).makeSas(5), 13)
|
return _bytesToInt((_method as _KeyVerificationMethodSas).makeSas(5), 13)
|
||||||
.map((n) => n + 1000)
|
.map((n) => n + 1000)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
@ -461,16 +462,16 @@ class KeyVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> get sasTypes {
|
List<String> get sasTypes {
|
||||||
if (method is _KeyVerificationMethodSas) {
|
if (_method is _KeyVerificationMethodSas) {
|
||||||
return (method as _KeyVerificationMethodSas).authenticationTypes ?? [];
|
return (_method as _KeyVerificationMethodSas).authenticationTypes ?? [];
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<KeyVerificationEmoji> get sasEmojis {
|
List<KeyVerificationEmoji> get sasEmojis {
|
||||||
if (method is _KeyVerificationMethodSas) {
|
if (_method is _KeyVerificationMethodSas) {
|
||||||
final numbers =
|
final numbers =
|
||||||
_bytesToInt((method as _KeyVerificationMethodSas).makeSas(6), 6);
|
_bytesToInt((_method as _KeyVerificationMethodSas).makeSas(6), 6);
|
||||||
return numbers.map((n) => KeyVerificationEmoji(n)).toList().sublist(0, 7);
|
return numbers.map((n) => KeyVerificationEmoji(n)).toList().sublist(0, 7);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -605,7 +606,7 @@ class KeyVerification {
|
||||||
|
|
||||||
Future<void> send(String type, Map<String, dynamic> payload) async {
|
Future<void> send(String type, Map<String, dynamic> payload) async {
|
||||||
makePayload(payload);
|
makePayload(payload);
|
||||||
Logs().i('[Key Verification] Sending type $type: ' + payload.toString());
|
Logs().i('[Key Verification] Sending type $type: $payload');
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
Logs().i('[Key Verification] Sending to $userId in room ${room!.id}...');
|
Logs().i('[Key Verification] Sending to $userId in room ${room!.id}...');
|
||||||
if ({EventTypes.KeyVerificationRequest}.contains(type)) {
|
if ({EventTypes.KeyVerificationRequest}.contains(type)) {
|
||||||
|
|
@ -680,6 +681,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
|
||||||
: super(request: request);
|
: super(request: request);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
// ignore: overridden_fields
|
||||||
final _type = 'm.sas.v1';
|
final _type = 'm.sas.v1';
|
||||||
|
|
||||||
String? keyAgreementProtocol;
|
String? keyAgreementProtocol;
|
||||||
|
|
@ -897,19 +899,13 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
|
||||||
'${client.userID}|${client.deviceID}|${sas!.get_pubkey()}|';
|
'${client.userID}|${client.deviceID}|${sas!.get_pubkey()}|';
|
||||||
final theirInfo =
|
final theirInfo =
|
||||||
'${request.userId}|${request.deviceId}|$theirPublicKey|';
|
'${request.userId}|${request.deviceId}|$theirPublicKey|';
|
||||||
sasInfo = 'MATRIX_KEY_VERIFICATION_SAS|' +
|
sasInfo =
|
||||||
(request.startedVerification
|
'MATRIX_KEY_VERIFICATION_SAS|${request.startedVerification ? ourInfo + theirInfo : theirInfo + ourInfo}${request.transactionId!}';
|
||||||
? ourInfo + theirInfo
|
|
||||||
: theirInfo + ourInfo) +
|
|
||||||
request.transactionId!;
|
|
||||||
} else if (keyAgreementProtocol == 'curve25519') {
|
} else if (keyAgreementProtocol == 'curve25519') {
|
||||||
final ourInfo = client.userID! + client.deviceID!;
|
final ourInfo = client.userID! + client.deviceID!;
|
||||||
final theirInfo = request.userId + request.deviceId!;
|
final theirInfo = request.userId + request.deviceId!;
|
||||||
sasInfo = 'MATRIX_KEY_VERIFICATION_SAS' +
|
sasInfo =
|
||||||
(request.startedVerification
|
'MATRIX_KEY_VERIFICATION_SAS${request.startedVerification ? ourInfo + theirInfo : theirInfo + ourInfo}${request.transactionId!}';
|
||||||
? ourInfo + theirInfo
|
|
||||||
: theirInfo + ourInfo) +
|
|
||||||
request.transactionId!;
|
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Unknown key agreement protocol');
|
throw Exception('Unknown key agreement protocol');
|
||||||
}
|
}
|
||||||
|
|
@ -917,12 +913,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _sendMac() async {
|
Future<void> _sendMac() async {
|
||||||
final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' +
|
final baseInfo =
|
||||||
client.userID! +
|
'MATRIX_KEY_VERIFICATION_MAC${client.userID!}${client.deviceID!}${request.userId}${request.deviceId!}${request.transactionId!}';
|
||||||
client.deviceID! +
|
|
||||||
request.userId +
|
|
||||||
request.deviceId! +
|
|
||||||
request.transactionId!;
|
|
||||||
final mac = <String, String>{};
|
final mac = <String, String>{};
|
||||||
final keyList = <String>[];
|
final keyList = <String>[];
|
||||||
|
|
||||||
|
|
@ -944,7 +936,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
|
||||||
}
|
}
|
||||||
|
|
||||||
keyList.sort();
|
keyList.sort();
|
||||||
final keys = _calculateMac(keyList.join(','), baseInfo + 'KEY_IDS');
|
final keys = _calculateMac(keyList.join(','), '${baseInfo}KEY_IDS');
|
||||||
await request.send('m.key.verification.mac', {
|
await request.send('m.key.verification.mac', {
|
||||||
'mac': mac,
|
'mac': mac,
|
||||||
'keys': keys,
|
'keys': keys,
|
||||||
|
|
@ -953,17 +945,13 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
|
||||||
|
|
||||||
Future<void> _processMac() async {
|
Future<void> _processMac() async {
|
||||||
final payload = macPayload!;
|
final payload = macPayload!;
|
||||||
final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' +
|
final baseInfo =
|
||||||
request.userId +
|
'MATRIX_KEY_VERIFICATION_MAC${request.userId}${request.deviceId!}${client.userID!}${client.deviceID!}${request.transactionId!}';
|
||||||
request.deviceId! +
|
|
||||||
client.userID! +
|
|
||||||
client.deviceID! +
|
|
||||||
request.transactionId!;
|
|
||||||
|
|
||||||
final keyList = payload['mac'].keys.toList();
|
final keyList = payload['mac'].keys.toList();
|
||||||
keyList.sort();
|
keyList.sort();
|
||||||
if (payload['keys'] !=
|
if (payload['keys'] !=
|
||||||
_calculateMac(keyList.join(','), baseInfo + 'KEY_IDS')) {
|
_calculateMac(keyList.join(','), '${baseInfo}KEY_IDS')) {
|
||||||
await request.cancel('m.key_mismatch');
|
await request.cancel('m.key_mismatch');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -981,7 +969,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
|
||||||
await request.verifyKeys(mac, (String mac, SignableKey key) async {
|
await request.verifyKeys(mac, (String mac, SignableKey key) async {
|
||||||
return mac ==
|
return mac ==
|
||||||
_calculateMac(
|
_calculateMac(
|
||||||
key.ed25519Key!, baseInfo + 'ed25519:' + key.identifier!);
|
key.ed25519Key!, '${baseInfo}ed25519:${key.identifier!}');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
class OlmSession {
|
class OlmSession {
|
||||||
String identityKey;
|
String identityKey;
|
||||||
|
|
@ -38,9 +38,8 @@ class OlmSession {
|
||||||
required this.lastReceived,
|
required this.lastReceived,
|
||||||
});
|
});
|
||||||
|
|
||||||
OlmSession.fromJson(Map<String, dynamic> dbEntry, String key)
|
OlmSession.fromJson(Map<String, dynamic> dbEntry, this.key)
|
||||||
: key = key,
|
: identityKey = dbEntry['identity_key'] ?? '' {
|
||||||
identityKey = dbEntry['identity_key'] ?? '' {
|
|
||||||
session = olm.Session();
|
session = olm.Session();
|
||||||
try {
|
try {
|
||||||
session!.unpickle(key, dbEntry['pickle']);
|
session!.unpickle(key, dbEntry['pickle']);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
class OutboundGroupSession {
|
class OutboundGroupSession {
|
||||||
/// The devices is a map from user id to device id to if the device is blocked.
|
/// The devices is a map from user id to device id to if the device is blocked.
|
||||||
|
|
@ -41,8 +41,7 @@ class OutboundGroupSession {
|
||||||
required this.outboundGroupSession,
|
required this.outboundGroupSession,
|
||||||
required this.key});
|
required this.key});
|
||||||
|
|
||||||
OutboundGroupSession.fromJson(Map<String, dynamic> dbEntry, String key)
|
OutboundGroupSession.fromJson(Map<String, dynamic> dbEntry, this.key) {
|
||||||
: key = key {
|
|
||||||
try {
|
try {
|
||||||
for (final entry in json.decode(dbEntry['device_ids']).entries) {
|
for (final entry in json.decode(dbEntry['device_ids']).entries) {
|
||||||
devices[entry.key] = Map<String, bool>.from(entry.value);
|
devices[entry.key] = Map<String, bool>.from(entry.value);
|
||||||
|
|
@ -50,8 +49,7 @@ class OutboundGroupSession {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// devices is bad (old data), so just not use this session
|
// devices is bad (old data), so just not use this session
|
||||||
Logs().i(
|
Logs().i(
|
||||||
'[OutboundGroupSession] Session in database is old, not using it. ' +
|
'[OutboundGroupSession] Session in database is old, not using it. $e');
|
||||||
e.toString());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
outboundGroupSession = olm.OutboundGroupSession();
|
outboundGroupSession = olm.OutboundGroupSession();
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import 'package:matrix_api_lite/src/utils/filter_map_extension.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
class SessionKey {
|
class SessionKey {
|
||||||
/// The raw json content of the key
|
/// The raw json content of the key
|
||||||
|
|
@ -74,9 +74,8 @@ class SessionKey {
|
||||||
: indexes = indexes ?? <String, String>{},
|
: indexes = indexes ?? <String, String>{},
|
||||||
allowedAtIndex = allowedAtIndex ?? <String, Map<String, int>>{};
|
allowedAtIndex = allowedAtIndex ?? <String, Map<String, int>>{};
|
||||||
|
|
||||||
SessionKey.fromDb(StoredInboundGroupSession dbEntry, String key)
|
SessionKey.fromDb(StoredInboundGroupSession dbEntry, this.key)
|
||||||
: key = key,
|
: content = Event.getMapFromPayload(dbEntry.content),
|
||||||
content = Event.getMapFromPayload(dbEntry.content),
|
|
||||||
indexes = Event.getMapFromPayload(dbEntry.indexes)
|
indexes = Event.getMapFromPayload(dbEntry.indexes)
|
||||||
.catchMap((k, v) => MapEntry<String, String>(k, v)),
|
.catchMap((k, v) => MapEntry<String, String>(k, v)),
|
||||||
allowedAtIndex = Event.getMapFromPayload(dbEntry.allowedAtIndex)
|
allowedAtIndex = Event.getMapFromPayload(dbEntry.allowedAtIndex)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ extension MatrixWidgets on Room {
|
||||||
Future<String> addWidget(MatrixWidget widget) {
|
Future<String> addWidget(MatrixWidget widget) {
|
||||||
final user = client.userID;
|
final user = client.userID;
|
||||||
final widgetId =
|
final widgetId =
|
||||||
widget.name!.toLowerCase().replaceAll(RegExp(r'\W'), '_') + '_' + user!;
|
'${widget.name!.toLowerCase().replaceAll(RegExp(r'\W'), '_')}_${user!}';
|
||||||
|
|
||||||
final json = widget.toJson();
|
final json = widget.toJson();
|
||||||
json['creatorUserId'] = user;
|
json['creatorUserId'] = user;
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,15 @@ import 'package:mime/mime.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
import 'package:random_string/random_string.dart';
|
import 'package:random_string/random_string.dart';
|
||||||
|
|
||||||
|
import 'package:matrix/encryption.dart';
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
|
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||||
|
import 'package:matrix/src/utils/compute_callback.dart';
|
||||||
|
import 'package:matrix/src/utils/multilock.dart';
|
||||||
|
import 'package:matrix/src/utils/run_benchmarked.dart';
|
||||||
import 'package:matrix/src/utils/run_in_root.dart';
|
import 'package:matrix/src/utils/run_in_root.dart';
|
||||||
import 'package:matrix/src/utils/sync_update_item_count.dart';
|
import 'package:matrix/src/utils/sync_update_item_count.dart';
|
||||||
import '../encryption.dart';
|
|
||||||
import '../matrix.dart';
|
|
||||||
import 'models/timeline_chunk.dart';
|
|
||||||
import 'utils/compute_callback.dart';
|
|
||||||
import 'utils/multilock.dart';
|
|
||||||
import 'utils/run_benchmarked.dart';
|
|
||||||
|
|
||||||
typedef RoomSorter = int Function(Room a, Room b);
|
typedef RoomSorter = int Function(Room a, Room b);
|
||||||
|
|
||||||
|
|
@ -940,28 +940,30 @@ class Client extends MatrixApi {
|
||||||
try {
|
try {
|
||||||
// stopping sync loop and subscriptions while keeping DB open
|
// stopping sync loop and subscriptions while keeping DB open
|
||||||
await dispose(closeDatabase: false);
|
await dispose(closeDatabase: false);
|
||||||
} finally {
|
} catch (_) {
|
||||||
_database ??= await databaseBuilder!.call(this);
|
// Client was probably not initialized yet.
|
||||||
|
|
||||||
final success = await database!.importDump(export);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
// closing including DB
|
|
||||||
await dispose();
|
|
||||||
|
|
||||||
try {
|
|
||||||
bearerToken = null;
|
|
||||||
|
|
||||||
await init(
|
|
||||||
waitForFirstSync: false,
|
|
||||||
waitUntilLoadCompletedLoaded: false,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_database ??= await databaseBuilder!.call(this);
|
||||||
|
|
||||||
|
final success = await database!.importDump(export);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
// closing including DB
|
||||||
|
await dispose();
|
||||||
|
|
||||||
|
try {
|
||||||
|
bearerToken = null;
|
||||||
|
|
||||||
|
await init(
|
||||||
|
waitForFirstSync: false,
|
||||||
|
waitUntilLoadCompletedLoaded: false,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uploads a new user avatar for this user. Leave file null to remove the
|
/// Uploads a new user avatar for this user. Leave file null to remove the
|
||||||
|
|
@ -1336,13 +1338,13 @@ class Client extends MatrixApi {
|
||||||
|
|
||||||
String? olmAccount;
|
String? olmAccount;
|
||||||
String? accessToken;
|
String? accessToken;
|
||||||
String? _userID;
|
String? userID;
|
||||||
final account = await this.database?.getClient(clientName);
|
final account = await this.database?.getClient(clientName);
|
||||||
if (account != null) {
|
if (account != null) {
|
||||||
_id = account['client_id'];
|
_id = account['client_id'];
|
||||||
homeserver = Uri.parse(account['homeserver_url']);
|
homeserver = Uri.parse(account['homeserver_url']);
|
||||||
accessToken = this.accessToken = account['token'];
|
accessToken = this.accessToken = account['token'];
|
||||||
_userID = this._userID = account['user_id'];
|
userID = _userID = account['user_id'];
|
||||||
_deviceID = account['device_id'];
|
_deviceID = account['device_id'];
|
||||||
_deviceName = account['device_name'];
|
_deviceName = account['device_name'];
|
||||||
syncFilterId = account['sync_filter_id'];
|
syncFilterId = account['sync_filter_id'];
|
||||||
|
|
@ -1352,20 +1354,20 @@ class Client extends MatrixApi {
|
||||||
if (newToken != null) {
|
if (newToken != null) {
|
||||||
accessToken = this.accessToken = newToken;
|
accessToken = this.accessToken = newToken;
|
||||||
homeserver = newHomeserver;
|
homeserver = newHomeserver;
|
||||||
_userID = this._userID = newUserID;
|
userID = _userID = newUserID;
|
||||||
_deviceID = newDeviceID;
|
_deviceID = newDeviceID;
|
||||||
_deviceName = newDeviceName;
|
_deviceName = newDeviceName;
|
||||||
olmAccount = newOlmAccount;
|
olmAccount = newOlmAccount;
|
||||||
} else {
|
} else {
|
||||||
accessToken = this.accessToken = newToken ?? accessToken;
|
accessToken = this.accessToken = newToken ?? accessToken;
|
||||||
homeserver = newHomeserver ?? homeserver;
|
homeserver = newHomeserver ?? homeserver;
|
||||||
_userID = this._userID = newUserID ?? _userID;
|
userID = _userID = newUserID ?? userID;
|
||||||
_deviceID = newDeviceID ?? _deviceID;
|
_deviceID = newDeviceID ?? _deviceID;
|
||||||
_deviceName = newDeviceName ?? _deviceName;
|
_deviceName = newDeviceName ?? _deviceName;
|
||||||
olmAccount = newOlmAccount ?? olmAccount;
|
olmAccount = newOlmAccount ?? olmAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessToken == null || homeserver == null || _userID == null) {
|
if (accessToken == null || homeserver == null || userID == null) {
|
||||||
if (legacyDatabaseBuilder != null) {
|
if (legacyDatabaseBuilder != null) {
|
||||||
await _migrateFromLegacyDatabase();
|
await _migrateFromLegacyDatabase();
|
||||||
if (isLogged()) return;
|
if (isLogged()) return;
|
||||||
|
|
@ -1397,7 +1399,7 @@ class Client extends MatrixApi {
|
||||||
await database.updateClient(
|
await database.updateClient(
|
||||||
homeserver.toString(),
|
homeserver.toString(),
|
||||||
accessToken,
|
accessToken,
|
||||||
_userID,
|
userID,
|
||||||
_deviceID,
|
_deviceID,
|
||||||
_deviceName,
|
_deviceName,
|
||||||
prevBatch,
|
prevBatch,
|
||||||
|
|
@ -1408,7 +1410,7 @@ class Client extends MatrixApi {
|
||||||
clientName,
|
clientName,
|
||||||
homeserver.toString(),
|
homeserver.toString(),
|
||||||
accessToken,
|
accessToken,
|
||||||
_userID,
|
userID,
|
||||||
_deviceID,
|
_deviceID,
|
||||||
_deviceName,
|
_deviceName,
|
||||||
prevBatch,
|
prevBatch,
|
||||||
|
|
@ -1434,7 +1436,7 @@ class Client extends MatrixApi {
|
||||||
_initLock = false;
|
_initLock = false;
|
||||||
onLoginStateChanged.add(LoginState.loggedIn);
|
onLoginStateChanged.add(LoginState.loggedIn);
|
||||||
Logs().i(
|
Logs().i(
|
||||||
'Successfully connected as ${userID?.localpart} with ${homeserver.toString()}',
|
'Successfully connected as ${userID.localpart} with ${homeserver.toString()}',
|
||||||
);
|
);
|
||||||
|
|
||||||
final syncFuture = _sync();
|
final syncFuture = _sync();
|
||||||
|
|
@ -1508,13 +1510,13 @@ class Client extends MatrixApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _sync() {
|
Future<void> _sync() {
|
||||||
final _currentSync = this._currentSync ??= _innerSync().whenComplete(() {
|
final currentSync = _currentSync ??= _innerSync().whenComplete(() {
|
||||||
this._currentSync = null;
|
_currentSync = null;
|
||||||
if (_backgroundSync && isLogged() && !_disposed) {
|
if (_backgroundSync && isLogged() && !_disposed) {
|
||||||
_sync();
|
_sync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return _currentSync;
|
return currentSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Presence that is set on sync.
|
/// Presence that is set on sync.
|
||||||
|
|
@ -1533,13 +1535,13 @@ class Client extends MatrixApi {
|
||||||
Future<void> _innerSync() async {
|
Future<void> _innerSync() async {
|
||||||
await _retryDelay;
|
await _retryDelay;
|
||||||
_retryDelay = Future.delayed(Duration(seconds: syncErrorTimeoutSec));
|
_retryDelay = Future.delayed(Duration(seconds: syncErrorTimeoutSec));
|
||||||
if (!isLogged() || _disposed || _aborted) return null;
|
if (!isLogged() || _disposed || _aborted) return;
|
||||||
try {
|
try {
|
||||||
if (_initLock) {
|
if (_initLock) {
|
||||||
Logs().d('Running sync while init isn\'t done yet, dropping request');
|
Logs().d('Running sync while init isn\'t done yet, dropping request');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var syncError;
|
dynamic syncError;
|
||||||
await _checkSyncFilter();
|
await _checkSyncFilter();
|
||||||
final syncRequest = sync(
|
final syncRequest = sync(
|
||||||
filter: syncFilterId,
|
filter: syncFilterId,
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import 'package:matrix/encryption/utils/olm_session.dart';
|
||||||
import 'package:matrix/encryption/utils/outbound_group_session.dart';
|
import 'package:matrix/encryption/utils/outbound_group_session.dart';
|
||||||
import 'package:matrix/encryption/utils/ssss_cache.dart';
|
import 'package:matrix/encryption/utils/ssss_cache.dart';
|
||||||
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:matrix/src/utils/queued_to_device_event.dart';
|
import 'package:matrix/src/utils/queued_to_device_event.dart';
|
||||||
import '../../matrix.dart';
|
|
||||||
|
|
||||||
abstract class DatabaseApi {
|
abstract class DatabaseApi {
|
||||||
int get maxFileSize => 1 * 1024 * 1024;
|
int get maxFileSize => 1 * 1024 * 1024;
|
||||||
|
|
@ -154,7 +154,7 @@ abstract class DatabaseApi {
|
||||||
);
|
);
|
||||||
|
|
||||||
Future storeOlmSession(
|
Future storeOlmSession(
|
||||||
String identitiyKey,
|
String identityKey,
|
||||||
String sessionId,
|
String sessionId,
|
||||||
String pickle,
|
String pickle,
|
||||||
int lastReceived,
|
int lastReceived,
|
||||||
|
|
|
||||||
|
|
@ -448,9 +448,9 @@ class FluffyBoxDatabase extends DatabaseApi {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<OlmSession>> getOlmSessionsForDevices(
|
Future<List<OlmSession>> getOlmSessionsForDevices(
|
||||||
List<String> identityKey, String userId) async {
|
List<String> identityKeys, String userId) async {
|
||||||
final sessions = await Future.wait(
|
final sessions = await Future.wait(
|
||||||
identityKey.map((identityKey) => getOlmSessions(identityKey, userId)));
|
identityKeys.map((identityKey) => getOlmSessions(identityKey, userId)));
|
||||||
return <OlmSession>[for (final sublist in sessions) ...sublist];
|
return <OlmSession>[for (final sublist in sessions) ...sublist];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1433,10 +1433,9 @@ class FluffyBoxDatabase extends DatabaseApi {
|
||||||
Future<void> addSeenDeviceId(
|
Future<void> addSeenDeviceId(
|
||||||
String userId,
|
String userId,
|
||||||
String deviceId,
|
String deviceId,
|
||||||
String publicKeysHash,
|
String publicKeys,
|
||||||
) =>
|
) =>
|
||||||
_seenDeviceIdsBox.put(
|
_seenDeviceIdsBox.put(TupleKey(userId, deviceId).toString(), publicKeys);
|
||||||
TupleKey(userId, deviceId).toString(), publicKeysHash);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addSeenPublicKey(
|
Future<void> addSeenPublicKey(
|
||||||
|
|
|
||||||
|
|
@ -474,9 +474,9 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<OlmSession>> getOlmSessionsForDevices(
|
Future<List<OlmSession>> getOlmSessionsForDevices(
|
||||||
List<String> identityKey, String userId) async {
|
List<String> identityKeys, String userId) async {
|
||||||
final sessions = await Future.wait(
|
final sessions = await Future.wait(
|
||||||
identityKey.map((identityKey) => getOlmSessions(identityKey, userId)));
|
identityKeys.map((identityKey) => getOlmSessions(identityKey, userId)));
|
||||||
return <OlmSession>[for (final sublist in sessions) ...sublist];
|
return <OlmSession>[for (final sublist in sessions) ...sublist];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1459,10 +1459,9 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
||||||
Future<void> addSeenDeviceId(
|
Future<void> addSeenDeviceId(
|
||||||
String userId,
|
String userId,
|
||||||
String deviceId,
|
String deviceId,
|
||||||
String publicKeysHash,
|
String publicKeys,
|
||||||
) =>
|
) =>
|
||||||
_seenDeviceIdsBox.put(
|
_seenDeviceIdsBox.put(TupleKey(userId, deviceId).toString(), publicKeys);
|
||||||
TupleKey(userId, deviceId).toString(), publicKeysHash);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addSeenPublicKey(
|
Future<void> addSeenPublicKey(
|
||||||
|
|
@ -1611,6 +1610,9 @@ class TupleKey {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(other) => parts.toString() == other.toString();
|
bool operator ==(other) => parts.toString() == other.toString();
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hashAll(parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic _castValue(dynamic value) {
|
dynamic _castValue(dynamic value) {
|
||||||
|
|
|
||||||
|
|
@ -505,9 +505,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<OlmSession>> getOlmSessionsForDevices(
|
Future<List<OlmSession>> getOlmSessionsForDevices(
|
||||||
List<String> identityKey, String userId) async {
|
List<String> identityKeys, String userId) async {
|
||||||
final sessions = await Future.wait(
|
final sessions = await Future.wait(
|
||||||
identityKey.map((identityKey) => getOlmSessions(identityKey, userId)));
|
identityKeys.map((identityKey) => getOlmSessions(identityKey, userId)));
|
||||||
return <OlmSession>[for (final sublist in sessions) ...sublist];
|
return <OlmSession>[for (final sublist in sessions) ...sublist];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1380,10 +1380,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
Future<void> addSeenDeviceId(
|
Future<void> addSeenDeviceId(
|
||||||
String userId,
|
String userId,
|
||||||
String deviceId,
|
String deviceId,
|
||||||
String publicKeysHash,
|
String publicKeys,
|
||||||
) =>
|
) =>
|
||||||
_seenDeviceIdsBox.put(
|
_seenDeviceIdsBox.put(MultiKey(userId, deviceId).toString(), publicKeys);
|
||||||
MultiKey(userId, deviceId).toString(), publicKeysHash);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addSeenPublicKey(
|
Future<void> addSeenPublicKey(
|
||||||
|
|
@ -1461,6 +1460,9 @@ class MultiKey {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(other) => parts.toString() == other.toString();
|
bool operator ==(other) => parts.toString() == other.toString();
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hashAll(parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
extension HiveKeyExtension on String {
|
extension HiveKeyExtension on String {
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
|
import 'package:matrix/src/utils/event_localizations.dart';
|
||||||
import 'package:matrix/src/utils/file_send_request_credentials.dart';
|
import 'package:matrix/src/utils/file_send_request_credentials.dart';
|
||||||
import '../matrix.dart';
|
import 'package:matrix/src/utils/html_to_text.dart';
|
||||||
import 'utils/event_localizations.dart';
|
import 'package:matrix/src/utils/markdown.dart';
|
||||||
import 'utils/html_to_text.dart';
|
|
||||||
import 'utils/markdown.dart';
|
|
||||||
|
|
||||||
abstract class RelationshipTypes {
|
abstract class RelationshipTypes {
|
||||||
static const String reply = 'm.in_reply_to';
|
static const String reply = 'm.in_reply_to';
|
||||||
|
|
@ -296,7 +296,7 @@ class Event extends MatrixEvent {
|
||||||
if (redacted) return 'Redacted';
|
if (redacted) return 'Redacted';
|
||||||
if (text != '') return text;
|
if (text != '') return text;
|
||||||
if (formattedText != '') return formattedText;
|
if (formattedText != '') return formattedText;
|
||||||
return '$type';
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use this to get a plain-text representation of the event, stripping things
|
/// Use this to get a plain-text representation of the event, stripping things
|
||||||
|
|
@ -870,7 +870,7 @@ class Event extends MatrixEvent {
|
||||||
bool get onlyEmotes {
|
bool get onlyEmotes {
|
||||||
if (isRichMessage) {
|
if (isRichMessage) {
|
||||||
final formattedTextStripped = formattedText.replaceAll(
|
final formattedTextStripped = formattedText.replaceAll(
|
||||||
RegExp('<mx-reply>.*<\/mx-reply>',
|
RegExp('<mx-reply>.*</mx-reply>',
|
||||||
caseSensitive: false, multiLine: false, dotAll: true),
|
caseSensitive: false, multiLine: false, dotAll: true),
|
||||||
'');
|
'');
|
||||||
return _onlyEmojiEmoteRegex.hasMatch(formattedTextStripped);
|
return _onlyEmojiEmoteRegex.hasMatch(formattedTextStripped);
|
||||||
|
|
@ -886,7 +886,7 @@ class Event extends MatrixEvent {
|
||||||
int get numberEmotes {
|
int get numberEmotes {
|
||||||
if (isRichMessage) {
|
if (isRichMessage) {
|
||||||
final formattedTextStripped = formattedText.replaceAll(
|
final formattedTextStripped = formattedText.replaceAll(
|
||||||
RegExp('<mx-reply>.*<\/mx-reply>',
|
RegExp('<mx-reply>.*</mx-reply>',
|
||||||
caseSensitive: false, multiLine: false, dotAll: true),
|
caseSensitive: false, multiLine: false, dotAll: true),
|
||||||
'');
|
'');
|
||||||
return _countEmojiEmoteRegex.allMatches(formattedTextStripped).length;
|
return _countEmojiEmoteRegex.allMatches(formattedTextStripped).length;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
class TimelineChunk {
|
class TimelineChunk {
|
||||||
String prevBatch; // pos of the first event of the database timeline chunk
|
String prevBatch; // pos of the first event of the database timeline chunk
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
class CachedPresence {
|
class CachedPresence {
|
||||||
PresenceType presence;
|
PresenceType presence;
|
||||||
|
|
@ -45,9 +45,7 @@ class CachedPresence {
|
||||||
: this(event.presence, event.lastActiveAgo, event.statusMsg,
|
: this(event.presence, event.lastActiveAgo, event.statusMsg,
|
||||||
event.currentlyActive, userid);
|
event.currentlyActive, userid);
|
||||||
|
|
||||||
CachedPresence.neverSeen(String userid)
|
CachedPresence.neverSeen(this.userid) : presence = PresenceType.offline;
|
||||||
: presence = PresenceType.offline,
|
|
||||||
userid = userid;
|
|
||||||
|
|
||||||
Presence toPresence() {
|
Presence toPresence() {
|
||||||
final content = <String, dynamic>{
|
final content = <String, dynamic>{
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,14 @@ import 'dart:typed_data';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:html_unescape/html_unescape.dart';
|
import 'package:html_unescape/html_unescape.dart';
|
||||||
|
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:matrix/src/models/timeline_chunk.dart';
|
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||||
import 'package:matrix/src/utils/crypto/crypto.dart';
|
import 'package:matrix/src/utils/crypto/crypto.dart';
|
||||||
import 'package:matrix/src/utils/file_send_request_credentials.dart';
|
import 'package:matrix/src/utils/file_send_request_credentials.dart';
|
||||||
|
import 'package:matrix/src/utils/markdown.dart';
|
||||||
|
import 'package:matrix/src/utils/marked_unread.dart';
|
||||||
import 'package:matrix/src/utils/space_child.dart';
|
import 'package:matrix/src/utils/space_child.dart';
|
||||||
import '../matrix.dart';
|
|
||||||
import 'utils/markdown.dart';
|
|
||||||
import 'utils/marked_unread.dart';
|
|
||||||
|
|
||||||
enum PushRuleState { notify, mentionsOnly, dontNotify }
|
enum PushRuleState { notify, mentionsOnly, dontNotify }
|
||||||
|
|
||||||
|
|
@ -907,7 +907,7 @@ class Room {
|
||||||
}
|
}
|
||||||
|
|
||||||
inPrefix = false;
|
inPrefix = false;
|
||||||
temp += temp.isEmpty ? l : ('\n' + l);
|
temp += temp.isEmpty ? l : ('\n$l');
|
||||||
}
|
}
|
||||||
|
|
||||||
return temp;
|
return temp;
|
||||||
|
|
@ -936,7 +936,7 @@ class Room {
|
||||||
|
|
||||||
if (inReplyTo != null) {
|
if (inReplyTo != null) {
|
||||||
var replyText =
|
var replyText =
|
||||||
'<${inReplyTo.senderId}> ' + _stripBodyFallback(inReplyTo.body);
|
'<${inReplyTo.senderId}> ${_stripBodyFallback(inReplyTo.body)}';
|
||||||
replyText = replyText.split('\n').map((line) => '> $line').join('\n');
|
replyText = replyText.split('\n').map((line) => '> $line').join('\n');
|
||||||
content['format'] = 'org.matrix.custom.html';
|
content['format'] = 'org.matrix.custom.html';
|
||||||
// be sure that we strip any previous reply fallbacks
|
// be sure that we strip any previous reply fallbacks
|
||||||
|
|
@ -971,10 +971,10 @@ class Room {
|
||||||
'rel_type': RelationshipTypes.edit,
|
'rel_type': RelationshipTypes.edit,
|
||||||
};
|
};
|
||||||
if (content['body'] is String) {
|
if (content['body'] is String) {
|
||||||
content['body'] = '* ' + content['body'];
|
content['body'] = '* ${content['body']}';
|
||||||
}
|
}
|
||||||
if (content['formatted_body'] is String) {
|
if (content['formatted_body'] is String) {
|
||||||
content['formatted_body'] = '* ' + content['formatted_body'];
|
content['formatted_body'] = '* ${content['formatted_body']}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final sentDate = DateTime.now();
|
final sentDate = DateTime.now();
|
||||||
|
|
@ -1113,7 +1113,7 @@ class Room {
|
||||||
/// power level event, there might something broken and this returns null.
|
/// power level event, there might something broken and this returns null.
|
||||||
Future<String> setPower(String userID, int power) async {
|
Future<String> setPower(String userID, int power) async {
|
||||||
var powerMap = getState(EventTypes.RoomPowerLevels)?.content;
|
var powerMap = getState(EventTypes.RoomPowerLevels)?.content;
|
||||||
if (!(powerMap is Map<String, dynamic>)) {
|
if (powerMap is! Map<String, dynamic>) {
|
||||||
powerMap = <String, dynamic>{};
|
powerMap = <String, dynamic>{};
|
||||||
}
|
}
|
||||||
(powerMap['users'] ??= {})[userID] = power;
|
(powerMap['users'] ??= {})[userID] = power;
|
||||||
|
|
@ -1155,7 +1155,7 @@ class Room {
|
||||||
if (onHistoryReceived != null) onHistoryReceived();
|
if (onHistoryReceived != null) onHistoryReceived();
|
||||||
this.prev_batch = resp.end;
|
this.prev_batch = resp.end;
|
||||||
|
|
||||||
final loadFn = () async {
|
Future<void> loadFn() async {
|
||||||
if (!((resp.chunk.isNotEmpty) && resp.end != null)) return;
|
if (!((resp.chunk.isNotEmpty) && resp.end != null)) return;
|
||||||
|
|
||||||
await client.handleSync(
|
await client.handleSync(
|
||||||
|
|
@ -1196,7 +1196,7 @@ class Room {
|
||||||
: null),
|
: null),
|
||||||
),
|
),
|
||||||
direction: Direction.b);
|
direction: Direction.b);
|
||||||
};
|
}
|
||||||
|
|
||||||
if (client.database != null) {
|
if (client.database != null) {
|
||||||
await client.database?.transaction(() async {
|
await client.database?.transaction(() async {
|
||||||
|
|
@ -1331,7 +1331,7 @@ class Room {
|
||||||
String? eventContextId}) async {
|
String? eventContextId}) async {
|
||||||
await postLoad();
|
await postLoad();
|
||||||
|
|
||||||
var events;
|
List<Event> events;
|
||||||
|
|
||||||
if (!isArchived) {
|
if (!isArchived) {
|
||||||
events = await client.database?.getEventList(
|
events = await client.database?.getEventList(
|
||||||
|
|
@ -1762,7 +1762,7 @@ class Room {
|
||||||
PushRuleState get pushRuleState {
|
PushRuleState get pushRuleState {
|
||||||
final globalPushRules =
|
final globalPushRules =
|
||||||
client.accountData['m.push_rules']?.content['global'];
|
client.accountData['m.push_rules']?.content['global'];
|
||||||
if (!(globalPushRules is Map)) {
|
if (globalPushRules is! Map) {
|
||||||
return PushRuleState.notify;
|
return PushRuleState.notify;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1794,7 +1794,7 @@ class Room {
|
||||||
/// Sends a request to the homeserver to set the [PushRuleState] for this room.
|
/// Sends a request to the homeserver to set the [PushRuleState] for this room.
|
||||||
/// Returns ErrorResponse if something goes wrong.
|
/// Returns ErrorResponse if something goes wrong.
|
||||||
Future<void> setPushRuleState(PushRuleState newState) async {
|
Future<void> setPushRuleState(PushRuleState newState) async {
|
||||||
if (newState == pushRuleState) return null;
|
if (newState == pushRuleState) return;
|
||||||
dynamic resp;
|
dynamic resp;
|
||||||
switch (newState) {
|
switch (newState) {
|
||||||
// All push notifications should be sent to the user
|
// All push notifications should be sent to the user
|
||||||
|
|
@ -2076,6 +2076,9 @@ class Room {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) => (other is Room && other.id == id);
|
bool operator ==(dynamic other) => (other is Room && other.id == id);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hashAll([id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EncryptionHealthState {
|
enum EncryptionHealthState {
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:collection/src/iterable_extensions.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
import '../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'models/timeline_chunk.dart';
|
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||||
|
|
||||||
/// Represents the timeline of a room. The callback [onUpdate] will be triggered
|
/// Represents the timeline of a room. The callback [onUpdate] will be triggered
|
||||||
/// automatically. The initial
|
/// automatically. The initial
|
||||||
|
|
@ -305,7 +305,7 @@ class Timeline {
|
||||||
|
|
||||||
void _sessionKeyReceived(String sessionId) async {
|
void _sessionKeyReceived(String sessionId) async {
|
||||||
var decryptAtLeastOneEvent = false;
|
var decryptAtLeastOneEvent = false;
|
||||||
final decryptFn = () async {
|
Future<void> decryptFn() async {
|
||||||
final encryption = room.client.encryption;
|
final encryption = room.client.encryption;
|
||||||
if (!room.client.encryptionEnabled || encryption == null) {
|
if (!room.client.encryptionEnabled || encryption == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -322,7 +322,8 @@ class Timeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
if (room.client.database != null) {
|
if (room.client.database != null) {
|
||||||
await room.client.database?.transaction(decryptFn);
|
await room.client.database?.transaction(decryptFn);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
/// Represents a Matrix User which may be a participant in a Matrix Room.
|
/// Represents a Matrix User which may be a participant in a Matrix Room.
|
||||||
class User extends Event {
|
class User extends Event {
|
||||||
|
|
@ -80,7 +80,7 @@ class User extends Event {
|
||||||
/// ban
|
/// ban
|
||||||
Membership get membership => Membership.values.firstWhere((e) {
|
Membership get membership => Membership.values.firstWhere((e) {
|
||||||
if (content['membership'] != null) {
|
if (content['membership'] != null) {
|
||||||
return e.toString() == 'Membership.' + content['membership'];
|
return e.toString() == 'Membership.${content['membership']}';
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}, orElse: () => Membership.join);
|
}, orElse: () => Membership.join);
|
||||||
|
|
@ -197,6 +197,9 @@ class User extends Event {
|
||||||
other.room == room &&
|
other.room == room &&
|
||||||
other.membership == membership);
|
other.membership == membership);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(id, room, membership);
|
||||||
|
|
||||||
/// Get the mention text to use in a plain text body to mention this specific user
|
/// Get the mention text to use in a plain text body to mention this specific user
|
||||||
/// in this specific room
|
/// in this specific room
|
||||||
String get mention {
|
String get mention {
|
||||||
|
|
@ -210,11 +213,8 @@ class User extends Event {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
final identifier = '@' +
|
final identifier =
|
||||||
// if we have non-word characters we need to surround with []
|
'@${RegExp(r'^\w+$').hasMatch(displayName) ? displayName : '[$displayName]'}';
|
||||||
(RegExp(r'^\w+$').hasMatch(displayName)
|
|
||||||
? displayName
|
|
||||||
: '[$displayName]');
|
|
||||||
|
|
||||||
// get all the users with the same display name
|
// get all the users with the same display name
|
||||||
final allUsersWithSameDisplayname = room.getParticipants();
|
final allUsersWithSameDisplayname = room.getParticipants();
|
||||||
|
|
@ -243,11 +243,8 @@ class User extends Event {
|
||||||
{'[', ']', ':'}.any(displayName.contains)) {
|
{'[', ']', ':'}.any(displayName.contains)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
final identifier = '@' +
|
final identifier =
|
||||||
// if we have non-word characters we need to surround with []
|
'@${RegExp(r'^\w+$').hasMatch(displayName) ? displayName : '[$displayName]'}';
|
||||||
(RegExp(r'^\w+$').hasMatch(displayName)
|
|
||||||
? displayName
|
|
||||||
: '[$displayName]');
|
|
||||||
|
|
||||||
final hash = _hash(id);
|
final hash = _hash(id);
|
||||||
return {identifier, '$identifier#$hash'};
|
return {identifier, '$identifier#$hash'};
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
extension CommandsClientExtension on Client {
|
extension CommandsClientExtension on Client {
|
||||||
/// Add a command to the command handler. `command` is its name, and `callback` is the
|
/// Add a command to the command handler. `command` is its name, and `callback` is the
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import 'dart:convert';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||||
import 'crypto.dart';
|
import 'package:matrix/src/utils/crypto/crypto.dart';
|
||||||
|
|
||||||
class EncryptedFile {
|
class EncryptedFile {
|
||||||
EncryptedFile({
|
EncryptedFile({
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'subtle.dart' as subtle;
|
import 'package:matrix/src/utils/crypto/subtle.dart' as subtle;
|
||||||
import 'subtle.dart';
|
import 'package:matrix/src/utils/crypto/subtle.dart';
|
||||||
|
|
||||||
abstract class Hash {
|
abstract class Hash {
|
||||||
Hash._(this.name);
|
Hash._(this.name);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
import 'ffi.dart';
|
import 'package:matrix/src/utils/crypto/ffi.dart';
|
||||||
|
|
||||||
abstract class Hash {
|
abstract class Hash {
|
||||||
Hash._(this.ptr);
|
Hash._(this.ptr);
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import 'package:canonical_json/canonical_json.dart';
|
||||||
import 'package:collection/collection.dart' show IterableExtension;
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
|
import 'package:matrix/encryption.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import '../../encryption.dart';
|
|
||||||
|
|
||||||
enum UserVerifiedStatus { verified, unknown, unknownDevice }
|
enum UserVerifiedStatus { verified, unknown, unknownDevice }
|
||||||
|
|
||||||
|
|
@ -349,6 +349,9 @@ abstract class SignableKey extends MatrixSignableKey {
|
||||||
bool operator ==(dynamic other) => (other is SignableKey &&
|
bool operator ==(dynamic other) => (other is SignableKey &&
|
||||||
other.userId == userId &&
|
other.userId == userId &&
|
||||||
other.identifier == identifier);
|
other.identifier == identifier);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(userId, identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CrossSigningKey extends SignableKey {
|
class CrossSigningKey extends SignableKey {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
import '../../encryption.dart';
|
import 'package:matrix/encryption.dart';
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
abstract class EventLocalizations {
|
abstract class EventLocalizations {
|
||||||
// As we need to create the localized body off of a different set of parameters, we
|
// As we need to create the localized body off of a different set of parameters, we
|
||||||
|
|
@ -54,16 +54,16 @@ abstract class EventLocalizations {
|
||||||
String errorText;
|
String errorText;
|
||||||
switch (event.body) {
|
switch (event.body) {
|
||||||
case DecryptException.channelCorrupted:
|
case DecryptException.channelCorrupted:
|
||||||
errorText = i18n.channelCorruptedDecryptError + '.';
|
errorText = '${i18n.channelCorruptedDecryptError}.';
|
||||||
break;
|
break;
|
||||||
case DecryptException.notEnabled:
|
case DecryptException.notEnabled:
|
||||||
errorText = i18n.encryptionNotEnabled + '.';
|
errorText = '${i18n.encryptionNotEnabled}.';
|
||||||
break;
|
break;
|
||||||
case DecryptException.unknownAlgorithm:
|
case DecryptException.unknownAlgorithm:
|
||||||
errorText = i18n.unknownEncryptionAlgorithm + '.';
|
errorText = '${i18n.unknownEncryptionAlgorithm}.';
|
||||||
break;
|
break;
|
||||||
case DecryptException.unknownSession:
|
case DecryptException.unknownSession:
|
||||||
errorText = i18n.noPermission + '.';
|
errorText = '${i18n.noPermission}.';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
errorText = body;
|
errorText = body;
|
||||||
|
|
@ -240,7 +240,7 @@ abstract class EventLocalizations {
|
||||||
var localizedBody = i18n.activatedEndToEndEncryption(
|
var localizedBody = i18n.activatedEndToEndEncryption(
|
||||||
event.senderFromMemoryOrFallback.calcDisplayname());
|
event.senderFromMemoryOrFallback.calcDisplayname());
|
||||||
if (event.room.client.encryptionEnabled == false) {
|
if (event.room.client.encryptionEnabled == false) {
|
||||||
localizedBody += '. ' + i18n.needPantalaimonWarning;
|
localizedBody += '. ${i18n.needPantalaimonWarning}';
|
||||||
}
|
}
|
||||||
return localizedBody;
|
return localizedBody;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
enum EventUpdateType {
|
enum EventUpdateType {
|
||||||
timeline,
|
timeline,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class HtmlToText {
|
||||||
// miss-matching tags, and this way we actually correctly identify what we want to strip and, well,
|
// miss-matching tags, and this way we actually correctly identify what we want to strip and, well,
|
||||||
// strip it.
|
// strip it.
|
||||||
final renderHtml = html.replaceAll(
|
final renderHtml = html.replaceAll(
|
||||||
RegExp('<mx-reply>.*<\/mx-reply>',
|
RegExp('<mx-reply>.*</mx-reply>',
|
||||||
caseSensitive: false, multiLine: false, dotAll: true),
|
caseSensitive: false, multiLine: false, dotAll: true),
|
||||||
'');
|
'');
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ class HtmlToText {
|
||||||
|
|
||||||
static String _parseBlockquoteContent(_ConvertOpts opts, Element node) {
|
static String _parseBlockquoteContent(_ConvertOpts opts, Element node) {
|
||||||
final msg = _walkChildNodes(opts, node);
|
final msg = _walkChildNodes(opts, node);
|
||||||
return msg.split('\n').map((s) => '> $s').join('\n') + '\n';
|
return '${msg.split('\n').map((s) => '> $s').join('\n')}\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
static String _parseSpanContent(_ConvertOpts opts, Element node) {
|
static String _parseSpanContent(_ConvertOpts opts, Element node) {
|
||||||
|
|
@ -109,10 +109,7 @@ class HtmlToText {
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
.map((s) =>
|
.map((s) =>
|
||||||
(' ' * opts.listDepth) +
|
'${' ' * opts.listDepth}$bulletPoint ${s.replaceAll('\n', '\n${' ' * opts.listDepth} ')}')
|
||||||
bulletPoint +
|
|
||||||
' ' +
|
|
||||||
s.replaceAll('\n', '\n' + (' ' * opts.listDepth) + ' '))
|
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,9 +125,7 @@ class HtmlToText {
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
.mapIndexed((index, s) =>
|
.mapIndexed((index, s) =>
|
||||||
(' ' * opts.listDepth) +
|
'${' ' * opts.listDepth}${start + index}. ${s.replaceAll('\n', '\n${' ' * opts.listDepth} ')}')
|
||||||
'${start + index}. ' +
|
|
||||||
s.replaceAll('\n', '\n' + (' ' * opts.listDepth) + ' '))
|
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
/// Stream.timeout fails if no progress is made in timeLimit.
|
/// Stream.timeout fails if no progress is made in timeLimit.
|
||||||
/// In contrast, streamTotalTimeout fails if the stream isn't completed
|
/// In contrast, streamTotalTimeout fails if the stream isn't completed
|
||||||
|
|
@ -68,10 +68,6 @@ class FixedTimeoutHttpClient extends TimeoutHttpClient {
|
||||||
FixedTimeoutHttpClient(http.Client inner, this.timeout) : super(inner);
|
FixedTimeoutHttpClient(http.Client inner, this.timeout) : super(inner);
|
||||||
@override
|
@override
|
||||||
Duration timeout;
|
Duration timeout;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<http.StreamedResponse> send(http.BaseRequest request) =>
|
|
||||||
super.send(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class VariableTimeoutHttpClient extends TimeoutHttpClient {
|
class VariableTimeoutHttpClient extends TimeoutHttpClient {
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,15 @@
|
||||||
import 'package:matrix_api_lite/matrix_api_lite.dart';
|
import 'package:matrix_api_lite/matrix_api_lite.dart';
|
||||||
import 'package:slugify/slugify.dart';
|
import 'package:slugify/slugify.dart';
|
||||||
|
|
||||||
import '../room.dart';
|
import 'package:matrix/src/room.dart';
|
||||||
|
|
||||||
extension ImagePackRoomExtension on Room {
|
extension ImagePackRoomExtension on Room {
|
||||||
/// Get all the active image packs for the specified [usage], mapped by their slug
|
/// Get all the active image packs for the specified [usage], mapped by their slug
|
||||||
Map<String, ImagePackContent> getImagePacks([ImagePackUsage? usage]) {
|
Map<String, ImagePackContent> getImagePacks([ImagePackUsage? usage]) {
|
||||||
final allMxcs = <Uri>{}; // used for easy deduplication
|
final allMxcs = <Uri>{}; // used for easy deduplication
|
||||||
final packs = <String, ImagePackContent>{};
|
final packs = <String, ImagePackContent>{};
|
||||||
final addImagePack = (BasicEvent? event, {Room? room, String? slug}) {
|
|
||||||
|
void addImagePack(BasicEvent? event, {Room? room, String? slug}) {
|
||||||
if (event == null) return;
|
if (event == null) return;
|
||||||
final imagePack = event.parsedImagePackContent;
|
final imagePack = event.parsedImagePackContent;
|
||||||
final finalSlug = slugify(slug ?? 'pack');
|
final finalSlug = slugify(slug ?? 'pack');
|
||||||
|
|
@ -53,7 +54,8 @@ extension ImagePackRoomExtension on Room {
|
||||||
.images[entry.key] = image;
|
.images[entry.key] = image;
|
||||||
allMxcs.add(image.url);
|
allMxcs.add(image.url);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// first we add the user image pack
|
// first we add the user image pack
|
||||||
addImagePack(client.accountData['im.ponies.user_emotes'], slug: 'user');
|
addImagePack(client.accountData['im.ponies.user_emotes'], slug: 'user');
|
||||||
// next we add all the external image packs
|
// next we add all the external image packs
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:matrix_api_lite/src/utils/try_get_map_extension.dart';
|
import 'package:matrix_api_lite/matrix_api_lite.dart';
|
||||||
|
|
||||||
mixin EventType {
|
mixin EventType {
|
||||||
static const String markedUnread = 'com.famedly.marked_unread';
|
static const String markedUnread = 'com.famedly.marked_unread';
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ import 'package:blurhash_dart/blurhash_dart.dart';
|
||||||
import 'package:image/image.dart';
|
import 'package:image/image.dart';
|
||||||
import 'package:mime/mime.dart';
|
import 'package:mime/mime.dart';
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'compute_callback.dart';
|
import 'package:matrix/src/utils/compute_callback.dart';
|
||||||
|
|
||||||
class MatrixFile {
|
class MatrixFile {
|
||||||
final Uint8List bytes;
|
final Uint8List bytes;
|
||||||
|
|
|
||||||
|
|
@ -81,14 +81,8 @@ extension MatrixIdExtension on String {
|
||||||
}
|
}
|
||||||
return uri.replace(pathSegments: identifiers);
|
return uri.replace(pathSegments: identifiers);
|
||||||
} else if (toLowerCase().startsWith(matrixToPrefix)) {
|
} else if (toLowerCase().startsWith(matrixToPrefix)) {
|
||||||
return Uri.tryParse('//' +
|
return Uri.tryParse(
|
||||||
substring(matrixToPrefix.length - 1)
|
'//${substring(matrixToPrefix.length - 1).replaceAllMapped(RegExp(r'(?<=/)[#!@+][^:]*:|(\?.*$)'), (m) => m[0]!.replaceAllMapped(RegExp(m.group(1) != null ? '' : '[/?]'), (m) => Uri.encodeComponent(m.group(0)!))).replaceAll('#', '%23')}');
|
||||||
.replaceAllMapped(
|
|
||||||
RegExp(r'(?<=/)[#!@+][^:]*:|(\?.*$)'),
|
|
||||||
(m) => m[0]!.replaceAllMapped(
|
|
||||||
RegExp(m.group(1) != null ? '' : '[/?]'),
|
|
||||||
(m) => Uri.encodeComponent(m.group(0)!)))
|
|
||||||
.replaceAll('#', '%23'));
|
|
||||||
} else {
|
} else {
|
||||||
return Uri(
|
return Uri(
|
||||||
pathSegments: RegExp(r'/((?:[#!@+][^:]*:)?[^/?]*)(?:\?.*$)?')
|
pathSegments: RegExp(r'/((?:[#!@+][^:]*:)?[^/?]*)(?:\?.*$)?')
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../room.dart';
|
import 'package:matrix/src/room.dart';
|
||||||
|
|
||||||
abstract class MatrixLocalizations {
|
abstract class MatrixLocalizations {
|
||||||
const MatrixLocalizations();
|
const MatrixLocalizations();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:matrix/encryption.dart';
|
import 'package:matrix/encryption.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'compute_callback.dart';
|
import 'package:matrix/src/utils/compute_callback.dart';
|
||||||
|
|
||||||
/// provides native implementations for demanding arithmetic operations
|
/// provides native implementations for demanding arithmetic operations
|
||||||
/// in order to prevent the UI from blocking
|
/// in order to prevent the UI from blocking
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../user.dart';
|
import 'package:matrix/src/user.dart';
|
||||||
|
|
||||||
/// Represents a receipt.
|
/// Represents a receipt.
|
||||||
/// This [user] has read an event at the given [time].
|
/// This [user] has read an event at the given [time].
|
||||||
|
|
@ -30,4 +30,7 @@ class Receipt {
|
||||||
bool operator ==(dynamic other) => (other is Receipt &&
|
bool operator ==(dynamic other) => (other is Receipt &&
|
||||||
other.user == user &&
|
other.user == user &&
|
||||||
other.time.microsecondsSinceEpoch == time.microsecondsSinceEpoch);
|
other.time.microsecondsSinceEpoch == time.microsecondsSinceEpoch);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(user, time);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
Future<T?> runInRoot<T>(FutureOr<T> Function() fn) async {
|
Future<T?> runInRoot<T>(FutureOr<T> Function() fn) async {
|
||||||
return await Zone.root.run(() async {
|
return await Zone.root.run(() async {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import 'package:matrix_api_lite/matrix_api_lite.dart';
|
import 'package:matrix_api_lite/matrix_api_lite.dart';
|
||||||
|
|
||||||
import '../event.dart';
|
import 'package:matrix/src/event.dart';
|
||||||
|
|
||||||
class SpaceChild {
|
class SpaceChild {
|
||||||
final String? roomId;
|
final String? roomId;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
/// This extension adds easy-to-use filters for the sync update, meant to be used on the `client.onSync` stream, e.g.
|
/// This extension adds easy-to-use filters for the sync update, meant to be used on the `client.onSync` stream, e.g.
|
||||||
/// `client.onSync.stream.where((s) => s.hasRoomUpdate)`. Multiple filters can easily be
|
/// `client.onSync.stream.where((s) => s.hasRoomUpdate)`. Multiple filters can easily be
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
class ToDeviceEvent extends BasicEventWithSender {
|
class ToDeviceEvent extends BasicEventWithSender {
|
||||||
Map<String, dynamic>? encryptedContent;
|
Map<String, dynamic>? encryptedContent;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
enum UiaRequestState {
|
enum UiaRequestState {
|
||||||
/// The request is done
|
/// The request is done
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
import '../client.dart';
|
import 'package:matrix/src/client.dart';
|
||||||
|
|
||||||
extension MxcUriExtension on Uri {
|
extension MxcUriExtension on Uri {
|
||||||
/// Returns a download Link to this content.
|
/// Returns a download Link to this content.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import 'package:js/js_util.dart';
|
||||||
|
|
||||||
import 'package:matrix/encryption.dart';
|
import 'package:matrix/encryption.dart';
|
||||||
import 'package:matrix/matrix.dart' hide Event;
|
import 'package:matrix/matrix.dart' hide Event;
|
||||||
import 'native_implementations_web_worker.dart';
|
import 'package:matrix/src/utils/web_worker/native_implementations_web_worker.dart';
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ import 'dart:core';
|
||||||
|
|
||||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||||
|
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||||
import '../../matrix.dart';
|
|
||||||
|
|
||||||
/// https://github.com/matrix-org/matrix-doc/pull/2746
|
/// https://github.com/matrix-org/matrix-doc/pull/2746
|
||||||
/// version 1
|
/// version 1
|
||||||
|
|
@ -997,7 +997,7 @@ class CallSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onRejectReceived(String? reason) async {
|
Future<void> onRejectReceived(String? reason) async {
|
||||||
Logs().v('[VOIP] Reject received for call ID ' + callId);
|
Logs().v('[VOIP] Reject received for call ID $callId');
|
||||||
// No need to check party_id for reject because if we'd received either
|
// No need to check party_id for reject because if we'd received either
|
||||||
// an answer or reject, we wouldn't be in state InviteSent
|
// an answer or reject, we wouldn't be in state InviteSent
|
||||||
final shouldTerminate = (state == CallState.kFledgling &&
|
final shouldTerminate = (state == CallState.kFledgling &&
|
||||||
|
|
|
||||||
|
|
@ -28,5 +28,5 @@ String roomAliasFromRoomName(String roomName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
String genCallID() {
|
String genCallID() {
|
||||||
return '${DateTime.now().millisecondsSinceEpoch}' + randomAlphaNumeric(16);
|
return '${DateTime.now().millisecondsSinceEpoch}${randomAlphaNumeric(16)}';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import 'dart:core';
|
||||||
import 'package:sdp_transform/sdp_transform.dart' as sdp_transform;
|
import 'package:sdp_transform/sdp_transform.dart' as sdp_transform;
|
||||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||||
|
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||||
import '../../matrix.dart';
|
|
||||||
|
|
||||||
/// Delegate WebRTC basic functionality.
|
/// Delegate WebRTC basic functionality.
|
||||||
abstract class WebRTCDelegate {
|
abstract class WebRTCDelegate {
|
||||||
|
|
@ -317,7 +317,7 @@ class VoIP {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String callId = content['call_id'];
|
final String callId = content['call_id'];
|
||||||
Logs().d('Reject received for call ID ' + callId);
|
Logs().d('Reject received for call ID $callId');
|
||||||
|
|
||||||
final call = calls[callId];
|
final call = calls[callId];
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
|
|
@ -339,7 +339,7 @@ class VoIP {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String callId = content['call_id'];
|
final String callId = content['call_id'];
|
||||||
Logs().d('onCallReplaces received for call ID ' + callId);
|
Logs().d('onCallReplaces received for call ID $callId');
|
||||||
final call = calls[callId];
|
final call = calls[callId];
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
if (call.room.id != roomId) {
|
if (call.room.id != roomId) {
|
||||||
|
|
@ -358,7 +358,7 @@ class VoIP {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String callId = content['call_id'];
|
final String callId = content['call_id'];
|
||||||
Logs().d('SelectAnswer received for call ID ' + callId);
|
Logs().d('SelectAnswer received for call ID $callId');
|
||||||
final call = calls[callId];
|
final call = calls[callId];
|
||||||
final String selectedPartyId = content['selected_party_id'];
|
final String selectedPartyId = content['selected_party_id'];
|
||||||
|
|
||||||
|
|
@ -379,7 +379,7 @@ class VoIP {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String callId = content['call_id'];
|
final String callId = content['call_id'];
|
||||||
Logs().d('SDP Stream metadata received for call ID ' + callId);
|
Logs().d('SDP Stream metadata received for call ID $callId');
|
||||||
final call = calls[callId];
|
final call = calls[callId];
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
if (call.room.id != roomId) {
|
if (call.room.id != roomId) {
|
||||||
|
|
@ -404,7 +404,7 @@ class VoIP {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String callId = content['call_id'];
|
final String callId = content['call_id'];
|
||||||
Logs().d('Asserted identity received for call ID ' + callId);
|
Logs().d('Asserted identity received for call ID $callId');
|
||||||
final call = calls[callId];
|
final call = calls[callId];
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
if (call.room.id != roomId) {
|
if (call.room.id != roomId) {
|
||||||
|
|
@ -429,7 +429,7 @@ class VoIP {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String callId = content['call_id'];
|
final String callId = content['call_id'];
|
||||||
Logs().d('Negotiate received for call ID ' + callId);
|
Logs().d('Negotiate received for call ID $callId');
|
||||||
final call = calls[callId];
|
final call = calls[callId];
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
if (call.room.id != roomId) {
|
if (call.room.id != roomId) {
|
||||||
|
|
|
||||||
44
pubspec.yaml
44
pubspec.yaml
|
|
@ -9,33 +9,33 @@ environment:
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
async: ^2.8.0
|
async: ^2.8.0
|
||||||
blurhash_dart: ^1.1.0
|
|
||||||
http: ^0.13.0
|
|
||||||
mime: ^1.0.0
|
|
||||||
canonical_json: ^1.1.0
|
|
||||||
markdown: ^4.0.0
|
|
||||||
html_unescape: ^2.0.0
|
|
||||||
random_string: ^2.3.1
|
|
||||||
crypto: ^3.0.0
|
|
||||||
base58check: ^2.0.0
|
base58check: ^2.0.0
|
||||||
olm: ^2.0.0
|
blurhash_dart: ^1.1.0
|
||||||
matrix_api_lite: ^1.1.0
|
canonical_json: ^1.1.0
|
||||||
hive: ^2.2.1
|
|
||||||
image: ^3.1.1
|
|
||||||
ffi: ^1.0.0
|
|
||||||
js: ^0.6.3
|
|
||||||
slugify: ^2.0.0
|
|
||||||
html: ^0.15.0
|
|
||||||
collection: ^1.15.0
|
collection: ^1.15.0
|
||||||
webrtc_interface: ^1.0.1
|
crypto: ^3.0.0
|
||||||
sdp_transform: ^0.3.2
|
ffi: ^1.0.0
|
||||||
fluffybox: ^0.4.3
|
fluffybox: ^0.4.3
|
||||||
|
hive: ^2.2.1
|
||||||
|
html: ^0.15.0
|
||||||
|
html_unescape: ^2.0.0
|
||||||
|
http: ^0.13.0
|
||||||
|
image: ^3.1.1
|
||||||
|
js: ^0.6.3
|
||||||
|
markdown: ^4.0.0
|
||||||
|
matrix_api_lite: ^1.1.0
|
||||||
|
mime: ^1.0.0
|
||||||
|
olm: ^2.0.0
|
||||||
|
random_string: ^2.3.1
|
||||||
|
sdp_transform: ^0.3.2
|
||||||
|
slugify: ^2.0.0
|
||||||
|
webrtc_interface: ^1.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
import_sorter: ^4.6.0
|
|
||||||
dart_code_metrics: ^4.10.1
|
|
||||||
pedantic: ^1.11.0
|
|
||||||
test: ^1.15.7
|
|
||||||
coverage: ">=0.15.0 <2.0.0"
|
coverage: ">=0.15.0 <2.0.0"
|
||||||
|
dart_code_metrics: ^4.10.1
|
||||||
file: ^6.1.1
|
file: ^6.1.1
|
||||||
|
import_sorter: ^4.6.0
|
||||||
|
lints: ^2.0.0
|
||||||
|
test: ^1.15.7
|
||||||
#flutter_test: {sdk: flutter}
|
#flutter_test: {sdk: flutter}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -31,15 +31,15 @@ void main() {
|
||||||
late Room room;
|
late Room room;
|
||||||
var olmEnabled = true;
|
var olmEnabled = true;
|
||||||
|
|
||||||
final getLastMessagePayload =
|
Map<String, dynamic> getLastMessagePayload(
|
||||||
([String type = 'm.room.message', String? stateKey]) {
|
[String type = 'm.room.message', String? stateKey]) {
|
||||||
final state = stateKey != null;
|
final state = stateKey != null;
|
||||||
return json.decode(FakeMatrixApi.calledEndpoints.entries
|
return json.decode(FakeMatrixApi.calledEndpoints.entries
|
||||||
.firstWhere((e) => e.key.startsWith(
|
.firstWhere((e) => e.key.startsWith(
|
||||||
'/client/v3/rooms/${Uri.encodeComponent(room.id)}/${state ? 'state' : 'send'}/${Uri.encodeComponent(type)}${state && stateKey?.isNotEmpty == true ? '/' + Uri.encodeComponent(stateKey!) : ''}'))
|
'/client/v3/rooms/${Uri.encodeComponent(room.id)}/${state ? 'state' : 'send'}/${Uri.encodeComponent(type)}${state && stateKey?.isNotEmpty == true ? '/${Uri.encodeComponent(stateKey!)}' : ''}'))
|
||||||
.value
|
.value
|
||||||
.first);
|
.first);
|
||||||
};
|
}
|
||||||
|
|
||||||
test('setupClient', () async {
|
test('setupClient', () async {
|
||||||
try {
|
try {
|
||||||
|
|
@ -57,7 +57,7 @@ void main() {
|
||||||
stateKey: '',
|
stateKey: '',
|
||||||
eventId: '\$fakeeventid',
|
eventId: '\$fakeeventid',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
));
|
));
|
||||||
room.setState(Event(
|
room.setState(Event(
|
||||||
type: 'm.room.member',
|
type: 'm.room.member',
|
||||||
|
|
@ -66,7 +66,7 @@ void main() {
|
||||||
stateKey: client.userID,
|
stateKey: client.userID,
|
||||||
eventId: '\$fakeeventid',
|
eventId: '\$fakeeventid',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ void main() {
|
||||||
var decoded = SSSS.decodeRecoveryKey(encoded);
|
var decoded = SSSS.decodeRecoveryKey(encoded);
|
||||||
expect(key, decoded);
|
expect(key, decoded);
|
||||||
|
|
||||||
decoded = SSSS.decodeRecoveryKey(encoded + ' \n\t');
|
decoded = SSSS.decodeRecoveryKey('$encoded \n\t');
|
||||||
expect(key, decoded);
|
expect(key, decoded);
|
||||||
|
|
||||||
final handle = client.encryption!.ssss.open();
|
final handle = client.encryption!.ssss.open();
|
||||||
|
|
|
||||||
|
|
@ -1043,7 +1043,7 @@ void main() {
|
||||||
'# Title\nsome text and [link](https://example.com)\nokay and this is **important**',
|
'# Title\nsome text and [link](https://example.com)\nokay and this is **important**',
|
||||||
'format': 'org.matrix.custom.html',
|
'format': 'org.matrix.custom.html',
|
||||||
'formatted_body':
|
'formatted_body':
|
||||||
'<h1>Title</h1>\n<p>some text and <a href=\"https://example.com\">link</a><br>okay and this is <strong>important</strong></p>\n',
|
'<h1>Title</h1>\n<p>some text and <a href="https://example.com">link</a><br>okay and this is <strong>important</strong></p>\n',
|
||||||
'msgtype': 'm.text'
|
'msgtype': 'm.text'
|
||||||
},
|
},
|
||||||
'event_id': '\$143273582443PhrSn:example.org',
|
'event_id': '\$143273582443PhrSn:example.org',
|
||||||
|
|
@ -1253,12 +1253,13 @@ void main() {
|
||||||
test('attachments', () async {
|
test('attachments', () async {
|
||||||
final FILE_BUFF = Uint8List.fromList([0]);
|
final FILE_BUFF = Uint8List.fromList([0]);
|
||||||
final THUMBNAIL_BUFF = Uint8List.fromList([2]);
|
final THUMBNAIL_BUFF = Uint8List.fromList([2]);
|
||||||
final downloadCallback = (Uri uri) async {
|
Future<Uint8List> downloadCallback(Uri uri) async {
|
||||||
return {
|
return {
|
||||||
'/_matrix/media/v3/download/example.org/file': FILE_BUFF,
|
'/_matrix/media/v3/download/example.org/file': FILE_BUFF,
|
||||||
'/_matrix/media/v3/download/example.org/thumb': THUMBNAIL_BUFF,
|
'/_matrix/media/v3/download/example.org/thumb': THUMBNAIL_BUFF,
|
||||||
}[uri.path]!;
|
}[uri.path]!;
|
||||||
};
|
}
|
||||||
|
|
||||||
await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'),
|
await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'),
|
||||||
checkWellKnown: false);
|
checkWellKnown: false);
|
||||||
final room = Room(id: '!localpart:server.abc', client: client);
|
final room = Room(id: '!localpart:server.abc', client: client);
|
||||||
|
|
@ -1344,12 +1345,13 @@ void main() {
|
||||||
Uint8List.fromList([0x55, 0xD7, 0xEB, 0x72, 0x05, 0x13]);
|
Uint8List.fromList([0x55, 0xD7, 0xEB, 0x72, 0x05, 0x13]);
|
||||||
final THUMB_BUFF_DEC =
|
final THUMB_BUFF_DEC =
|
||||||
Uint8List.fromList([0x74, 0x68, 0x75, 0x6D, 0x62, 0x0A]);
|
Uint8List.fromList([0x74, 0x68, 0x75, 0x6D, 0x62, 0x0A]);
|
||||||
final downloadCallback = (Uri uri) async {
|
Future<Uint8List> downloadCallback(Uri uri) async {
|
||||||
return {
|
return {
|
||||||
'/_matrix/media/v3/download/example.com/file': FILE_BUFF_ENC,
|
'/_matrix/media/v3/download/example.com/file': FILE_BUFF_ENC,
|
||||||
'/_matrix/media/v3/download/example.com/thumb': THUMB_BUFF_ENC,
|
'/_matrix/media/v3/download/example.com/thumb': THUMB_BUFF_ENC,
|
||||||
}[uri.path]!;
|
}[uri.path]!;
|
||||||
};
|
}
|
||||||
|
|
||||||
final room = Room(id: '!localpart:server.abc', client: await getClient());
|
final room = Room(id: '!localpart:server.abc', client: await getClient());
|
||||||
var event = Event.fromJson({
|
var event = Event.fromJson({
|
||||||
'type': EventTypes.Message,
|
'type': EventTypes.Message,
|
||||||
|
|
@ -1440,12 +1442,13 @@ void main() {
|
||||||
test('downloadAndDecryptAttachment store', () async {
|
test('downloadAndDecryptAttachment store', () async {
|
||||||
final FILE_BUFF = Uint8List.fromList([0]);
|
final FILE_BUFF = Uint8List.fromList([0]);
|
||||||
var serverHits = 0;
|
var serverHits = 0;
|
||||||
final downloadCallback = (Uri uri) async {
|
Future<Uint8List> downloadCallback(Uri uri) async {
|
||||||
serverHits++;
|
serverHits++;
|
||||||
return {
|
return {
|
||||||
'/_matrix/media/v3/download/example.org/newfile': FILE_BUFF,
|
'/_matrix/media/v3/download/example.org/newfile': FILE_BUFF,
|
||||||
}[uri.path]!;
|
}[uri.path]!;
|
||||||
};
|
}
|
||||||
|
|
||||||
await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'),
|
await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'),
|
||||||
checkWellKnown: false);
|
checkWellKnown: false);
|
||||||
final room = Room(id: '!localpart:server.abc', client: await getClient());
|
final room = Room(id: '!localpart:server.abc', client: await getClient());
|
||||||
|
|
@ -1606,7 +1609,7 @@ void main() {
|
||||||
❤❤❤''',
|
❤❤❤''',
|
||||||
'format': 'org.matrix.custom.html',
|
'format': 'org.matrix.custom.html',
|
||||||
'formatted_body':
|
'formatted_body':
|
||||||
'\<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>😒😒</blockquote></mx-reply>❤❤❤'
|
'<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>😒😒</blockquote></mx-reply>❤❤❤'
|
||||||
},
|
},
|
||||||
'event_id': '\$edit2',
|
'event_id': '\$edit2',
|
||||||
'sender': '@alice:example.org',
|
'sender': '@alice:example.org',
|
||||||
|
|
@ -1622,7 +1625,7 @@ void main() {
|
||||||
❤❤''',
|
❤❤''',
|
||||||
'format': 'org.matrix.custom.html',
|
'format': 'org.matrix.custom.html',
|
||||||
'formatted_body':
|
'formatted_body':
|
||||||
'\<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>A 😒</blockquote></mx-reply>❤❤'
|
'<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>A 😒</blockquote></mx-reply>❤❤'
|
||||||
},
|
},
|
||||||
'event_id': '\$edit2',
|
'event_id': '\$edit2',
|
||||||
'sender': '@alice:example.org',
|
'sender': '@alice:example.org',
|
||||||
|
|
@ -1638,7 +1641,7 @@ void main() {
|
||||||
❤A❤''',
|
❤A❤''',
|
||||||
'format': 'org.matrix.custom.html',
|
'format': 'org.matrix.custom.html',
|
||||||
'formatted_body':
|
'formatted_body':
|
||||||
'\<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>😒😒😒</blockquote></mx-reply>❤A❤'
|
'<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>😒😒😒</blockquote></mx-reply>❤A❤'
|
||||||
},
|
},
|
||||||
'event_id': '\$edit2',
|
'event_id': '\$edit2',
|
||||||
'sender': '@alice:example.org',
|
'sender': '@alice:example.org',
|
||||||
|
|
@ -1654,7 +1657,7 @@ void main() {
|
||||||
❤A❤''',
|
❤A❤''',
|
||||||
'format': 'org.matrix.custom.html',
|
'format': 'org.matrix.custom.html',
|
||||||
'formatted_body':
|
'formatted_body':
|
||||||
'\<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>A😒</blockquote></mx-reply>❤A❤'
|
'<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>A😒</blockquote></mx-reply>❤A❤'
|
||||||
},
|
},
|
||||||
'event_id': '\$edit2',
|
'event_id': '\$edit2',
|
||||||
'sender': '@alice:example.org',
|
'sender': '@alice:example.org',
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ class FakeMatrixApi extends BaseClient {
|
||||||
var action = request.url.path;
|
var action = request.url.path;
|
||||||
if (request.url.path.contains('/_matrix')) {
|
if (request.url.path.contains('/_matrix')) {
|
||||||
action =
|
action =
|
||||||
request.url.path.split('/_matrix').last + '?' + request.url.query;
|
'${request.url.path.split('/_matrix').last}?${request.url.query}';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: avoid_print
|
// ignore: avoid_print
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ void main() {
|
||||||
content: {'membership': 'join'},
|
content: {'membership': 'join'},
|
||||||
room: room,
|
room: room,
|
||||||
stateKey: client.userID,
|
stateKey: client.userID,
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
eventId: '\$fakeid2:fakeServer.notExisting',
|
eventId: '\$fakeid2:fakeServer.notExisting',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
));
|
));
|
||||||
|
|
@ -63,7 +63,7 @@ void main() {
|
||||||
content: {'membership': 'join'},
|
content: {'membership': 'join'},
|
||||||
room: room,
|
room: room,
|
||||||
stateKey: client.userID,
|
stateKey: client.userID,
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
eventId: '\$fakeid4:fakeServer.notExisting',
|
eventId: '\$fakeid4:fakeServer.notExisting',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
));
|
));
|
||||||
|
|
@ -81,7 +81,7 @@ void main() {
|
||||||
},
|
},
|
||||||
room: room,
|
room: room,
|
||||||
stateKey: '',
|
stateKey: '',
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
eventId: '\$fakeid5:fakeServer.notExisting',
|
eventId: '\$fakeid5:fakeServer.notExisting',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
));
|
));
|
||||||
|
|
@ -110,7 +110,7 @@ void main() {
|
||||||
},
|
},
|
||||||
room: room,
|
room: room,
|
||||||
stateKey: '',
|
stateKey: '',
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
eventId: '\$fakeid6:fakeServer.notExisting',
|
eventId: '\$fakeid6:fakeServer.notExisting',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
));
|
));
|
||||||
|
|
@ -135,7 +135,7 @@ void main() {
|
||||||
},
|
},
|
||||||
room: room,
|
room: room,
|
||||||
stateKey: '',
|
stateKey: '',
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
eventId: '\$fakeid7:fakeServer.notExisting',
|
eventId: '\$fakeid7:fakeServer.notExisting',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
));
|
));
|
||||||
|
|
@ -158,7 +158,7 @@ void main() {
|
||||||
},
|
},
|
||||||
room: room,
|
room: room,
|
||||||
stateKey: 'fox',
|
stateKey: 'fox',
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
eventId: '\$fakeid8:fakeServer.notExisting',
|
eventId: '\$fakeid8:fakeServer.notExisting',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
));
|
));
|
||||||
|
|
@ -201,7 +201,7 @@ void main() {
|
||||||
},
|
},
|
||||||
room: room2,
|
room: room2,
|
||||||
stateKey: '',
|
stateKey: '',
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
eventId: '\$fakeid9:fakeServer.notExisting',
|
eventId: '\$fakeid9:fakeServer.notExisting',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
));
|
));
|
||||||
|
|
@ -234,7 +234,7 @@ void main() {
|
||||||
},
|
},
|
||||||
room: room2,
|
room: room2,
|
||||||
stateKey: 'fox',
|
stateKey: 'fox',
|
||||||
senderId: '\@fakeuser:fakeServer.notExisting',
|
senderId: '@fakeuser:fakeServer.notExisting',
|
||||||
eventId: '\$fakeid10:fakeServer.notExisting',
|
eventId: '\$fakeid10:fakeServer.notExisting',
|
||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ void main() {
|
||||||
'@[Fast Fox]#123': '@fastfox:example.org',
|
'@[Fast Fox]#123': '@fastfox:example.org',
|
||||||
'@[">]': '@blah:example.org',
|
'@[">]': '@blah:example.org',
|
||||||
};
|
};
|
||||||
final getMention = (mention) => mentionMap[mention];
|
String? getMention(mention) => mentionMap[mention];
|
||||||
test('simple markdown', () {
|
test('simple markdown', () {
|
||||||
expect(markdown('hey *there* how are **you** doing?'),
|
expect(markdown('hey *there* how are **you** doing?'),
|
||||||
'hey <em>there</em> how are <strong>you</strong> doing?');
|
'hey <em>there</em> how are <strong>you</strong> doing?');
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ void main() {
|
||||||
group('Sync Filters', () {
|
group('Sync Filters', () {
|
||||||
Logs().level = Level.error;
|
Logs().level = Level.error;
|
||||||
test('room update', () {
|
test('room update', () {
|
||||||
final testFn = (SyncUpdate s) => s.hasRoomUpdate;
|
bool testFn(SyncUpdate s) => s.hasRoomUpdate;
|
||||||
final expected = {
|
final expected = {
|
||||||
'empty': false,
|
'empty': false,
|
||||||
'presence': false,
|
'presence': false,
|
||||||
|
|
@ -153,7 +153,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('presence update', () {
|
test('presence update', () {
|
||||||
final testFn = (SyncUpdate s) => s.hasPresenceUpdate;
|
bool testFn(SyncUpdate s) => s.hasPresenceUpdate;
|
||||||
final expected = {
|
final expected = {
|
||||||
'empty': false,
|
'empty': false,
|
||||||
'presence': true,
|
'presence': true,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue