diff --git a/packages/moxxmpp/lib/moxxmpp.dart b/packages/moxxmpp/lib/moxxmpp.dart index 2f74fca..6c321a7 100644 --- a/packages/moxxmpp/lib/moxxmpp.dart +++ b/packages/moxxmpp/lib/moxxmpp.dart @@ -77,6 +77,7 @@ export 'package:moxxmpp/src/xeps/xep_0384/helpers.dart'; export 'package:moxxmpp/src/xeps/xep_0384/types.dart'; export 'package:moxxmpp/src/xeps/xep_0384/xep_0384.dart'; export 'package:moxxmpp/src/xeps/xep_0385.dart'; +export 'package:moxxmpp/src/xeps/xep_0386.dart'; export 'package:moxxmpp/src/xeps/xep_0414.dart'; export 'package:moxxmpp/src/xeps/xep_0424.dart'; export 'package:moxxmpp/src/xeps/xep_0444.dart'; diff --git a/packages/moxxmpp/lib/src/namespaces.dart b/packages/moxxmpp/lib/src/namespaces.dart index f7a11eb..38cf744 100644 --- a/packages/moxxmpp/lib/src/namespaces.dart +++ b/packages/moxxmpp/lib/src/namespaces.dart @@ -116,6 +116,9 @@ const omemoBundlesXmlns = 'urn:xmpp:omemo:2:bundles'; // XEP-0385 const simsXmlns = 'urn:xmpp:sims:1'; +// XEP-0386 +const bind2Xmlns = 'urn:xmpp:bind:0'; + // XEP-0388 const sasl2Xmlns = 'urn:xmpp:sasl:2'; diff --git a/packages/moxxmpp/lib/src/negotiators/namespaces.dart b/packages/moxxmpp/lib/src/negotiators/namespaces.dart index 1605329..755835d 100644 --- a/packages/moxxmpp/lib/src/negotiators/namespaces.dart +++ b/packages/moxxmpp/lib/src/negotiators/namespaces.dart @@ -8,3 +8,4 @@ const resourceBindingNegotiator = 'im.moxxmpp.core.resource'; const streamManagementNegotiator = 'im.moxxmpp.xeps.sm'; const startTlsNegotiator = 'im.moxxmpp.core.starttls'; const sasl2Negotiator = 'org.moxxmpp.sasl.sasl2'; +const bind2Negotiator = 'org.moxxmpp.bind2'; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0386.dart b/packages/moxxmpp/lib/src/xeps/xep_0386.dart new file mode 100644 index 0000000..7bc40b2 --- /dev/null +++ b/packages/moxxmpp/lib/src/xeps/xep_0386.dart @@ -0,0 +1,43 @@ +import 'package:moxxmpp/src/namespaces.dart'; +import 'package:moxxmpp/src/negotiators/namespaces.dart'; +import 'package:moxxmpp/src/negotiators/negotiator.dart'; +import 'package:moxxmpp/src/negotiators/sasl2.dart'; +import 'package:moxxmpp/src/stringxml.dart'; +import 'package:moxxmpp/src/types/result.dart'; + +/// A negotiator implementing XEP-0386. This negotiator is useless on its own +/// and requires a [Sasl2Negotiator] to be registered. +class Bind2Negotiator extends Sasl2FeatureNegotiator { + Bind2Negotiator() : super(0, false, bind2Xmlns, bind2Negotiator); + + @override + Future> negotiate( + XMLNode nonza, + ) async { + return const Result(NegotiatorState.done); + } + + @override + Future> onSasl2FeaturesReceived(XMLNode sasl2Features) async { + return [ + XMLNode.xmlns( + tag: 'bind', + xmlns: bind2Xmlns, + ), + ]; + } + + @override + Future> onSasl2Success(XMLNode response) async { + attributes.removeNegotiatingFeature(bindXmlns); + + return const Result(true); + } + + @override + Future postRegisterCallback() async { + attributes + .getNegotiatorById(sasl2Negotiator)! + .registerNegotiator(this); + } +} diff --git a/packages/moxxmpp/test/xeps/xep_0386_test.dart b/packages/moxxmpp/test/xeps/xep_0386_test.dart new file mode 100644 index 0000000..c9aaccf --- /dev/null +++ b/packages/moxxmpp/test/xeps/xep_0386_test.dart @@ -0,0 +1,81 @@ +import 'package:moxxmpp/moxxmpp.dart'; +import 'package:test/test.dart'; +import '../helpers/logging.dart'; +import '../helpers/xmpp.dart'; + +void main() { + initLogger(); + + test('Test simple Bind2 negotiation', () async { + final fakeSocket = StubTCPSocket([ + StringExpectation( + "", + ''' + + + + PLAIN + + + PLAIN + + + + + + + + ''', + ), + StanzaExpectation( + "moxxmppPapaTutuWawa's awesome deviceAHBvbHlub21kaXZpc2lvbgBhYWFh", + ''' + + polynomdivision@test.server/random.resource + + ''', + ), + ]); + final conn = XmppConnection( + TestingReconnectionPolicy(), + AlwaysConnectedConnectivityManager(), + fakeSocket, + )..setConnectionSettings( + ConnectionSettings( + jid: JID.fromString('polynomdivision@test.server'), + password: 'aaaa', + useDirectTLS: true, + ), + ); + await conn.registerManagers([ + PresenceManager(), + RosterManager(TestingRosterStateManager('', [])), + DiscoManager([]), + ]); + await conn.registerFeatureNegotiators([ + SaslPlainNegotiator(), + ResourceBindingNegotiator(), + Bind2Negotiator(), + Sasl2Negotiator( + userAgent: const UserAgent( + id: 'd4565fa7-4d72-4749-b3d3-740edbf87770', + software: 'moxxmpp', + device: "PapaTutuWawa's awesome device", + ), + ), + ]); + + final result = await conn.connect( + waitUntilLogin: true, + shouldReconnect: false, + enableReconnectOnSuccess: false, + ); + expect(result.isType(), false); + expect(conn.resource, 'random.resource'); + }); +}