From c2f62e29675029e148be9f6576ee060200f6da30 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sat, 17 Jun 2023 21:28:54 +0200 Subject: [PATCH 1/8] feat(xep): Implement an OMEMO example client --- examples_dart/bin/component.dart | 24 +-- examples_dart/bin/omemo_client.dart | 140 ++++++++++++++++++ examples_dart/pubspec.yaml | 5 + integration_tests/create_users.sh | 2 + packages/moxxmpp/lib/src/connection.dart | 9 ++ packages/moxxmpp/lib/src/events.dart | 4 +- packages/moxxmpp/lib/src/managers/data.dart | 14 +- packages/moxxmpp/lib/src/message.dart | 11 +- packages/moxxmpp/lib/src/stanza.dart | 10 +- packages/moxxmpp/lib/src/util/queue.dart | 6 +- packages/moxxmpp/lib/src/util/typed_map.dart | 2 + .../lib/src/xeps/xep_0030/xep_0030.dart | 6 +- .../lib/src/xeps/xep_0060/xep_0060.dart | 9 ++ .../moxxmpp/lib/src/xeps/xep_0384/types.dart | 4 +- .../lib/src/xeps/xep_0384/xep_0384.dart | 117 ++++++++------- packages/moxxmpp/pubspec.yaml | 5 +- packages/moxxmpp/test/async_queue_test.dart | 4 - .../moxxmpp_socket_tcp/pubspec_overrides.yaml | 2 +- 18 files changed, 285 insertions(+), 89 deletions(-) create mode 100644 examples_dart/bin/omemo_client.dart create mode 100644 integration_tests/create_users.sh diff --git a/examples_dart/bin/component.dart b/examples_dart/bin/component.dart index 051751e..41d60ec 100644 --- a/examples_dart/bin/component.dart +++ b/examples_dart/bin/component.dart @@ -30,24 +30,26 @@ class EchoMessageManager extends XmppManagerBase { StanzaHandlerData state, ) async { final body = stanza.firstTag('body'); - if (body == null) return state.copyWith(done: true); + if (body == null) return state..done = true; final bodyText = body.innerText(); await getAttributes().sendStanza( - Stanza.message( - to: stanza.from, - children: [ - XMLNode( - tag: 'body', - text: 'Hello, ${stanza.from}! You said "$bodyText"', - ), - ], + StanzaDetails( + Stanza.message( + to: stanza.from, + children: [ + XMLNode( + tag: 'body', + text: 'Hello, ${stanza.from}! You said "$bodyText"', + ), + ], + ), + awaitable: false, ), - awaitable: false, ); - return state.copyWith(done: true); + return state..done = true; } } diff --git a/examples_dart/bin/omemo_client.dart b/examples_dart/bin/omemo_client.dart new file mode 100644 index 0000000..183f999 --- /dev/null +++ b/examples_dart/bin/omemo_client.dart @@ -0,0 +1,140 @@ +import 'package:args/args.dart'; +import 'package:chalkdart/chalk.dart'; +import 'package:cli_repl/cli_repl.dart'; +import 'package:logging/logging.dart'; +import 'package:moxxmpp/moxxmpp.dart'; +import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart'; +import 'package:omemo_dart/omemo_dart.dart' as omemo; + +class TestingOmemoManager extends BaseOmemoManager { + TestingOmemoManager(this._encryptToJid); + + final JID _encryptToJid; + + late omemo.OmemoManager manager; + + @override + Future getOmemoManager() async { + return manager; + } + + @override + Future shouldEncryptStanza(JID toJid, Stanza stanza) async { + return toJid.toBare() == _encryptToJid; + } +} + +class TestingTCPSocketWrapper extends TCPSocketWrapper { + @override + bool onBadCertificate(dynamic certificate, String domain) { + return true; + } +} + +void main(List args) async { + // Set up logging + Logger.root.level = Level.ALL; + Logger.root.onRecord.listen((record) { + // ignore: avoid_print + print( + '[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}', + ); + }); + + final parser = ArgParser() + ..addOption('jid') + ..addOption('password') + ..addOption('host') + ..addOption('port') + ..addOption('to'); + final options = parser.parse(args); + + // Connect + final jid = JID.fromString(options['jid']! as String); + final to = JID.fromString(options['to']! as String).toBare(); + final portString = options['port'] as String?; + final connection = XmppConnection( + TestingReconnectionPolicy(), + AlwaysConnectedConnectivityManager(), + ClientToServerNegotiator(), + TestingTCPSocketWrapper(), + )..connectionSettings = ConnectionSettings( + jid: jid, + password: options['password']! as String, + host: options['host'] as String?, + port: portString != null ? int.parse(portString) : null, + ); + + // Generate OMEMO data + final moxxmppOmemo = TestingOmemoManager(to); + final omemoManager = omemo.OmemoManager( + await omemo.OmemoDevice.generateNewDevice(jid.toString(), opkAmount: 5), + omemo.BlindTrustBeforeVerificationTrustManager(), + moxxmppOmemo.sendEmptyMessageImpl, + moxxmppOmemo.fetchDeviceList, + moxxmppOmemo.fetchDeviceBundle, + moxxmppOmemo.subscribeToDeviceListImpl, + ); + moxxmppOmemo.manager = omemoManager; + final deviceId = await omemoManager.getDeviceId(); + Logger.root.info('Our device id: $deviceId'); + + // Register the managers and negotiators + await connection.registerManagers([ + PresenceManager(), + DiscoManager([]), + PubSubManager(), + MessageManager(), + moxxmppOmemo, + ]); + await connection.registerFeatureNegotiators([ + SaslPlainNegotiator(), + ResourceBindingNegotiator(), + StartTlsNegotiator(), + SaslScramNegotiator(10, '', '', ScramHashType.sha1), + ]); + + // Set up event handlers + connection.asBroadcastStream().listen((event) { + if (event is MessageEvent) { + Logger.root.info(event.id); + Logger.root.info(event.extensions.keys.toList()); + + final body = event.encryptionError != null + ? chalk.red('Failed to decrypt message: ${event.encryptionError}') + : chalk.green(event.get()?.body ?? ''); + print('[${event.from.toString()}] ' + body); + } + }); + + // Connect + Logger.root.info('Connecting...'); + final result = await connection.connect(shouldReconnect: false, waitUntilLogin: true); + if (!result.isType()) { + Logger.root.severe('Authentication failed!'); + return; + } + Logger.root.info('Connected.'); + + // Publish our bundle + Logger.root.info('Publishing bundle'); + final device = await moxxmppOmemo.manager.getDevice(); + final omemoResult = await moxxmppOmemo.publishBundle(await device.toBundle()); + if (!omemoResult.isType()) { + Logger.root.severe('Failed to publish OMEMO bundle: ${omemoResult.get()}'); + return; + } + + final repl = Repl(prompt: '> '); + await for (final line in repl.runAsync()) { + await connection.getManagerById(messageManager)!.sendMessage( + to, + TypedMap.fromList([ + MessageBodyData(line), + ]), + ); + } + + // Disconnect + await connection.disconnect(); +} diff --git a/examples_dart/pubspec.yaml b/examples_dart/pubspec.yaml index 6d95fe3..5f4e285 100644 --- a/examples_dart/pubspec.yaml +++ b/examples_dart/pubspec.yaml @@ -7,6 +7,9 @@ environment: sdk: '>=2.18.0 <3.0.0' dependencies: + args: 2.4.1 + chalkdart: 2.0.9 + cli_repl: 0.2.3 logging: ^1.0.2 moxxmpp: hosted: https://git.polynom.me/api/packages/Moxxy/pub @@ -14,6 +17,8 @@ dependencies: moxxmpp_socket_tcp: hosted: https://git.polynom.me/api/packages/Moxxy/pub version: 0.3.1 + omemo_dart: + path: ../../../Personal/omemo_dart dependency_overrides: moxxmpp: diff --git a/integration_tests/create_users.sh b/integration_tests/create_users.sh new file mode 100644 index 0000000..89b3d9f --- /dev/null +++ b/integration_tests/create_users.sh @@ -0,0 +1,2 @@ +prosodyctl --config ./prosody.cfg.lua register testuser1 localhost abc123 +prosodyctl --config ./prosody.cfg.lua register testuser2 localhost abc123 \ No newline at end of file diff --git a/packages/moxxmpp/lib/src/connection.dart b/packages/moxxmpp/lib/src/connection.dart index 413d8a0..6a0442d 100644 --- a/packages/moxxmpp/lib/src/connection.dart +++ b/packages/moxxmpp/lib/src/connection.dart @@ -479,6 +479,7 @@ class XmppConnection { newStanza, TypedMap(), encrypted: details.encrypted, + shouldEncrypt: details.shouldEncrypt, forceEncryption: details.forceEncryption, ), ); @@ -736,6 +737,13 @@ class XmppConnection { : ''; _log.finest('<== $prefix${incomingPreHandlers.stanza.toXml()}'); + if (incomingPreHandlers.skip) { + _log.fine( + 'Not processing stanza (${incomingPreHandlers.stanza.tag}, ${incomingPreHandlers.stanza.id}) due to skip=true.', + ); + return; + } + final awaited = await _stanzaAwaiter.onData( incomingPreHandlers.stanza, connectionSettings.jid.toBare(), @@ -753,6 +761,7 @@ class XmppConnection { incomingPreHandlers.stanza, incomingPreHandlers.extensions, encrypted: incomingPreHandlers.encrypted, + encryptionError: incomingPreHandlers.encryptionError, cancelReason: incomingPreHandlers.cancelReason, ), ); diff --git a/packages/moxxmpp/lib/src/events.dart b/packages/moxxmpp/lib/src/events.dart index 9c0b8b3..21e12c2 100644 --- a/packages/moxxmpp/lib/src/events.dart +++ b/packages/moxxmpp/lib/src/events.dart @@ -70,9 +70,9 @@ class MessageEvent extends XmppEvent { MessageEvent( this.from, this.to, - this.id, this.encrypted, this.extensions, { + this.id, this.type, this.error, this.encryptionError, @@ -85,7 +85,7 @@ class MessageEvent extends XmppEvent { final JID to; /// The id attribute of the message. - final String id; + final String? id; /// The type attribute of the message. final String? type; diff --git a/packages/moxxmpp/lib/src/managers/data.dart b/packages/moxxmpp/lib/src/managers/data.dart index 6d8a5f2..924f8aa 100644 --- a/packages/moxxmpp/lib/src/managers/data.dart +++ b/packages/moxxmpp/lib/src/managers/data.dart @@ -13,12 +13,20 @@ class StanzaHandlerData { this.encryptionError, this.encrypted = false, this.forceEncryption = false, + this.shouldEncrypt = true, + this.skip = false, }); /// Indicates to the runner that processing is now done. This means that all /// pre-processing is done and no other handlers should be consulted. bool done; + /// Only useful in combination with [done] = true: When [skip] is set to true and + /// this [StanzaHandlerData] object is returned from a IncomingPreStanzaHandler, then + /// moxxmpp will skip checking whether the stanza was awaited and will not run any actual + /// IncomingStanzaHandler callbacks. + bool skip; + /// Indicates to the runner that processing is to be cancelled and no further handlers /// should run. The stanza also will not be sent. bool cancel; @@ -33,7 +41,7 @@ class StanzaHandlerData { /// absolutely necessary, e.g. with Message Carbons or OMEMO. Stanza stanza; - /// Whether the stanza was received encrypted + /// Whether the stanza is already encrypted bool encrypted; // If true, forces the encryption manager to encrypt to the JID, even if it @@ -42,6 +50,10 @@ class StanzaHandlerData { // to the JID anyway. bool forceEncryption; + /// Flag indicating whether a E2EE implementation should encrypt the stanza (true) + /// or not (false). + bool shouldEncrypt; + /// Additional data from other managers. final TypedMap extensions; } diff --git a/packages/moxxmpp/lib/src/message.dart b/packages/moxxmpp/lib/src/message.dart index bae047e..9700ee6 100644 --- a/packages/moxxmpp/lib/src/message.dart +++ b/packages/moxxmpp/lib/src/message.dart @@ -73,16 +73,23 @@ class MessageManager extends XmppManagerBase { Future isSupported() async => true; Future _onMessage( - Stanza _, + Stanza stanza, StanzaHandlerData state, ) async { + final body = stanza.firstTag('body'); + if (body != null) { + state.extensions.set( + MessageBodyData(body.innerText()), + ); + } + getAttributes().sendEvent( MessageEvent( JID.fromString(state.stanza.attributes['from']! as String), JID.fromString(state.stanza.attributes['to']! as String), - state.stanza.attributes['id']! as String, state.encrypted, state.extensions, + id: state.stanza.attributes['id'] as String?, type: state.stanza.attributes['type'] as String?, error: StanzaError.fromStanza(state.stanza), encryptionError: state.encryptionError, diff --git a/packages/moxxmpp/lib/src/stanza.dart b/packages/moxxmpp/lib/src/stanza.dart index 0b07f8d..41db9f6 100644 --- a/packages/moxxmpp/lib/src/stanza.dart +++ b/packages/moxxmpp/lib/src/stanza.dart @@ -7,6 +7,7 @@ class StanzaDetails { this.stanza, { this.addId = true, this.awaitable = true, + this.shouldEncrypt = true, this.encrypted = false, this.forceEncryption = false, this.bypassQueue = false, @@ -22,9 +23,16 @@ class StanzaDetails { /// Track the stanza to allow awaiting its response. final bool awaitable; + final bool forceEncryption; + + /// Flag indicating whether the stanza that is sent is already encrypted (true) + /// or not (false). This is only useful for E2EE implementations that have to + /// send heartbeats that must bypass themselves. final bool encrypted; - final bool forceEncryption; + /// Tells an E2EE implementation, if available, to encrypt the stanza (true) or + /// ignore the stanza (false). + final bool shouldEncrypt; /// Bypasses being put into the queue. Useful for sending stanzas that must go out /// now, where it's okay if it does not get sent. diff --git a/packages/moxxmpp/lib/src/util/queue.dart b/packages/moxxmpp/lib/src/util/queue.dart index b209bc4..b10047b 100644 --- a/packages/moxxmpp/lib/src/util/queue.dart +++ b/packages/moxxmpp/lib/src/util/queue.dart @@ -50,16 +50,12 @@ class AsyncStanzaQueue { @visibleForTesting Queue get queue => _queue; - @visibleForTesting - bool get isRunning => _running; - /// Adds a job [entry] to the queue. Future enqueueStanza(StanzaQueueEntry entry) async { await _lock.synchronized(() async { _queue.add(entry); - if (!_running && _queue.isNotEmpty && await _canSendCallback()) { - _running = true; + if (_queue.isNotEmpty && await _canSendCallback()) { unawaited( _runJob(_queue.removeFirst()), ); diff --git a/packages/moxxmpp/lib/src/util/typed_map.dart b/packages/moxxmpp/lib/src/util/typed_map.dart index f6a096e..b2775b9 100644 --- a/packages/moxxmpp/lib/src/util/typed_map.dart +++ b/packages/moxxmpp/lib/src/util/typed_map.dart @@ -20,4 +20,6 @@ class TypedMap { /// Return the object of type [T] from the map, if it has been stored. T? get() => _data[T] as T?; + + Iterable get keys => _data.keys; } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart b/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart index 903304e..98679de 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart @@ -255,7 +255,7 @@ class DiscoManager extends XmppManagerBase { Future> discoInfoQuery( JID entity, { String? node, - bool shouldEncrypt = true, + bool shouldEncrypt = false, bool shouldCache = true, }) async { DiscoInfo? info; @@ -294,7 +294,7 @@ class DiscoManager extends XmppManagerBase { final stanza = (await getAttributes().sendStanza( StanzaDetails( buildDiscoInfoQueryStanza(entity, node), - encrypted: !shouldEncrypt, + shouldEncrypt: shouldEncrypt, ), ))!; final query = stanza.firstTag('query'); @@ -325,7 +325,7 @@ class DiscoManager extends XmppManagerBase { Future>> discoItemsQuery( JID entity, { String? node, - bool shouldEncrypt = true, + bool shouldEncrypt = false, }) async { final key = DiscoCacheKey(entity, node); final future = await _discoItemsTracker.waitFor(key); diff --git a/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart b/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart index c840c37..a6d590d 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart @@ -202,6 +202,7 @@ class PubSubManager extends XmppManagerBase { ), ], ), + shouldEncrypt: false, ), ))!; @@ -245,6 +246,7 @@ class PubSubManager extends XmppManagerBase { ), ], ), + shouldEncrypt: false, ), ))!; @@ -329,6 +331,7 @@ class PubSubManager extends XmppManagerBase { ) ], ), + shouldEncrypt: false, ), ))!; if (result.attributes['type'] != 'result') { @@ -419,6 +422,7 @@ class PubSubManager extends XmppManagerBase { ) ], ), + shouldEncrypt: false, ), ))!; @@ -471,6 +475,7 @@ class PubSubManager extends XmppManagerBase { ), ], ), + shouldEncrypt: false, ), ))!; @@ -521,6 +526,7 @@ class PubSubManager extends XmppManagerBase { ), ], ), + shouldEncrypt: false, ), ))!; if (form.attributes['type'] != 'result') { @@ -550,6 +556,7 @@ class PubSubManager extends XmppManagerBase { ), ], ), + shouldEncrypt: false, ), ))!; if (submit.attributes['type'] != 'result') { @@ -580,6 +587,7 @@ class PubSubManager extends XmppManagerBase { ), ], ), + shouldEncrypt: false, ), ))!; @@ -624,6 +632,7 @@ class PubSubManager extends XmppManagerBase { ), ], ), + shouldEncrypt: false, ), ))!; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart b/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart index 038598c..9a6c4b8 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart @@ -16,6 +16,6 @@ class OmemoEncryptionError { const OmemoEncryptionError(this.jids, this.devices); /// See omemo_dart's EncryptionResult for info on these fields. - final Map jids; - final Map devices; + final Map jids; + final Map devices; } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart index bdf618d..e02a014 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart @@ -24,7 +24,7 @@ import 'package:moxxmpp/src/xeps/xep_0384/crypto.dart'; import 'package:moxxmpp/src/xeps/xep_0384/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0384/helpers.dart'; import 'package:moxxmpp/src/xeps/xep_0384/types.dart'; -import 'package:omemo_dart/omemo_dart.dart'; +import 'package:omemo_dart/omemo_dart.dart' as omemo; import 'package:xml/xml.dart'; const _doNotEncryptList = [ @@ -113,7 +113,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { } // Tell the OmemoManager - (await getOmemoManager()).onDeviceListUpdate(jid.toString(), ids); + await (await getOmemoManager()).onDeviceListUpdate(jid.toString(), ids); // Generate an event getAttributes().sendEvent(OmemoDeviceListUpdatedEvent(jid, ids)); @@ -121,13 +121,13 @@ abstract class BaseOmemoManager extends XmppManagerBase { } @visibleForOverriding - Future getOmemoManager(); + Future getOmemoManager(); /// Wrapper around using getSessionManager and then calling getDeviceId on it. Future _getDeviceId() async => (await getOmemoManager()).getDeviceId(); /// Wrapper around using getSessionManager and then calling getDeviceId on it. - Future _getDeviceBundle() async { + Future _getDeviceBundle() async { final om = await getOmemoManager(); final device = await om.getDevice(); return device.toBundle(); @@ -199,53 +199,43 @@ abstract class BaseOmemoManager extends XmppManagerBase { } XMLNode _buildEncryptedElement( - EncryptionResult result, + omemo.EncryptionResult result, String recipientJid, int deviceId, ) { final keyElements = >{}; - for (final key in result.encryptedKeys) { - final keyElement = XMLNode( + for (final keys in result.encryptedKeys.entries) { + keyElements[keys.key] = keys.value.map((ek) => XMLNode( tag: 'key', - attributes: { - 'rid': '${key.rid}', - 'kex': key.kex ? 'true' : 'false', + attributes: { + 'rid': ek.rid.toString(), + if (ek.kex) + 'kex': 'true', }, - text: key.value, - ); - - if (keyElements.containsKey(key.jid)) { - keyElements[key.jid]!.add(keyElement); - } else { - keyElements[key.jid] = [keyElement]; - } + text: ek.value, + ),).toList(); } final keysElements = keyElements.entries.map((entry) { return XMLNode( tag: 'keys', - attributes: { + attributes: { 'jid': entry.key, }, children: entry.value, ); }).toList(); - var payloadElement = []; - if (result.ciphertext != null) { - payloadElement = [ - XMLNode( - tag: 'payload', - text: base64.encode(result.ciphertext!), - ), - ]; - } - return XMLNode.xmlns( tag: 'encrypted', xmlns: omemoXmlns, children: [ - ...payloadElement, + if (result.ciphertext != null) + XMLNode( + tag: 'payload', + text: base64Encode(result.ciphertext!), + ), + XMLNode( tag: 'header', attributes: { @@ -259,7 +249,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// For usage with omemo_dart's OmemoManager. Future sendEmptyMessageImpl( - EncryptionResult result, + omemo.EncryptionResult result, String toJid, ) async { await getAttributes().sendStanza( @@ -301,17 +291,22 @@ abstract class BaseOmemoManager extends XmppManagerBase { } /// For usage with omemo_dart's OmemoManager - Future fetchDeviceBundle(String jid, int id) async { + Future fetchDeviceBundle(String jid, int id) async { final result = await retrieveDeviceBundle(JID.fromString(jid), id); if (result.isType()) return null; - return result.get(); + return result.get(); } Future _onOutgoingStanza( Stanza stanza, StanzaHandlerData state, ) async { + if (!state.shouldEncrypt) { + logger.finest('Not encrypting since state.shouldEncrypt is false'); + return state; + } + if (state.encrypted) { logger.finest('Not encrypting since state.encrypted is true'); return state; @@ -352,29 +347,31 @@ abstract class BaseOmemoManager extends XmppManagerBase { ?.isEnabled ?? false; final om = await getOmemoManager(); + final encryptToJids = [ + toJid.toString(), + if (carbonsEnabled) getAttributes().getFullJID().toBare().toString(), + ]; final result = await om.onOutgoingStanza( - OmemoOutgoingStanza( - [ - toJid.toString(), - if (carbonsEnabled) getAttributes().getFullJID().toBare().toString(), - ], + omemo.OmemoOutgoingStanza( + encryptToJids, _buildEnvelope(toEncrypt, toJid.toString()), ), ); logger.finest('Encryption done'); - if (!result.isSuccess(2)) { + if (!result.canSend) { return state ..cancel = true // If we have no device list for toJid, then the contact most likely does not // support OMEMO:2 - ..cancelReason = result.jidEncryptionErrors[toJid.toString()] - is NoKeyMaterialAvailableException + ..cancelReason = result.deviceEncryptionErrors[toJid.toString()]!.first.error + is omemo.NoKeyMaterialAvailableError ? OmemoNotSupportedForContactException() : UnknownOmemoError() - ..encryptionError = OmemoEncryptionError( - result.jidEncryptionErrors, - result.deviceEncryptionErrors, + // TODO + ..encryptionError = const OmemoEncryptionError( + {}, + {}, ); } @@ -411,20 +408,19 @@ abstract class BaseOmemoManager extends XmppManagerBase { Stanza stanza, StanzaHandlerData state, ) async { - final encrypted = stanza.firstTag('encrypted', xmlns: omemoXmlns); - if (encrypted == null) return state; if (stanza.from == null) return state; + final encrypted = stanza.firstTag('encrypted', xmlns: omemoXmlns)!; final fromJid = JID.fromString(stanza.from!).toBare(); final header = encrypted.firstTag('header')!; final payloadElement = encrypted.firstTag('payload'); - final keys = List.empty(growable: true); + // TODO: Only extract our own keys by the JID attribute + final keys = List.empty(growable: true); for (final keysElement in header.findTags('keys')) { final jid = keysElement.attributes['jid']! as String; for (final key in keysElement.findTags('key')) { keys.add( - EncryptedKey( - jid, + omemo.EncryptedKey( int.parse(key.attributes['rid']! as String), key.innerText(), key.attributes['kex'] == 'true', @@ -438,7 +434,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { final om = await getOmemoManager(); final result = await om.onIncomingStanza( - OmemoIncomingStanza( + omemo.OmemoIncomingStanza( fromJid.toString(), sid, state.extensions @@ -448,6 +444,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { DateTime.now().millisecondsSinceEpoch, keys, payloadElement?.innerText(), + false, ), ); @@ -464,6 +461,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { .toList(); } + logger.finest('Got payload: ${result.payload != null}'); if (result.payload != null) { XMLNode envelope; try { @@ -481,6 +479,8 @@ abstract class BaseOmemoManager extends XmppManagerBase { // Do not add forbidden elements from the envelope envelopeChildren.where(shouldEncryptElement), ); + + logger.finest('Adding children: ${envelopeChildren.map((c) => c.tag)}'); } else { logger.warning('Invalid envelope element: No element'); } @@ -490,6 +490,15 @@ abstract class BaseOmemoManager extends XmppManagerBase { } } + // Ignore heartbeat messages + if (stanza.tag == 'message' && encrypted.firstTag('payload') == null) { + logger.finest('Received empty OMEMO message. Ending processing early.'); + return state + ..encrypted = true + ..skip = true + ..done = true; + } + return state ..encrypted = true ..stanza = Stanza( @@ -532,7 +541,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// Retrieve all device bundles for the JID [jid]. /// /// On success, returns a list of devices. On failure, returns am OmemoError. - Future>> retrieveDeviceBundles( + Future>> retrieveDeviceBundles( JID jid, ) async { // TODO(Unknown): Should we query the device list first? @@ -553,7 +562,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// Retrieves a bundle from entity [jid] with the device id [deviceId]. /// /// On success, returns the device bundle. On failure, returns an OmemoError. - Future> retrieveDeviceBundle( + Future> retrieveDeviceBundle( JID jid, int deviceId, ) async { @@ -569,7 +578,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// nodes. /// /// On success, returns true. On failure, returns an OmemoError. - Future> publishBundle(OmemoBundle bundle) async { + Future> publishBundle(omemo.OmemoBundle bundle) async { final attrs = getAttributes(); final pm = attrs.getManagerById(pubsubManager)!; final bareJid = attrs.getFullJID().toBare(); @@ -642,7 +651,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// On failure, returns an OmemoError. Future> supportsOmemo(JID jid) async { final dm = getAttributes().getManagerById(discoManager)!; - final items = await dm.discoItemsQuery(jid.toBare()); + final items = await dm.discoItemsQuery(jid.toBare(), shouldEncrypt: false); if (items.isType()) return Result(UnknownOmemoError()); diff --git a/packages/moxxmpp/pubspec.yaml b/packages/moxxmpp/pubspec.yaml index 3198a24..e7aeb68 100644 --- a/packages/moxxmpp/pubspec.yaml +++ b/packages/moxxmpp/pubspec.yaml @@ -18,10 +18,9 @@ dependencies: meta: ^1.7.0 moxlib: hosted: https://git.polynom.me/api/packages/Moxxy/pub - version: ^0.1.5 + version: ^0.2.0 omemo_dart: - hosted: https://git.polynom.me/api/packages/PapaTutuWawa/pub - version: ^0.4.3 + path: ../../../../Personal/omemo_dart random_string: ^2.3.1 saslprep: ^1.0.2 synchronized: ^3.0.0+2 diff --git a/packages/moxxmpp/test/async_queue_test.dart b/packages/moxxmpp/test/async_queue_test.dart index 301172b..3b71fb6 100644 --- a/packages/moxxmpp/test/async_queue_test.dart +++ b/packages/moxxmpp/test/async_queue_test.dart @@ -30,7 +30,6 @@ void main() { await Future.delayed(const Duration(seconds: 1)); expect(queue.queue.length, 2); - expect(queue.isRunning, false); }); test('Test sending', () async { @@ -58,7 +57,6 @@ void main() { await Future.delayed(const Duration(seconds: 1)); expect(queue.queue.length, 0); - expect(queue.isRunning, false); }); test('Test partial sending and resuming', () async { @@ -89,12 +87,10 @@ void main() { await Future.delayed(const Duration(seconds: 1)); expect(queue.queue.length, 1); - expect(queue.isRunning, false); canRun = true; await queue.restart(); await Future.delayed(const Duration(seconds: 1)); expect(queue.queue.length, 0); - expect(queue.isRunning, false); }); } diff --git a/packages/moxxmpp_socket_tcp/pubspec_overrides.yaml b/packages/moxxmpp_socket_tcp/pubspec_overrides.yaml index 98626b5..ad07205 100644 --- a/packages/moxxmpp_socket_tcp/pubspec_overrides.yaml +++ b/packages/moxxmpp_socket_tcp/pubspec_overrides.yaml @@ -1,4 +1,4 @@ # melos_managed_dependency_overrides: moxxmpp dependency_overrides: moxxmpp: - path: ../moxxmpp + path: ../moxxmpp \ No newline at end of file From 3cb5a568ce4a58c5953113e1221526e476a88c05 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sat, 17 Jun 2023 21:45:00 +0200 Subject: [PATCH 2/8] feat(xep,core): Migrate to moxlib's Result type --- packages/moxxmpp/lib/moxxmpp.dart | 1 - packages/moxxmpp/lib/src/connection.dart | 1 - packages/moxxmpp/lib/src/managers/base.dart | 1 - .../moxxmpp/lib/src/managers/handlers.dart | 10 +++--- .../lib/src/negotiators/negotiator.dart | 5 ++- packages/moxxmpp/lib/src/presence.dart | 2 +- .../src/rfcs/rfc_6120/resource_binding.dart | 2 +- .../src/rfcs/rfc_6120/sasl/negotiator.dart | 8 ++--- .../lib/src/rfcs/rfc_6120/sasl/plain.dart | 2 +- .../lib/src/rfcs/rfc_6120/sasl/scram.dart | 2 +- .../lib/src/rfcs/rfc_6120/starttls.dart | 2 +- packages/moxxmpp/lib/src/roster/roster.dart | 2 +- packages/moxxmpp/lib/src/types/result.dart | 16 --------- packages/moxxmpp/lib/src/util/queue.dart | 8 +---- .../moxxmpp/lib/src/xeps/staging/fast.dart | 2 +- packages/moxxmpp/lib/src/xeps/xep_0004.dart | 4 +-- .../lib/src/xeps/xep_0030/xep_0030.dart | 2 +- packages/moxxmpp/lib/src/xeps/xep_0054.dart | 2 +- .../lib/src/xeps/xep_0060/xep_0060.dart | 2 +- packages/moxxmpp/lib/src/xeps/xep_0084.dart | 3 +- .../lib/src/xeps/xep_0198/negotiator.dart | 2 +- packages/moxxmpp/lib/src/xeps/xep_0352.dart | 2 +- .../lib/src/xeps/xep_0363/xep_0363.dart | 10 +++--- .../lib/src/xeps/xep_0384/xep_0384.dart | 33 ++++++++++--------- packages/moxxmpp/lib/src/xeps/xep_0386.dart | 2 +- .../lib/src/xeps/xep_0388/negotiators.dart | 2 +- .../lib/src/xeps/xep_0388/xep_0388.dart | 2 +- packages/moxxmpp/lib/src/xeps/xep_0447.dart | 5 ++- packages/moxxmpp/lib/src/xeps/xep_0448.dart | 5 ++- packages/moxxmpp/lib/src/xeps/xep_0449.dart | 2 +- packages/moxxmpp/test/negotiator_test.dart | 2 ++ packages/moxxmpp/test/xeps/xep_0060_test.dart | 1 + packages/moxxmpp/test/xeps/xep_0115_test.dart | 1 + packages/moxxmpp/test/xeps/xep_0388_test.dart | 2 ++ 34 files changed, 63 insertions(+), 85 deletions(-) delete mode 100644 packages/moxxmpp/lib/src/types/result.dart diff --git a/packages/moxxmpp/lib/moxxmpp.dart b/packages/moxxmpp/lib/moxxmpp.dart index 10cbb94..221ca5e 100644 --- a/packages/moxxmpp/lib/moxxmpp.dart +++ b/packages/moxxmpp/lib/moxxmpp.dart @@ -38,7 +38,6 @@ export 'package:moxxmpp/src/settings.dart'; export 'package:moxxmpp/src/socket.dart'; export 'package:moxxmpp/src/stanza.dart'; export 'package:moxxmpp/src/stringxml.dart'; -export 'package:moxxmpp/src/types/result.dart'; export 'package:moxxmpp/src/util/typed_map.dart'; export 'package:moxxmpp/src/xeps/staging/extensible_file_thumbnails.dart'; export 'package:moxxmpp/src/xeps/staging/fast.dart'; diff --git a/packages/moxxmpp/lib/src/connection.dart b/packages/moxxmpp/lib/src/connection.dart index 6a0442d..223551a 100644 --- a/packages/moxxmpp/lib/src/connection.dart +++ b/packages/moxxmpp/lib/src/connection.dart @@ -25,7 +25,6 @@ import 'package:moxxmpp/src/settings.dart'; import 'package:moxxmpp/src/socket.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/util/queue.dart'; import 'package:moxxmpp/src/util/typed_map.dart'; import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart'; diff --git a/packages/moxxmpp/lib/src/managers/base.dart b/packages/moxxmpp/lib/src/managers/base.dart index dca035b..ab61ed3 100644 --- a/packages/moxxmpp/lib/src/managers/base.dart +++ b/packages/moxxmpp/lib/src/managers/base.dart @@ -47,7 +47,6 @@ abstract class XmppManagerBase { final result = await dm!.discoInfoQuery( _managerAttributes.getConnectionSettings().jid.toDomain(), - shouldEncrypt: false, ); if (result.isType()) { return false; diff --git a/packages/moxxmpp/lib/src/managers/handlers.dart b/packages/moxxmpp/lib/src/managers/handlers.dart index f35a849..f38d944 100644 --- a/packages/moxxmpp/lib/src/managers/handlers.dart +++ b/packages/moxxmpp/lib/src/managers/handlers.dart @@ -1,4 +1,4 @@ -import 'package:moxlib/moxlib.dart'; +import 'package:collection/collection.dart'; import 'package:moxxmpp/src/managers/data.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stanza.dart'; @@ -100,10 +100,10 @@ class StanzaHandler extends Handler { matches &= firstTag?.xmlns == tagXmlns; } } else if (tagXmlns != null) { - matches &= listContains( - node.children, - (XMLNode node_) => node_.attributes['xmlns'] == tagXmlns, - ); + matches &= node.children.firstWhereOrNull( + (XMLNode node_) => node_.attributes['xmlns'] == tagXmlns, + ) != + null; } return matches; diff --git a/packages/moxxmpp/lib/src/negotiators/negotiator.dart b/packages/moxxmpp/lib/src/negotiators/negotiator.dart index 600e872..5e5c378 100644 --- a/packages/moxxmpp/lib/src/negotiators/negotiator.dart +++ b/packages/moxxmpp/lib/src/negotiators/negotiator.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/connection.dart'; @@ -8,7 +9,6 @@ import 'package:moxxmpp/src/managers/base.dart'; import 'package:moxxmpp/src/settings.dart'; import 'package:moxxmpp/src/socket.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; /// The state a negotiator is currently in enum NegotiatorState { @@ -117,8 +117,7 @@ abstract class XmppFeatureNegotiatorBase { /// Returns true if a feature in [features], which are the children of the /// nonza, can be negotiated. Otherwise, returns false. bool matchesFeature(List features) { - return firstWhereOrNull( - features, + return features.firstWhereOrNull( (XMLNode feature) => feature.attributes['xmlns'] == negotiatingXmlns, ) != null; diff --git a/packages/moxxmpp/lib/src/presence.dart b/packages/moxxmpp/lib/src/presence.dart index f248601..d53d349 100644 --- a/packages/moxxmpp/lib/src/presence.dart +++ b/packages/moxxmpp/lib/src/presence.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/base.dart'; @@ -10,7 +11,6 @@ import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; /// A function that will be called when presence, outside of subscription request /// management, will be sent. Useful for managers that want to add [XMLNode]s to said diff --git a/packages/moxxmpp/lib/src/rfcs/rfc_6120/resource_binding.dart b/packages/moxxmpp/lib/src/rfcs/rfc_6120/resource_binding.dart index 2ebb737..3f7afb4 100644 --- a/packages/moxxmpp/lib/src/rfcs/rfc_6120/resource_binding.dart +++ b/packages/moxxmpp/lib/src/rfcs/rfc_6120/resource_binding.dart @@ -1,10 +1,10 @@ +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0198/xep_0198.dart'; import 'package:uuid/uuid.dart'; diff --git a/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/negotiator.dart b/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/negotiator.dart index e0cef18..5ef0a86 100644 --- a/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/negotiator.dart +++ b/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/negotiator.dart @@ -1,4 +1,4 @@ -import 'package:moxlib/moxlib.dart'; +import 'package:collection/collection.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/stringxml.dart'; @@ -13,15 +13,13 @@ abstract class SaslNegotiator extends XmppFeatureNegotiatorBase { @override bool matchesFeature(List features) { // Is SASL advertised? - final mechanisms = firstWhereOrNull( - features, + final mechanisms = features.firstWhereOrNull( (XMLNode feature) => feature.attributes['xmlns'] == saslXmlns, ); if (mechanisms == null) return false; // Is SASL PLAIN advertised? - return firstWhereOrNull( - mechanisms.children, + return mechanisms.children.firstWhereOrNull( (XMLNode mechanism) => mechanism.text == mechanismName, ) != null; diff --git a/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/plain.dart b/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/plain.dart index 573762c..e839a4d 100644 --- a/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/plain.dart +++ b/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/plain.dart @@ -1,12 +1,12 @@ import 'dart:convert'; import 'package:logging/logging.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/rfcs/rfc_6120/sasl/errors.dart'; import 'package:moxxmpp/src/rfcs/rfc_6120/sasl/nonza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0388/negotiators.dart'; import 'package:moxxmpp/src/xeps/xep_0388/xep_0388.dart'; import 'package:saslprep/saslprep.dart'; diff --git a/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/scram.dart b/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/scram.dart index 9ea570b..e753c53 100644 --- a/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/scram.dart +++ b/packages/moxxmpp/lib/src/rfcs/rfc_6120/sasl/scram.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:math' show Random; import 'package:cryptography/cryptography.dart'; import 'package:logging/logging.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; @@ -10,7 +11,6 @@ import 'package:moxxmpp/src/rfcs/rfc_6120/sasl/errors.dart'; import 'package:moxxmpp/src/rfcs/rfc_6120/sasl/kv.dart'; import 'package:moxxmpp/src/rfcs/rfc_6120/sasl/nonza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0388/negotiators.dart'; import 'package:moxxmpp/src/xeps/xep_0388/xep_0388.dart'; import 'package:random_string/random_string.dart'; diff --git a/packages/moxxmpp/lib/src/rfcs/rfc_6120/starttls.dart b/packages/moxxmpp/lib/src/rfcs/rfc_6120/starttls.dart index b8e07d1..bb92d40 100644 --- a/packages/moxxmpp/lib/src/rfcs/rfc_6120/starttls.dart +++ b/packages/moxxmpp/lib/src/rfcs/rfc_6120/starttls.dart @@ -1,9 +1,9 @@ import 'package:logging/logging.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; enum _StartTlsState { ready, requested } diff --git a/packages/moxxmpp/lib/src/roster/roster.dart b/packages/moxxmpp/lib/src/roster/roster.dart index c6d7758..61fa837 100644 --- a/packages/moxxmpp/lib/src/roster/roster.dart +++ b/packages/moxxmpp/lib/src/roster/roster.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/attributes.dart'; import 'package:moxxmpp/src/managers/base.dart'; @@ -14,7 +15,6 @@ import 'package:moxxmpp/src/roster/errors.dart'; import 'package:moxxmpp/src/roster/state.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; @immutable class XmppRosterItem { diff --git a/packages/moxxmpp/lib/src/types/result.dart b/packages/moxxmpp/lib/src/types/result.dart deleted file mode 100644 index a277740..0000000 --- a/packages/moxxmpp/lib/src/types/result.dart +++ /dev/null @@ -1,16 +0,0 @@ -class Result { - const Result(this._data) - : assert( - _data is T || _data is V, - 'Invalid data type: Must be either $T or $V', - ); - final dynamic _data; - - bool isType() => _data is S; - - S get() { - assert(_data is S, 'Data is not $S'); - - return _data as S; - } -} diff --git a/packages/moxxmpp/lib/src/util/queue.dart b/packages/moxxmpp/lib/src/util/queue.dart index b10047b..203da8d 100644 --- a/packages/moxxmpp/lib/src/util/queue.dart +++ b/packages/moxxmpp/lib/src/util/queue.dart @@ -33,7 +33,7 @@ class AsyncStanzaQueue { this._canSendCallback, ); - /// The lock for accessing [AsyncStanzaQueue._lock] and [AsyncStanzaQueue._running]. + /// The lock for accessing [AsyncStanzaQueue._queue]. final Lock _lock = Lock(); /// The actual job queue. @@ -44,9 +44,6 @@ class AsyncStanzaQueue { final CanSendCallback _canSendCallback; - /// Indicates whether we are currently executing a job. - bool _running = false; - @visibleForTesting Queue get queue => _queue; @@ -75,8 +72,6 @@ class AsyncStanzaQueue { unawaited( _runJob(_queue.removeFirst()), ); - } else { - _running = false; } }); } @@ -86,7 +81,6 @@ class AsyncStanzaQueue { await _lock.synchronized(() { if (_queue.isNotEmpty) { - _running = true; unawaited( _runJob(_queue.removeFirst()), ); diff --git a/packages/moxxmpp/lib/src/xeps/staging/fast.dart b/packages/moxxmpp/lib/src/xeps/staging/fast.dart index 3a873fd..c1ad724 100644 --- a/packages/moxxmpp/lib/src/xeps/staging/fast.dart +++ b/packages/moxxmpp/lib/src/xeps/staging/fast.dart @@ -1,11 +1,11 @@ import 'package:collection/collection.dart'; import 'package:logging/logging.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0388/negotiators.dart'; import 'package:moxxmpp/src/xeps/xep_0388/xep_0388.dart'; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0004.dart b/packages/moxxmpp/lib/src/xeps/xep_0004.dart index 27ea054..7b4a684 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0004.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0004.dart @@ -1,4 +1,4 @@ -import 'package:moxlib/moxlib.dart'; +import 'package:collection/collection.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stringxml.dart'; @@ -80,7 +80,7 @@ class DataForm { final List> items; DataFormField? getFieldByVar(String varAttr) { - return firstWhereOrNull(fields, (field) => field.varAttr == varAttr); + return fields.firstWhereOrNull((field) => field.varAttr == varAttr); } XMLNode toXml() { diff --git a/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart b/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart index 98679de..09443d2 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/base.dart'; @@ -9,7 +10,6 @@ import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/util/wait.dart'; import 'package:moxxmpp/src/xeps/xep_0030/cache.dart'; import 'package:moxxmpp/src/xeps/xep_0030/errors.dart'; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0054.dart b/packages/moxxmpp/lib/src/xeps/xep_0054.dart index e38d3d0..90c76e8 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0054.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0054.dart @@ -1,3 +1,4 @@ +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/base.dart'; @@ -7,7 +8,6 @@ import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; abstract class VCardError {} diff --git a/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart b/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart index a6d590d..661274c 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart @@ -1,5 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:meta/meta.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/base.dart'; @@ -9,7 +10,6 @@ import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0004.dart'; import 'package:moxxmpp/src/xeps/xep_0030/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0084.dart b/packages/moxxmpp/lib/src/xeps/xep_0084.dart index 17bdd88..08ffef4 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0084.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0084.dart @@ -1,11 +1,11 @@ import 'dart:convert'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/base.dart'; import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0030/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart'; @@ -225,7 +225,6 @@ class UserAvatarManager extends XmppManagerBase { final response = await disco.discoItemsQuery( jid, node: userAvatarDataXmlns, - shouldEncrypt: false, ); if (response.isType()) return Result(UnknownAvatarError()); diff --git a/packages/moxxmpp/lib/src/xeps/xep_0198/negotiator.dart b/packages/moxxmpp/lib/src/xeps/xep_0198/negotiator.dart index 0f3176e..d96f2e8 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0198/negotiator.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0198/negotiator.dart @@ -1,12 +1,12 @@ import 'package:collection/collection.dart'; import 'package:logging/logging.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0198/nonzas.dart'; import 'package:moxxmpp/src/xeps/xep_0198/state.dart'; import 'package:moxxmpp/src/xeps/xep_0198/xep_0198.dart'; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0352.dart b/packages/moxxmpp/lib/src/xeps/xep_0352.dart index 718d805..e2d73f7 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0352.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0352.dart @@ -1,10 +1,10 @@ +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/managers/base.dart'; import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0386.dart'; class CSIActiveNonza extends XMLNode { diff --git a/packages/moxxmpp/lib/src/xeps/xep_0363/xep_0363.dart b/packages/moxxmpp/lib/src/xeps/xep_0363/xep_0363.dart index b63ffab..7b7507e 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0363/xep_0363.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0363/xep_0363.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; @@ -7,7 +8,6 @@ import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0030/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart'; @@ -58,10 +58,10 @@ class HttpFileUploadManager extends XmppManagerBase { /// Returns whether the entity provided an identity that tells us that we can ask it /// for an HTTP upload slot. bool _containsFileUploadIdentity(DiscoInfo info) { - return listContains( - info.identities, - (Identity id) => id.category == 'store' && id.type == 'file', - ); + return info.identities.firstWhereOrNull( + (Identity id) => id.category == 'store' && id.type == 'file', + ) != + null; } /// Extract the maximum filesize in octets from the disco response. Returns null diff --git a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart index e02a014..b94537d 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:meta/meta.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/base.dart'; @@ -10,7 +11,6 @@ import 'package:moxxmpp/src/managers/namespaces.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0030/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart'; @@ -205,15 +205,18 @@ abstract class BaseOmemoManager extends XmppManagerBase { ) { final keyElements = >{}; for (final keys in result.encryptedKeys.entries) { - keyElements[keys.key] = keys.value.map((ek) => XMLNode( - tag: 'key', - attributes: { - 'rid': ek.rid.toString(), - if (ek.kex) - 'kex': 'true', - }, - text: ek.value, - ),).toList(); + keyElements[keys.key] = keys.value + .map( + (ek) => XMLNode( + tag: 'key', + attributes: { + 'rid': ek.rid.toString(), + if (ek.kex) 'kex': 'true', + }, + text: ek.value, + ), + ) + .toList(); } final keysElements = keyElements.entries.map((entry) { @@ -235,7 +238,6 @@ abstract class BaseOmemoManager extends XmppManagerBase { tag: 'payload', text: base64Encode(result.ciphertext!), ), - XMLNode( tag: 'header', attributes: { @@ -364,8 +366,8 @@ abstract class BaseOmemoManager extends XmppManagerBase { ..cancel = true // If we have no device list for toJid, then the contact most likely does not // support OMEMO:2 - ..cancelReason = result.deviceEncryptionErrors[toJid.toString()]!.first.error - is omemo.NoKeyMaterialAvailableError + ..cancelReason = result.deviceEncryptionErrors[toJid.toString()]!.first + .error is omemo.NoKeyMaterialAvailableError ? OmemoNotSupportedForContactException() : UnknownOmemoError() // TODO @@ -578,7 +580,8 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// nodes. /// /// On success, returns true. On failure, returns an OmemoError. - Future> publishBundle(omemo.OmemoBundle bundle) async { + Future> publishBundle( + omemo.OmemoBundle bundle,) async { final attrs = getAttributes(); final pm = attrs.getManagerById(pubsubManager)!; final bareJid = attrs.getFullJID().toBare(); @@ -651,7 +654,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// On failure, returns an OmemoError. Future> supportsOmemo(JID jid) async { final dm = getAttributes().getManagerById(discoManager)!; - final items = await dm.discoItemsQuery(jid.toBare(), shouldEncrypt: false); + final items = await dm.discoItemsQuery(jid.toBare()); if (items.isType()) return Result(UnknownOmemoError()); diff --git a/packages/moxxmpp/lib/src/xeps/xep_0386.dart b/packages/moxxmpp/lib/src/xeps/xep_0386.dart index 4f61add..ad2fbfa 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0386.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0386.dart @@ -1,10 +1,10 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0388/negotiators.dart'; import 'package:moxxmpp/src/xeps/xep_0388/xep_0388.dart'; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0388/negotiators.dart b/packages/moxxmpp/lib/src/xeps/xep_0388/negotiators.dart index 948930f..24af826 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0388/negotiators.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0388/negotiators.dart @@ -1,7 +1,7 @@ +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/rfcs/rfc_6120/sasl/negotiator.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; /// A special type of [XmppFeatureNegotiatorBase] that is aware of SASL2. abstract class Sasl2FeatureNegotiator extends XmppFeatureNegotiatorBase { diff --git a/packages/moxxmpp/lib/src/xeps/xep_0388/xep_0388.dart b/packages/moxxmpp/lib/src/xeps/xep_0388/xep_0388.dart index e871c03..9e56694 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0388/xep_0388.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0388/xep_0388.dart @@ -1,10 +1,10 @@ +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/negotiators/namespaces.dart'; import 'package:moxxmpp/src/negotiators/negotiator.dart'; import 'package:moxxmpp/src/rfcs/rfc_6120/sasl/errors.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/xeps/xep_0388/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0388/negotiators.dart'; import 'package:moxxmpp/src/xeps/xep_0388/user_agent.dart'; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0447.dart b/packages/moxxmpp/lib/src/xeps/xep_0447.dart index 36bf848..d29ce5d 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0447.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0447.dart @@ -1,4 +1,4 @@ -import 'package:moxlib/moxlib.dart'; +import 'package:collection/collection.dart'; import 'package:moxxmpp/src/managers/base.dart'; import 'package:moxxmpp/src/managers/data.dart'; import 'package:moxxmpp/src/managers/handlers.dart'; @@ -114,8 +114,7 @@ class StatelessFileSharingData implements StanzaHandlerExtension { } StatelessFileSharingUrlSource? getFirstUrlSource() { - return firstWhereOrNull( - sources, + return sources.firstWhereOrNull( (StatelessFileSharingSource source) => source is StatelessFileSharingUrlSource, ) as StatelessFileSharingUrlSource?; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0448.dart b/packages/moxxmpp/lib/src/xeps/xep_0448.dart index 545bd3d..a6a829b 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0448.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0448.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'package:moxlib/moxlib.dart'; +import 'package:collection/collection.dart'; import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stringxml.dart'; import 'package:moxxmpp/src/xeps/xep_0300.dart'; @@ -54,8 +54,7 @@ class StatelessFileSharingEncryptedSource extends StatelessFileSharingSource { final sources = element.firstTag('sources', xmlns: sfsXmlns)!.children; // Find the first URL source - final source = firstWhereOrNull( - sources, + final source = sources.firstWhereOrNull( (XMLNode child) => child.tag == 'url-data' && child.attributes['xmlns'] == urlDataXmlns, )!; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0449.dart b/packages/moxxmpp/lib/src/xeps/xep_0449.dart index b32aa3c..26bfc3c 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0449.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0449.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/src/jid.dart'; import 'package:moxxmpp/src/managers/base.dart'; import 'package:moxxmpp/src/managers/data.dart'; @@ -9,7 +10,6 @@ import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/rfcs/rfc_4790.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; -import 'package:moxxmpp/src/types/result.dart'; import 'package:moxxmpp/src/util/typed_map.dart'; import 'package:moxxmpp/src/xeps/xep_0060/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0060/xep_0060.dart'; diff --git a/packages/moxxmpp/test/negotiator_test.dart b/packages/moxxmpp/test/negotiator_test.dart index 15de453..622cad5 100644 --- a/packages/moxxmpp/test/negotiator_test.dart +++ b/packages/moxxmpp/test/negotiator_test.dart @@ -1,6 +1,8 @@ +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/moxxmpp.dart'; import 'package:moxxmpp/src/parser.dart'; import 'package:test/test.dart'; + import 'helpers/logging.dart'; const exampleXmlns1 = 'im:moxxmpp:example1'; diff --git a/packages/moxxmpp/test/xeps/xep_0060_test.dart b/packages/moxxmpp/test/xeps/xep_0060_test.dart index da31d14..0aaf10b 100644 --- a/packages/moxxmpp/test/xeps/xep_0060_test.dart +++ b/packages/moxxmpp/test/xeps/xep_0060_test.dart @@ -1,3 +1,4 @@ +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/moxxmpp.dart'; import 'package:test/test.dart'; diff --git a/packages/moxxmpp/test/xeps/xep_0115_test.dart b/packages/moxxmpp/test/xeps/xep_0115_test.dart index 10ef205..46c073b 100644 --- a/packages/moxxmpp/test/xeps/xep_0115_test.dart +++ b/packages/moxxmpp/test/xeps/xep_0115_test.dart @@ -1,3 +1,4 @@ +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/moxxmpp.dart'; import 'package:test/test.dart'; diff --git a/packages/moxxmpp/test/xeps/xep_0388_test.dart b/packages/moxxmpp/test/xeps/xep_0388_test.dart index f11e98a..8fa81ab 100644 --- a/packages/moxxmpp/test/xeps/xep_0388_test.dart +++ b/packages/moxxmpp/test/xeps/xep_0388_test.dart @@ -1,6 +1,8 @@ import 'package:collection/collection.dart'; +import 'package:moxlib/moxlib.dart'; import 'package:moxxmpp/moxxmpp.dart'; import 'package:test/test.dart'; + import '../helpers/logging.dart'; import '../helpers/xmpp.dart'; From 8252472faed71ddbd47c8b3a153e93045fed7ebd Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sat, 17 Jun 2023 23:37:08 +0200 Subject: [PATCH 3/8] feat(xep): Adjust to omemo_dart changes --- .../moxxmpp/lib/src/xeps/xep_0384/types.dart | 7 ++- .../lib/src/xeps/xep_0384/xep_0384.dart | 44 +++++++++---------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart b/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart index 9a6c4b8..bb1331a 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart @@ -13,9 +13,8 @@ class DoNotEncrypt { /// An encryption error caused by OMEMO. class OmemoEncryptionError { - const OmemoEncryptionError(this.jids, this.devices); + const OmemoEncryptionError(this.deviceEncryptionErrors); - /// See omemo_dart's EncryptionResult for info on these fields. - final Map jids; - final Map devices; + /// See omemo_dart's EncryptionResult for info on this field. + final Map> deviceEncryptionErrors; } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart index b94537d..bf3fc07 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart @@ -16,7 +16,6 @@ import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart'; import 'package:moxxmpp/src/xeps/xep_0060/errors.dart'; import 'package:moxxmpp/src/xeps/xep_0060/xep_0060.dart'; -import 'package:moxxmpp/src/xeps/xep_0203.dart'; import 'package:moxxmpp/src/xeps/xep_0280.dart'; import 'package:moxxmpp/src/xeps/xep_0334.dart'; import 'package:moxxmpp/src/xeps/xep_0380.dart'; @@ -370,10 +369,8 @@ abstract class BaseOmemoManager extends XmppManagerBase { .error is omemo.NoKeyMaterialAvailableError ? OmemoNotSupportedForContactException() : UnknownOmemoError() - // TODO - ..encryptionError = const OmemoEncryptionError( - {}, - {}, + ..encryptionError = OmemoEncryptionError( + result.deviceEncryptionErrors, ); } @@ -415,37 +412,35 @@ abstract class BaseOmemoManager extends XmppManagerBase { final encrypted = stanza.firstTag('encrypted', xmlns: omemoXmlns)!; final fromJid = JID.fromString(stanza.from!).toBare(); final header = encrypted.firstTag('header')!; - final payloadElement = encrypted.firstTag('payload'); - // TODO: Only extract our own keys by the JID attribute + final ourJid = getAttributes().getFullJID(); + final ourJidString = ourJid.toBare().toString(); final keys = List.empty(growable: true); for (final keysElement in header.findTags('keys')) { + // We only care about our own JID final jid = keysElement.attributes['jid']! as String; - for (final key in keysElement.findTags('key')) { - keys.add( - omemo.EncryptedKey( - int.parse(key.attributes['rid']! as String), - key.innerText(), - key.attributes['kex'] == 'true', - ), - ); + if (jid != ourJidString) { + continue; } + + keys.addAll( + keysElement.findTags('key').map( + (key) => omemo.EncryptedKey( + int.parse(key.attributes['rid']! as String), + key.innerText(), + key.attributes['kex'] == 'true', + ), + ), + ); } - final ourJid = getAttributes().getFullJID(); final sid = int.parse(header.attributes['sid']! as String); - final om = await getOmemoManager(); final result = await om.onIncomingStanza( omemo.OmemoIncomingStanza( fromJid.toString(), sid, - state.extensions - .get() - ?.timestamp - .millisecondsSinceEpoch ?? - DateTime.now().millisecondsSinceEpoch, keys, - payloadElement?.innerText(), + encrypted.firstTag('payload')?.innerText(), false, ), ); @@ -581,7 +576,8 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// /// On success, returns true. On failure, returns an OmemoError. Future> publishBundle( - omemo.OmemoBundle bundle,) async { + omemo.OmemoBundle bundle, + ) async { final attrs = getAttributes(); final pm = attrs.getManagerById(pubsubManager)!; final bareJid = attrs.getFullJID().toBare(); From 9fd2daabb215ee7ee31abf629cb364268a29b6c2 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sat, 17 Jun 2023 23:51:52 +0200 Subject: [PATCH 4/8] feat(xep): Adjust to more omemo_dart changes --- examples_dart/bin/omemo_client.dart | 34 ++++--------- .../lib/src/xeps/xep_0384/xep_0384.dart | 51 ++++++++++++------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/examples_dart/bin/omemo_client.dart b/examples_dart/bin/omemo_client.dart index 183f999..2aef2fd 100644 --- a/examples_dart/bin/omemo_client.dart +++ b/examples_dart/bin/omemo_client.dart @@ -6,24 +6,6 @@ import 'package:moxxmpp/moxxmpp.dart'; import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart'; import 'package:omemo_dart/omemo_dart.dart' as omemo; -class TestingOmemoManager extends BaseOmemoManager { - TestingOmemoManager(this._encryptToJid); - - final JID _encryptToJid; - - late omemo.OmemoManager manager; - - @override - Future getOmemoManager() async { - return manager; - } - - @override - Future shouldEncryptStanza(JID toJid, Stanza stanza) async { - return toJid.toBare() == _encryptToJid; - } -} - class TestingTCPSocketWrapper extends TCPSocketWrapper { @override bool onBadCertificate(dynamic certificate, String domain) { @@ -66,17 +48,21 @@ void main(List args) async { ); // Generate OMEMO data - final moxxmppOmemo = TestingOmemoManager(to); - final omemoManager = omemo.OmemoManager( + omemo.OmemoManager? oom; + final moxxmppOmemo = OmemoManager( + () async => oom!, + (toJid, _) async => toJid == to, + ); + oom = omemo.OmemoManager( await omemo.OmemoDevice.generateNewDevice(jid.toString(), opkAmount: 5), omemo.BlindTrustBeforeVerificationTrustManager(), moxxmppOmemo.sendEmptyMessageImpl, moxxmppOmemo.fetchDeviceList, moxxmppOmemo.fetchDeviceBundle, moxxmppOmemo.subscribeToDeviceListImpl, + moxxmppOmemo.publishDeviceImpl, ); - moxxmppOmemo.manager = omemoManager; - final deviceId = await omemoManager.getDeviceId(); + final deviceId = await oom.getDeviceId(); Logger.root.info('Our device id: $deviceId'); // Register the managers and negotiators @@ -85,7 +71,7 @@ void main(List args) async { DiscoManager([]), PubSubManager(), MessageManager(), - moxxmppOmemo, + moxxmppOmemo, ]); await connection.registerFeatureNegotiators([ SaslPlainNegotiator(), @@ -118,7 +104,7 @@ void main(List args) async { // Publish our bundle Logger.root.info('Publishing bundle'); - final device = await moxxmppOmemo.manager.getDevice(); + final device = await oom.getDevice(); final omemoResult = await moxxmppOmemo.publishBundle(await device.toBundle()); if (!omemoResult.isType()) { Logger.root.severe('Failed to publish OMEMO bundle: ${omemoResult.get()}'); diff --git a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart index bf3fc07..2ea82c3 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart @@ -26,6 +26,18 @@ import 'package:moxxmpp/src/xeps/xep_0384/types.dart'; import 'package:omemo_dart/omemo_dart.dart' as omemo; import 'package:xml/xml.dart'; +/// A callback that is executed whenever we need to acquire the OmemoManager backing +/// the manager. +typedef GetOmemoManagerCallback = Future Function(); + +/// A callback for figuring out whether a stanza should be encrypted or not. Note that +/// returning true here does not necessarily mean that a stanza gets encrypted because +/// handlers can indicate that a stanza should not be encrypted, e.g. PubSub. +typedef ShouldEncryptStanzaCallback = Future Function( + JID toJid, + Stanza stanza, +); + const _doNotEncryptList = [ // XEP-0033 DoNotEncrypt('addresses', extendedAddressingXmlns), @@ -42,8 +54,15 @@ const _doNotEncryptList = [ DoNotEncrypt('stanza-id', stableIdXmlns), ]; -abstract class BaseOmemoManager extends XmppManagerBase { - BaseOmemoManager() : super(omemoManager); +class OmemoManager extends XmppManagerBase { + OmemoManager(this._getOmemoManager, this._shouldEncryptStanza) + : super(omemoManager); + + /// Callback for getting the [omemo.OmemoManager]. + final GetOmemoManagerCallback _getOmemoManager; + + /// Callback for checking whether a stanza should be encrypted or not. + final ShouldEncryptStanzaCallback _shouldEncryptStanza; // TODO(Unknown): Technically, this is not always true @override @@ -112,22 +131,19 @@ abstract class BaseOmemoManager extends XmppManagerBase { } // Tell the OmemoManager - await (await getOmemoManager()).onDeviceListUpdate(jid.toString(), ids); + await (await _getOmemoManager()).onDeviceListUpdate(jid.toString(), ids); // Generate an event getAttributes().sendEvent(OmemoDeviceListUpdatedEvent(jid, ids)); } } - @visibleForOverriding - Future getOmemoManager(); - /// Wrapper around using getSessionManager and then calling getDeviceId on it. - Future _getDeviceId() async => (await getOmemoManager()).getDeviceId(); + Future _getDeviceId() async => (await _getOmemoManager()).getDeviceId(); /// Wrapper around using getSessionManager and then calling getDeviceId on it. Future _getDeviceBundle() async { - final om = await getOmemoManager(); + final om = await _getOmemoManager(); final device = await om.getDevice(); return device.toBundle(); } @@ -279,7 +295,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { /// Send a heartbeat message to [jid]. Future sendOmemoHeartbeat(String jid) async { - final om = await getOmemoManager(); + final om = await _getOmemoManager(); await om.sendOmemoHeartbeat(jid); } @@ -320,7 +336,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { } final toJid = JID.fromString(stanza.to!).toBare(); - final shouldEncryptResult = await shouldEncryptStanza(toJid, stanza); + final shouldEncryptResult = await _shouldEncryptStanza(toJid, stanza); if (!shouldEncryptResult && !state.forceEncryption) { logger.finest( 'Not encrypting stanza for $toJid: Both shouldEncryptStanza and forceEncryption are false.', @@ -347,7 +363,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { .getManagerById(carbonsManager) ?.isEnabled ?? false; - final om = await getOmemoManager(); + final om = await _getOmemoManager(); final encryptToJids = [ toJid.toString(), if (carbonsEnabled) getAttributes().getFullJID().toBare().toString(), @@ -397,12 +413,6 @@ abstract class BaseOmemoManager extends XmppManagerBase { ..encrypted = true; } - /// This function is called whenever a message is to be encrypted. If it returns true, - /// then the message will be encrypted. If it returns false, the message won't be - /// encrypted. - @visibleForOverriding - Future shouldEncryptStanza(JID toJid, Stanza stanza); - Future _onIncomingStanza( Stanza stanza, StanzaHandlerData state, @@ -434,7 +444,7 @@ abstract class BaseOmemoManager extends XmppManagerBase { } final sid = int.parse(header.attributes['sid']! as String); - final om = await getOmemoManager(); + final om = await _getOmemoManager(); final result = await om.onIncomingStanza( omemo.OmemoIncomingStanza( fromJid.toString(), @@ -644,6 +654,11 @@ abstract class BaseOmemoManager extends XmppManagerBase { await pm.subscribe(JID.fromString(jid), omemoDevicesXmlns); } + /// Implementation for publishing our device [device]. + Future publishDeviceImpl(omemo.OmemoDevice device) async { + await publishBundle(await device.toBundle()); + } + /// Attempts to find out if [jid] supports omemo:2. /// /// On success, returns whether [jid] has published a device list and device bundles. From fbbe4131487812abd9b95ac00a85477d89fbb061 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sun, 18 Jun 2023 20:59:54 +0200 Subject: [PATCH 5/8] feat(example): Improve the example code --- examples_dart/bin/omemo_client.dart | 64 +++++++++++++++++------------ examples_dart/lib/socket.dart | 22 ++++++++++ 2 files changed, 60 insertions(+), 26 deletions(-) create mode 100644 examples_dart/lib/socket.dart diff --git a/examples_dart/bin/omemo_client.dart b/examples_dart/bin/omemo_client.dart index 2aef2fd..53bf7e3 100644 --- a/examples_dart/bin/omemo_client.dart +++ b/examples_dart/bin/omemo_client.dart @@ -1,18 +1,12 @@ import 'package:args/args.dart'; import 'package:chalkdart/chalk.dart'; import 'package:cli_repl/cli_repl.dart'; +import 'package:example_dart/socket.dart'; import 'package:logging/logging.dart'; import 'package:moxxmpp/moxxmpp.dart'; import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart'; import 'package:omemo_dart/omemo_dart.dart' as omemo; -class TestingTCPSocketWrapper extends TCPSocketWrapper { - @override - bool onBadCertificate(dynamic certificate, String domain) { - return true; - } -} - void main(List args) async { // Set up logging Logger.root.level = Level.ALL; @@ -28,9 +22,23 @@ void main(List args) async { ..addOption('password') ..addOption('host') ..addOption('port') - ..addOption('to'); + ..addOption('to') + ..addOption('xmpps-srv'); final options = parser.parse(args); + // Parse a potential xmpps-client SRV record here. + // Format: --xmpps-srv ,,, + MoxSrvRecord? srvRecord; + if (options['xmpps-srv'] != null) { + final parts = (options['xmpps-srv']! as String).split(','); + srvRecord = MoxSrvRecord( + int.parse(parts[0]), + int.parse(parts[1]), + parts[2], + int.parse(parts[3]), + ); + } + // Connect final jid = JID.fromString(options['jid']! as String); final to = JID.fromString(options['to']! as String).toBare(); @@ -39,13 +47,13 @@ void main(List args) async { TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), ClientToServerNegotiator(), - TestingTCPSocketWrapper(), + ExampleTCPSocketWrapper(srvRecord), )..connectionSettings = ConnectionSettings( - jid: jid, - password: options['password']! as String, - host: options['host'] as String?, - port: portString != null ? int.parse(portString) : null, - ); + jid: jid, + password: options['password']! as String, + host: options['host'] as String?, + port: portString != null ? int.parse(portString) : null, + ); // Generate OMEMO data omemo.OmemoManager? oom; @@ -71,7 +79,7 @@ void main(List args) async { DiscoManager([]), PubSubManager(), MessageManager(), - moxxmppOmemo, + moxxmppOmemo, ]); await connection.registerFeatureNegotiators([ SaslPlainNegotiator(), @@ -87,15 +95,16 @@ void main(List args) async { Logger.root.info(event.extensions.keys.toList()); final body = event.encryptionError != null - ? chalk.red('Failed to decrypt message: ${event.encryptionError}') - : chalk.green(event.get()?.body ?? ''); - print('[${event.from.toString()}] ' + body); + ? chalk.red('Failed to decrypt message: ${event.encryptionError}') + : chalk.green(event.get()?.body ?? ''); + print('[${event.from.toString()}] $body'); } }); // Connect Logger.root.info('Connecting...'); - final result = await connection.connect(shouldReconnect: false, waitUntilLogin: true); + final result = + await connection.connect(shouldReconnect: false, waitUntilLogin: true); if (!result.isType()) { Logger.root.severe('Authentication failed!'); return; @@ -107,18 +116,21 @@ void main(List args) async { final device = await oom.getDevice(); final omemoResult = await moxxmppOmemo.publishBundle(await device.toBundle()); if (!omemoResult.isType()) { - Logger.root.severe('Failed to publish OMEMO bundle: ${omemoResult.get()}'); + Logger.root.severe( + 'Failed to publish OMEMO bundle: ${omemoResult.get()}'); return; } final repl = Repl(prompt: '> '); await for (final line in repl.runAsync()) { - await connection.getManagerById(messageManager)!.sendMessage( - to, - TypedMap.fromList([ - MessageBodyData(line), - ]), - ); + await connection + .getManagerById(messageManager)! + .sendMessage( + to, + TypedMap.fromList([ + MessageBodyData(line), + ]), + ); } // Disconnect diff --git a/examples_dart/lib/socket.dart b/examples_dart/lib/socket.dart new file mode 100644 index 0000000..3031050 --- /dev/null +++ b/examples_dart/lib/socket.dart @@ -0,0 +1,22 @@ +import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart'; + +/// A simple socket for examples that allows injection of SRV records (since +/// we cannot use moxdns here). +class ExampleTCPSocketWrapper extends TCPSocketWrapper { + ExampleTCPSocketWrapper(this.srvRecord); + + /// A potential SRV record to inject for testing. + final MoxSrvRecord? srvRecord; + + @override + bool onBadCertificate(dynamic certificate, String domain) { + return true; + } + + @override + Future> srvQuery(String domain, bool dnssec) async { + return [ + if (srvRecord != null) srvRecord!, + ]; + } +} From e3ca83670a9c1c55ed3ac1bc5b3b18987afef499 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sun, 18 Jun 2023 21:16:47 +0200 Subject: [PATCH 6/8] feat(example): Implement common argument parsing --- examples_dart/bin/omemo_client.dart | 40 ++++---------- examples_dart/lib/arguments.dart | 84 +++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 examples_dart/lib/arguments.dart diff --git a/examples_dart/bin/omemo_client.dart b/examples_dart/bin/omemo_client.dart index 53bf7e3..5241d03 100644 --- a/examples_dart/bin/omemo_client.dart +++ b/examples_dart/bin/omemo_client.dart @@ -1,10 +1,9 @@ -import 'package:args/args.dart'; import 'package:chalkdart/chalk.dart'; import 'package:cli_repl/cli_repl.dart'; +import 'package:example_dart/arguments.dart'; import 'package:example_dart/socket.dart'; import 'package:logging/logging.dart'; import 'package:moxxmpp/moxxmpp.dart'; -import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart'; import 'package:omemo_dart/omemo_dart.dart' as omemo; void main(List args) async { @@ -17,43 +16,22 @@ void main(List args) async { ); }); - final parser = ArgParser() - ..addOption('jid') - ..addOption('password') - ..addOption('host') - ..addOption('port') - ..addOption('to') - ..addOption('xmpps-srv'); - final options = parser.parse(args); - - // Parse a potential xmpps-client SRV record here. - // Format: --xmpps-srv ,,, - MoxSrvRecord? srvRecord; - if (options['xmpps-srv'] != null) { - final parts = (options['xmpps-srv']! as String).split(','); - srvRecord = MoxSrvRecord( - int.parse(parts[0]), - int.parse(parts[1]), - parts[2], - int.parse(parts[3]), - ); + final parser = ArgumentParser() + ..parser.addOption('to', help: 'The JID to send messages to'); + final options = parser.handleArguments(args); + if (options == null) { + return; } // Connect - final jid = JID.fromString(options['jid']! as String); + final jid = parser.jid; final to = JID.fromString(options['to']! as String).toBare(); - final portString = options['port'] as String?; final connection = XmppConnection( TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), ClientToServerNegotiator(), - ExampleTCPSocketWrapper(srvRecord), - )..connectionSettings = ConnectionSettings( - jid: jid, - password: options['password']! as String, - host: options['host'] as String?, - port: portString != null ? int.parse(portString) : null, - ); + ExampleTCPSocketWrapper(parser.srvRecord), + )..connectionSettings = parser.connectionSettings; // Generate OMEMO data omemo.OmemoManager? oom; diff --git a/examples_dart/lib/arguments.dart b/examples_dart/lib/arguments.dart new file mode 100644 index 0000000..94a8f10 --- /dev/null +++ b/examples_dart/lib/arguments.dart @@ -0,0 +1,84 @@ +import 'package:args/args.dart'; +import 'package:chalkdart/chalk.dart'; +import 'package:moxxmpp/moxxmpp.dart'; +import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart'; + +extension StringToInt on String { + int toInt() => int.parse(this); +} + +/// A wrapper around [ArgParser] for providing convenience functions and standard parameters +/// to the examples. +class ArgumentParser { + ArgumentParser() { + parser + ..addOption('jid', help: 'The JID to connect as') + ..addOption('password', help: 'The password to use for authenticating') + ..addOption('host', + help: + 'The host address to connect to (By default uses the domain part of the JID)') + ..addOption('port', help: 'The port to connect to') + ..addOption('xmpps-srv', + help: + 'Inject a SRV record for _xmpps-client._tcp. Format: ,,,') + ..addFlag('help', + abbr: 'h', + negatable: false, + defaultsTo: false, + help: 'Show this help text'); + } + + /// The [ArgParser] that handles parsing the arguments. + final ArgParser parser = ArgParser(); + + /// The parsed options. Only valid after calling [handleArguments]. + late ArgResults options; + + ArgResults? handleArguments(List args) { + options = parser.parse(args); + if (options['help']!) { + print(parser.usage); + return null; + } + + if (options['jid'] == null) { + print(chalk.red('No JID specified')); + print(parser.usage); + return null; + } + + if (options['password'] == null) { + print(chalk.red('No password specified')); + print(parser.usage); + return null; + } + + return options; + } + + /// The JID to connect as. + JID get jid => JID.fromString(options['jid']!).toBare(); + + /// Construct connection settings from the parsed options. + ConnectionSettings get connectionSettings => ConnectionSettings( + jid: jid, + password: options['password']!, + host: options['host'], + port: (options['port'] as String?)?.toInt(), + ); + + /// Construct an xmpps-client SRV record for injection, if specified. + MoxSrvRecord? get srvRecord { + if (options['xmpps-srv'] == null) { + return null; + } + + final parts = options['xmpps-srv']!.split(','); + return MoxSrvRecord( + int.parse(parts[0]), + int.parse(parts[1]), + parts[2], + int.parse(parts[3]), + ); + } +} From 9da6d319a32c6740ae0378da9c85fce4e7ae8fd4 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sun, 18 Jun 2023 21:30:35 +0200 Subject: [PATCH 7/8] feat(all): Use 0.5.0 of omemo_dart --- examples_dart/pubspec.yaml | 3 ++- packages/moxxmpp/pubspec.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples_dart/pubspec.yaml b/examples_dart/pubspec.yaml index 5f4e285..8478ba1 100644 --- a/examples_dart/pubspec.yaml +++ b/examples_dart/pubspec.yaml @@ -18,7 +18,8 @@ dependencies: hosted: https://git.polynom.me/api/packages/Moxxy/pub version: 0.3.1 omemo_dart: - path: ../../../Personal/omemo_dart + hosted: https://git.polynom.me/api/packages/PapaTutuWawa/pub + version: ^0.5.0 dependency_overrides: moxxmpp: diff --git a/packages/moxxmpp/pubspec.yaml b/packages/moxxmpp/pubspec.yaml index e7aeb68..6032af7 100644 --- a/packages/moxxmpp/pubspec.yaml +++ b/packages/moxxmpp/pubspec.yaml @@ -20,7 +20,8 @@ dependencies: hosted: https://git.polynom.me/api/packages/Moxxy/pub version: ^0.2.0 omemo_dart: - path: ../../../../Personal/omemo_dart + hosted: https://git.polynom.me/api/packages/PapaTutuWawa/pub + version: ^0.5.0 random_string: ^2.3.1 saslprep: ^1.0.2 synchronized: ^3.0.0+2 From 3621f2709ac6800d49de79066a2ba17f9fb04c46 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Sun, 18 Jun 2023 21:57:53 +0200 Subject: [PATCH 8/8] chore(docs): Update changelog --- packages/moxxmpp/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/moxxmpp/CHANGELOG.md b/packages/moxxmpp/CHANGELOG.md index 12185d0..3960a67 100644 --- a/packages/moxxmpp/CHANGELOG.md +++ b/packages/moxxmpp/CHANGELOG.md @@ -16,6 +16,7 @@ - **BREAKING**: `MessageEvent` now makes use of `TypedMap`. - **BREAKING**: Removed `PresenceReceivedEvent`. Use a manager registering handlers with priority greater than `[PresenceManager.presenceHandlerPriority]` instead. - **BREAKING**: `ChatState.toString()` is now `ChatState.toName()` +- **BREAKING**: Overriding `BaseOmemoManager` is no longer required. `OmemoManager` now takes callback methods instead. ## 0.3.1