From 916be1c927c4c15dd615d3c5543e848e00a951af Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Wed, 5 Apr 2023 13:17:18 +0200 Subject: [PATCH] fix(core): Fix components' stanza matching --- README.md | 9 ++ example/pubspec_overrides.yaml | 6 - {example => example_flutter}/.gitignore | 0 {example => example_flutter}/.metadata | 0 {example => example_flutter}/README.md | 0 .../analysis_options.yaml | 0 .../android/.gitignore | 0 .../android/app/build.gradle | 0 .../android/app/src/debug/AndroidManifest.xml | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../com/example/example/MainActivity.kt | 0 .../res/drawable-v21/launch_background.xml | 0 .../main/res/drawable/launch_background.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values-night/styles.xml | 0 .../app/src/main/res/values/styles.xml | 0 .../app/src/profile/AndroidManifest.xml | 0 .../android/build.gradle | 0 .../android/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../android/settings.gradle | 0 {example => example_flutter}/lib/main.dart | 0 {example => example_flutter}/linux/.gitignore | 0 .../linux/CMakeLists.txt | 0 .../linux/flutter/CMakeLists.txt | 0 .../flutter/generated_plugin_registrant.cc | 0 .../flutter/generated_plugin_registrant.h | 0 .../linux/flutter/generated_plugins.cmake | 0 {example => example_flutter}/linux/main.cc | 0 .../linux/my_application.cc | 0 .../linux/my_application.h | 0 {example => example_flutter}/pubspec.yaml | 0 examples_dart/.gitignore | 6 + examples_dart/README.md | 7 ++ examples_dart/analysis_options.yaml | 30 +++++ examples_dart/bin/component.dart | 90 +++++++++++++ examples_dart/pubspec.yaml | 24 ++++ packages/moxxmpp/lib/src/connection.dart | 4 + packages/moxxmpp/lib/src/handlers/base.dart | 3 + packages/moxxmpp/lib/src/handlers/client.dart | 3 + .../moxxmpp/lib/src/handlers/component.dart | 3 + .../moxxmpp/lib/src/managers/handlers.dart | 118 ++++++++++-------- packages/moxxmpp/lib/src/presence.dart | 4 +- packages/moxxmpp/lib/src/stanza.dart | 20 ++- packages/moxxmpp/test/stanzahandler_test.dart | 60 ++++++--- packages/moxxmpp/test/xeps/xep_0030_test.dart | 2 +- packages/moxxmpp/test/xeps/xep_0198_test.dart | 17 ++- 51 files changed, 324 insertions(+), 82 deletions(-) delete mode 100644 example/pubspec_overrides.yaml rename {example => example_flutter}/.gitignore (100%) rename {example => example_flutter}/.metadata (100%) rename {example => example_flutter}/README.md (100%) rename {example => example_flutter}/analysis_options.yaml (100%) rename {example => example_flutter}/android/.gitignore (100%) rename {example => example_flutter}/android/app/build.gradle (100%) rename {example => example_flutter}/android/app/src/debug/AndroidManifest.xml (100%) rename {example => example_flutter}/android/app/src/main/AndroidManifest.xml (100%) rename {example => example_flutter}/android/app/src/main/kotlin/com/example/example/MainActivity.kt (100%) rename {example => example_flutter}/android/app/src/main/res/drawable-v21/launch_background.xml (100%) rename {example => example_flutter}/android/app/src/main/res/drawable/launch_background.xml (100%) rename {example => example_flutter}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {example => example_flutter}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {example => example_flutter}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {example => example_flutter}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {example => example_flutter}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {example => example_flutter}/android/app/src/main/res/values-night/styles.xml (100%) rename {example => example_flutter}/android/app/src/main/res/values/styles.xml (100%) rename {example => example_flutter}/android/app/src/profile/AndroidManifest.xml (100%) rename {example => example_flutter}/android/build.gradle (100%) rename {example => example_flutter}/android/gradle.properties (100%) rename {example => example_flutter}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename {example => example_flutter}/android/settings.gradle (100%) rename {example => example_flutter}/lib/main.dart (100%) rename {example => example_flutter}/linux/.gitignore (100%) rename {example => example_flutter}/linux/CMakeLists.txt (100%) rename {example => example_flutter}/linux/flutter/CMakeLists.txt (100%) rename {example => example_flutter}/linux/flutter/generated_plugin_registrant.cc (100%) rename {example => example_flutter}/linux/flutter/generated_plugin_registrant.h (100%) rename {example => example_flutter}/linux/flutter/generated_plugins.cmake (100%) rename {example => example_flutter}/linux/main.cc (100%) rename {example => example_flutter}/linux/my_application.cc (100%) rename {example => example_flutter}/linux/my_application.h (100%) rename {example => example_flutter}/pubspec.yaml (100%) create mode 100644 examples_dart/.gitignore create mode 100644 examples_dart/README.md create mode 100644 examples_dart/analysis_options.yaml create mode 100644 examples_dart/bin/component.dart create mode 100644 examples_dart/pubspec.yaml diff --git a/README.md b/README.md index fa34344..3b8c78a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,15 @@ To run the example, make sure that Flutter is correctly set up and working. If y the development shell provided by the NixOS Flake, ensure that `ANDROID_HOME` and `ANDROID_AVD_HOME` are pointing to the correct directories. +## Examples + +This repository contains 2 types of examples: + +- `example_flutter`: An example of using moxxmpp using Flutter +- `examples_dart`: A collection of pure Dart examples for showing different aspects of moxxmpp + +For more information, see the respective README files. + ## License See `./LICENSE`. diff --git a/example/pubspec_overrides.yaml b/example/pubspec_overrides.yaml deleted file mode 100644 index d2e8e37..0000000 --- a/example/pubspec_overrides.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# melos_managed_dependency_overrides: moxxmpp,moxxmpp_socket_tcp -dependency_overrides: - moxxmpp: - path: ../packages/moxxmpp - moxxmpp_socket_tcp: - path: ../packages/moxxmpp_socket_tcp diff --git a/example/.gitignore b/example_flutter/.gitignore similarity index 100% rename from example/.gitignore rename to example_flutter/.gitignore diff --git a/example/.metadata b/example_flutter/.metadata similarity index 100% rename from example/.metadata rename to example_flutter/.metadata diff --git a/example/README.md b/example_flutter/README.md similarity index 100% rename from example/README.md rename to example_flutter/README.md diff --git a/example/analysis_options.yaml b/example_flutter/analysis_options.yaml similarity index 100% rename from example/analysis_options.yaml rename to example_flutter/analysis_options.yaml diff --git a/example/android/.gitignore b/example_flutter/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to example_flutter/android/.gitignore diff --git a/example/android/app/build.gradle b/example_flutter/android/app/build.gradle similarity index 100% rename from example/android/app/build.gradle rename to example_flutter/android/app/build.gradle diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example_flutter/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from example/android/app/src/debug/AndroidManifest.xml rename to example_flutter/android/app/src/debug/AndroidManifest.xml diff --git a/example/android/app/src/main/AndroidManifest.xml b/example_flutter/android/app/src/main/AndroidManifest.xml similarity index 100% rename from example/android/app/src/main/AndroidManifest.xml rename to example_flutter/android/app/src/main/AndroidManifest.xml diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example_flutter/android/app/src/main/kotlin/com/example/example/MainActivity.kt similarity index 100% rename from example/android/app/src/main/kotlin/com/example/example/MainActivity.kt rename to example_flutter/android/app/src/main/kotlin/com/example/example/MainActivity.kt diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example_flutter/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable-v21/launch_background.xml rename to example_flutter/android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example_flutter/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to example_flutter/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example_flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to example_flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example_flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to example_flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example_flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to example_flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example_flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to example_flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example_flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to example_flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example_flutter/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from example/android/app/src/main/res/values-night/styles.xml rename to example_flutter/android/app/src/main/res/values-night/styles.xml diff --git a/example/android/app/src/main/res/values/styles.xml b/example_flutter/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to example_flutter/android/app/src/main/res/values/styles.xml diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example_flutter/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from example/android/app/src/profile/AndroidManifest.xml rename to example_flutter/android/app/src/profile/AndroidManifest.xml diff --git a/example/android/build.gradle b/example_flutter/android/build.gradle similarity index 100% rename from example/android/build.gradle rename to example_flutter/android/build.gradle diff --git a/example/android/gradle.properties b/example_flutter/android/gradle.properties similarity index 100% rename from example/android/gradle.properties rename to example_flutter/android/gradle.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example_flutter/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to example_flutter/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example/android/settings.gradle b/example_flutter/android/settings.gradle similarity index 100% rename from example/android/settings.gradle rename to example_flutter/android/settings.gradle diff --git a/example/lib/main.dart b/example_flutter/lib/main.dart similarity index 100% rename from example/lib/main.dart rename to example_flutter/lib/main.dart diff --git a/example/linux/.gitignore b/example_flutter/linux/.gitignore similarity index 100% rename from example/linux/.gitignore rename to example_flutter/linux/.gitignore diff --git a/example/linux/CMakeLists.txt b/example_flutter/linux/CMakeLists.txt similarity index 100% rename from example/linux/CMakeLists.txt rename to example_flutter/linux/CMakeLists.txt diff --git a/example/linux/flutter/CMakeLists.txt b/example_flutter/linux/flutter/CMakeLists.txt similarity index 100% rename from example/linux/flutter/CMakeLists.txt rename to example_flutter/linux/flutter/CMakeLists.txt diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example_flutter/linux/flutter/generated_plugin_registrant.cc similarity index 100% rename from example/linux/flutter/generated_plugin_registrant.cc rename to example_flutter/linux/flutter/generated_plugin_registrant.cc diff --git a/example/linux/flutter/generated_plugin_registrant.h b/example_flutter/linux/flutter/generated_plugin_registrant.h similarity index 100% rename from example/linux/flutter/generated_plugin_registrant.h rename to example_flutter/linux/flutter/generated_plugin_registrant.h diff --git a/example/linux/flutter/generated_plugins.cmake b/example_flutter/linux/flutter/generated_plugins.cmake similarity index 100% rename from example/linux/flutter/generated_plugins.cmake rename to example_flutter/linux/flutter/generated_plugins.cmake diff --git a/example/linux/main.cc b/example_flutter/linux/main.cc similarity index 100% rename from example/linux/main.cc rename to example_flutter/linux/main.cc diff --git a/example/linux/my_application.cc b/example_flutter/linux/my_application.cc similarity index 100% rename from example/linux/my_application.cc rename to example_flutter/linux/my_application.cc diff --git a/example/linux/my_application.h b/example_flutter/linux/my_application.h similarity index 100% rename from example/linux/my_application.h rename to example_flutter/linux/my_application.h diff --git a/example/pubspec.yaml b/example_flutter/pubspec.yaml similarity index 100% rename from example/pubspec.yaml rename to example_flutter/pubspec.yaml diff --git a/examples_dart/.gitignore b/examples_dart/.gitignore new file mode 100644 index 0000000..3c8a157 --- /dev/null +++ b/examples_dart/.gitignore @@ -0,0 +1,6 @@ +# Files and directories created by pub. +.dart_tool/ +.packages + +# Conventional directory for build output. +build/ diff --git a/examples_dart/README.md b/examples_dart/README.md new file mode 100644 index 0000000..c533364 --- /dev/null +++ b/examples_dart/README.md @@ -0,0 +1,7 @@ +# Dart Examples + +Run using `dart run bin/.dart`. + +## Examples + +- `component.dart`: Use moxxmpp to implement a component using XEP-0114. diff --git a/examples_dart/analysis_options.yaml b/examples_dart/analysis_options.yaml new file mode 100644 index 0000000..dee8927 --- /dev/null +++ b/examples_dart/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/examples_dart/bin/component.dart b/examples_dart/bin/component.dart new file mode 100644 index 0000000..051751e --- /dev/null +++ b/examples_dart/bin/component.dart @@ -0,0 +1,90 @@ +import 'package:logging/logging.dart'; +import 'package:moxxmpp/moxxmpp.dart'; +import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart'; + +class TestingTCPSocketWrapper extends TCPSocketWrapper { + @override + bool onBadCertificate(dynamic certificate, String domain) { + return true; + } +} + +class EchoMessageManager extends XmppManagerBase { + EchoMessageManager() : super('org.moxxy.example.message'); + + @override + Future isSupported() async => true; + + @override + List getIncomingStanzaHandlers() => [ + StanzaHandler( + stanzaTag: 'message', + callback: _onMessage, + priority: -100, + xmlns: null, + ) + ]; + + Future _onMessage( + Stanza stanza, + StanzaHandlerData state, + ) async { + final body = stanza.firstTag('body'); + if (body == null) return state.copyWith(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"', + ), + ], + ), + awaitable: false, + ); + + return state.copyWith(done: true); + } +} + +void main(List arguments) async { + Logger.root.level = Level.ALL; + Logger.root.onRecord.listen((record) { + // ignore: avoid_print + print( + '[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}', + ); + }); + + final conn = XmppConnection( + TestingReconnectionPolicy(), + AlwaysConnectedConnectivityManager(), + ComponentToServerNegotiator(), + TestingTCPSocketWrapper(), + )..connectionSettings = ConnectionSettings( + jid: JID.fromString('component.localhost'), + password: 'abc123', + host: '127.0.0.1', + port: 8888, + ); + await conn.registerManagers([ + EchoMessageManager(), + ]); + + final result = await conn.connect( + waitUntilLogin: true, + shouldReconnect: false, + enableReconnectOnSuccess: false, + ); + if (result.isType()) { + print('Failed to connect as component'); + return; + } + + // Just block for some time to test the connection + await Future.delayed(const Duration(seconds: 9999)); +} diff --git a/examples_dart/pubspec.yaml b/examples_dart/pubspec.yaml new file mode 100644 index 0000000..3e852ab --- /dev/null +++ b/examples_dart/pubspec.yaml @@ -0,0 +1,24 @@ +name: example_dart +description: A sample command-line application. +version: 1.0.0 +# homepage: https://www.example.com + +environment: + sdk: '>=2.18.0 <3.0.0' + +dependencies: + logging: ^1.0.2 + moxxmpp: + hosted: https://git.polynom.me/api/packages/Moxxy/pub + version: 0.3.0 + moxxmpp_socket_tcp: + hosted: https://git.polynom.me/api/packages/Moxxy/pub + version: 0.3.0 + +dependency_overrides: + moxxmpp: + path: ../packages/moxxmpp + +dev_dependencies: + lints: ^2.0.0 + test: ^1.16.0 diff --git a/packages/moxxmpp/lib/src/connection.dart b/packages/moxxmpp/lib/src/connection.dart index d5a957d..e3e9ec8 100644 --- a/packages/moxxmpp/lib/src/connection.dart +++ b/packages/moxxmpp/lib/src/connection.dart @@ -446,6 +446,9 @@ class XmppConnection { break; } } + stanza_ = stanza_.copyWith( + xmlns: _negotiationsHandler.getStanzaNamespace(), + ); _log.fine('Running pre stanza handlers..'); final data = await _runOutgoingPreStanzaHandlers( @@ -731,6 +734,7 @@ class XmppConnection { ), ); if (!incomingHandlers.done) { + _log.warning('Returning error for unhandled stanza'); await handleUnhandledStanza(this, incomingPreHandlers); } } diff --git a/packages/moxxmpp/lib/src/handlers/base.dart b/packages/moxxmpp/lib/src/handlers/base.dart index 03bf820..7b84886 100644 --- a/packages/moxxmpp/lib/src/handlers/base.dart +++ b/packages/moxxmpp/lib/src/handlers/base.dart @@ -81,6 +81,9 @@ abstract class NegotiationsHandler { log = Logger(toString()); } + /// Returns the xmlns attribute that stanzas should have. + String getStanzaNamespace(); + /// Registers the negotiator [negotiator] against this negotiations handler. void registerNegotiator(XmppFeatureNegotiatorBase negotiator); diff --git a/packages/moxxmpp/lib/src/handlers/client.dart b/packages/moxxmpp/lib/src/handlers/client.dart index eff1309..a7d2cc0 100644 --- a/packages/moxxmpp/lib/src/handlers/client.dart +++ b/packages/moxxmpp/lib/src/handlers/client.dart @@ -35,6 +35,9 @@ class ClientToServerNegotiator extends NegotiationsHandler { /// The currently active negotiator. XmppFeatureNegotiatorBase? _currentNegotiator; + @override + String getStanzaNamespace() => stanzaXmlns; + @override void registerNegotiator(XmppFeatureNegotiatorBase negotiator) { negotiators[negotiator.id] = negotiator; diff --git a/packages/moxxmpp/lib/src/handlers/component.dart b/packages/moxxmpp/lib/src/handlers/component.dart index 540f17a..3fe4cb1 100644 --- a/packages/moxxmpp/lib/src/handlers/component.dart +++ b/packages/moxxmpp/lib/src/handlers/component.dart @@ -41,6 +41,9 @@ class ComponentToServerNegotiator extends NegotiationsHandler { /// The state the negotiation handler is currently in ComponentToServerState _state = ComponentToServerState.idle; + @override + String getStanzaNamespace() => componentAcceptXmlns; + @override void registerNegotiator(XmppFeatureNegotiatorBase negotiator) {} diff --git a/packages/moxxmpp/lib/src/managers/handlers.dart b/packages/moxxmpp/lib/src/managers/handlers.dart index 32eef43..ca7dea3 100644 --- a/packages/moxxmpp/lib/src/managers/handlers.dart +++ b/packages/moxxmpp/lib/src/managers/handlers.dart @@ -4,88 +4,108 @@ import 'package:moxxmpp/src/namespaces.dart'; import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stringxml.dart'; +/// A Handler is responsible for matching any kind of toplevel item in the XML stream +/// (stanzas and Nonzas). For that, its [matches] method is called. What happens +/// next depends on the subclass. +// ignore: one_member_abstracts abstract class Handler { - const Handler(this.matchStanzas, {this.nonzaTag, this.nonzaXmlns}); - final String? nonzaTag; - final String? nonzaXmlns; - final bool matchStanzas; - /// Returns true if the node matches the description provided by this [Handler]. + bool matches(XMLNode node); +} + +/// A Handler that specialises in matching Nonzas (and stanzas). +class NonzaHandler extends Handler { + NonzaHandler({ + required this.callback, + this.nonzaTag, + this.nonzaXmlns, + }); + + /// The function to call when a nonza matches the description. + final Future Function(XMLNode) callback; + + /// The expected tag of a matching nonza. + final String? nonzaTag; + + // The expected xmlns attribute of a matching nonza. + final String? nonzaXmlns; + + @override bool matches(XMLNode node) { - var matches = false; - + var matches = true; if (nonzaTag == null && nonzaXmlns == null) { - matches = true; - } - - if (nonzaXmlns != null && nonzaTag != null) { - matches = (node.attributes['xmlns'] ?? '') == nonzaXmlns! && - node.tag == nonzaTag!; - } - - if (matchStanzas && nonzaTag == null) { - matches = ['iq', 'presence', 'message'].contains(node.tag); + return true; + } else { + if (nonzaXmlns != null) { + matches &= node.attributes['xmlns'] == nonzaXmlns; + } + if (nonzaTag != null) { + matches &= node.tag == nonzaTag; + } } return matches; } } -class NonzaHandler extends Handler { - NonzaHandler({ - required this.callback, - String? nonzaTag, - String? nonzaXmlns, - }) : super( - false, - nonzaTag: nonzaTag, - nonzaXmlns: nonzaXmlns, - ); - final Future Function(XMLNode) callback; -} - +/// A Handler that only matches stanzas. class StanzaHandler extends Handler { StanzaHandler({ required this.callback, this.tagXmlns, this.tagName, this.priority = 0, - String? stanzaTag, - }) : super( - true, - nonzaTag: stanzaTag, - nonzaXmlns: stanzaXmlns, - ); + this.stanzaTag, + this.xmlns = stanzaXmlns, + }); + + /// If specified, then the stanza must contain a direct child with a tag equal to + /// [tagName]. final String? tagName; + + /// If specified, then the stanza must contain a direct child with a xmlns attribute + /// equal to [tagXmlns]. If [tagName] is also non-null, then the element must also + /// have a tag equal to [tagName]. final String? tagXmlns; + + /// If specified, the matching stanza must have a tag equal to [stanzaTag]. + final String? stanzaTag; + + /// If specified, then the stanza must have a xmlns attribute equal to [xmlns]. + /// This defaults to [stanzaXmlns], but can be set to any other value or null. This + /// is useful, for example, for components. + final String? xmlns; + + /// The priority after which [StanzaHandler]s are sorted. final int priority; + + /// The function to call when a stanza matches the description. final Future Function(Stanza, StanzaHandlerData) callback; @override bool matches(XMLNode node) { - var matches = super.matches(node); - - if (matches == false) { - return false; + var matches = ['iq', 'message', 'presence'].contains(node.tag); + if (stanzaTag != null) { + matches &= node.tag == stanzaTag; + } + if (xmlns != null) { + matches &= node.xmlns == xmlns; } if (tagName != null) { final firstTag = node.firstTag(tagName!, xmlns: tagXmlns); + matches &= firstTag != null; - matches = firstTag != null; + if (tagXmlns != null) { + matches &= firstTag?.xmlns == tagXmlns; + } } else if (tagXmlns != null) { - return listContains( + matches &= listContains( node.children, - (XMLNode node_) => - node_.attributes.containsKey('xmlns') && - node_.attributes['xmlns'] == tagXmlns, + (XMLNode node_) => node_.attributes['xmlns'] == tagXmlns, ); } - if (tagName == null && tagXmlns == null) { - matches = true; - } - return matches; } } diff --git a/packages/moxxmpp/lib/src/presence.dart b/packages/moxxmpp/lib/src/presence.dart index 6dcc962..0a73171 100644 --- a/packages/moxxmpp/lib/src/presence.dart +++ b/packages/moxxmpp/lib/src/presence.dart @@ -107,11 +107,13 @@ class PresenceManager extends XmppManagerBase { } final attrs = getAttributes(); - attrs.sendNonza( + await attrs.sendStanza( Stanza.presence( from: attrs.getFullJID().toString(), children: children, ), + awaitable: false, + addId: false, ); } diff --git a/packages/moxxmpp/lib/src/stanza.dart b/packages/moxxmpp/lib/src/stanza.dart index 1e7bb1d..47a06fe 100644 --- a/packages/moxxmpp/lib/src/stanza.dart +++ b/packages/moxxmpp/lib/src/stanza.dart @@ -33,6 +33,7 @@ class Stanza extends XMLNode { List children = const [], required String tag, Map attributes = const {}, + String? xmlns, }) : super( tag: tag, attributes: { @@ -45,7 +46,7 @@ class Stanza extends XMLNode { ...from != null ? {'from': from} : {}, - 'xmlns': stanzaXmlns + if (xmlns != null) 'xmlns': xmlns, }, children: children, ); @@ -57,6 +58,7 @@ class Stanza extends XMLNode { String? id, List children = const [], Map? attributes = const {}, + String? xmlns, }) { return Stanza( tag: 'iq', @@ -64,8 +66,9 @@ class Stanza extends XMLNode { to: to, id: id, type: type, - attributes: {...attributes!, 'xmlns': stanzaXmlns}, + attributes: {...attributes!}, children: children, + xmlns: xmlns, ); } @@ -76,6 +79,7 @@ class Stanza extends XMLNode { String? id, List children = const [], Map? attributes = const {}, + String? xmlns, }) { return Stanza( tag: 'presence', @@ -83,8 +87,9 @@ class Stanza extends XMLNode { to: to, id: id, type: type, - attributes: {...attributes!, 'xmlns': stanzaXmlns}, + attributes: {...attributes!}, children: children, + xmlns: xmlns, ); } factory Stanza.message({ @@ -94,6 +99,7 @@ class Stanza extends XMLNode { String? id, List children = const [], Map? attributes = const {}, + String? xmlns, }) { return Stanza( tag: 'message', @@ -101,8 +107,9 @@ class Stanza extends XMLNode { to: to, id: id, type: type, - attributes: {...attributes!, 'xmlns': stanzaXmlns}, + attributes: {...attributes!}, children: children, + xmlns: xmlns, ); } @@ -134,6 +141,7 @@ class Stanza extends XMLNode { String? to, String? type, List? children, + String? xmlns, }) { return Stanza( tag: tag, @@ -142,6 +150,10 @@ class Stanza extends XMLNode { id: id ?? this.id, type: type ?? this.type, children: children ?? this.children, + attributes: { + ...attributes.cast(), + }, + xmlns: xmlns ?? this.xmlns, ); } } diff --git a/packages/moxxmpp/test/stanzahandler_test.dart b/packages/moxxmpp/test/stanzahandler_test.dart index 6bcee1c..3c3bae8 100644 --- a/packages/moxxmpp/test/stanzahandler_test.dart +++ b/packages/moxxmpp/test/stanzahandler_test.dart @@ -3,9 +3,11 @@ import 'package:test/test.dart'; final stanza1 = Stanza.iq( children: [XMLNode.xmlns(tag: 'tag', xmlns: 'owo')], + xmlns: stanzaXmlns, ); final stanza2 = Stanza.message( children: [XMLNode.xmlns(tag: 'some-other-tag', xmlns: 'owo')], + xmlns: stanzaXmlns, ); void main() { @@ -19,11 +21,17 @@ void main() { ), ); - expect(handler.matches(Stanza.iq()), true); - expect(handler.matches(Stanza.message()), true); - expect(handler.matches(Stanza.presence()), true); + expect(handler.matches(Stanza.iq(xmlns: stanzaXmlns)), true); + expect(handler.matches(Stanza.message(xmlns: stanzaXmlns)), true); + expect(handler.matches(Stanza.presence(xmlns: stanzaXmlns)), true); expect(handler.matches(stanza1), true); expect(handler.matches(stanza2), true); + expect( + handler.matches( + XMLNode.xmlns(tag: 'active', xmlns: csiXmlns), + ), + false, + ); }); test('xmlns matching', () { final handler = StanzaHandler( @@ -36,12 +44,13 @@ void main() { tagXmlns: 'owo', ); - expect(handler.matches(Stanza.iq()), false); - expect(handler.matches(Stanza.message()), false); - expect(handler.matches(Stanza.presence()), false); + expect(handler.matches(Stanza.iq(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.message(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.presence(xmlns: stanzaXmlns)), false); expect(handler.matches(stanza1), true); expect(handler.matches(stanza2), true); }); + test('stanzaTag matching', () { var run = false; final handler = StanzaHandler( @@ -57,9 +66,9 @@ void main() { stanzaTag: 'iq', ); - expect(handler.matches(Stanza.iq()), true); - expect(handler.matches(Stanza.message()), false); - expect(handler.matches(Stanza.presence()), false); + expect(handler.matches(Stanza.iq(xmlns: stanzaXmlns)), true); + expect(handler.matches(Stanza.message(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.presence(xmlns: stanzaXmlns)), false); expect(handler.matches(stanza1), true); expect(handler.matches(stanza2), false); @@ -74,6 +83,7 @@ void main() { ); expect(run, true); }); + test('tagName matching', () { final handler = StanzaHandler( callback: (stanza, _) async => StanzaHandlerData( @@ -85,12 +95,13 @@ void main() { tagName: 'tag', ); - expect(handler.matches(Stanza.iq()), false); - expect(handler.matches(Stanza.message()), false); - expect(handler.matches(Stanza.presence()), false); + expect(handler.matches(Stanza.iq(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.message(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.presence(xmlns: stanzaXmlns)), false); expect(handler.matches(stanza1), true); expect(handler.matches(stanza2), false); }); + test('combined matching', () { final handler = StanzaHandler( callback: (stanza, _) async => StanzaHandlerData( @@ -104,13 +115,32 @@ void main() { tagXmlns: 'owo', ); - expect(handler.matches(Stanza.iq()), false); - expect(handler.matches(Stanza.message()), false); - expect(handler.matches(Stanza.presence()), false); + expect(handler.matches(Stanza.iq(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.message(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.presence(xmlns: stanzaXmlns)), false); expect(handler.matches(stanza1), true); expect(handler.matches(stanza2), false); }); + test('Test matching stanzas with a different xmlns', () { + final handler = StanzaHandler( + callback: (stanza, _) async => StanzaHandlerData( + true, + false, + null, + stanza, + ), + xmlns: componentAcceptXmlns, + ); + + expect(handler.matches(Stanza.iq(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.message(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.presence(xmlns: stanzaXmlns)), false); + expect(handler.matches(Stanza.iq(xmlns: componentAcceptXmlns)), true); + expect(handler.matches(stanza1), false); + expect(handler.matches(stanza2), false); + }); + test('sorting', () { final handlerList = [ StanzaHandler( diff --git a/packages/moxxmpp/test/xeps/xep_0030_test.dart b/packages/moxxmpp/test/xeps/xep_0030_test.dart index 85e703c..57f80e2 100644 --- a/packages/moxxmpp/test/xeps/xep_0030_test.dart +++ b/packages/moxxmpp/test/xeps/xep_0030_test.dart @@ -56,7 +56,7 @@ void main() { 'polynomdivision@test.server/MU29eEZn', ignoreId: true, ), - StringExpectation( + StanzaExpectation( "chat", '', ), diff --git a/packages/moxxmpp/test/xeps/xep_0198_test.dart b/packages/moxxmpp/test/xeps/xep_0198_test.dart index fead91d..f8745c8 100644 --- a/packages/moxxmpp/test/xeps/xep_0198_test.dart +++ b/packages/moxxmpp/test/xeps/xep_0198_test.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:moxxmpp/moxxmpp.dart'; import 'package:test/test.dart'; import '../helpers/logging.dart'; @@ -88,6 +89,7 @@ void main() { final stanza = Stanza( to: 'some.user@server.example', tag: 'message', + xmlns: stanzaXmlns, ); test('Test stream with SM enablement', () async { @@ -388,10 +390,14 @@ void main() { "", '', ), - StringExpectation( + StanzaExpectation( "chat", '', ), + StringExpectation( + "", + "", + ), StanzaExpectation( "", "", @@ -425,7 +431,8 @@ void main() { waitUntilLogin: true, ); - expect(fakeSocket.getState(), 6); + await Future.delayed(const Duration(seconds: 3)); + expect(fakeSocket.getState(), 7); expect(await conn.getConnectionState(), XmppConnectionState.connected); expect( conn @@ -502,9 +509,6 @@ void main() { password: 'aaaa', ); await conn.registerManagers([ - PresenceManager(), - RosterManager(TestingRosterStateManager('', [])), - DiscoManager([]), StreamManagementManager(), ]); await conn.registerFeatureNegotiators([ @@ -523,7 +527,8 @@ void main() { await conn.connect( waitUntilLogin: true, ); - expect(fakeSocket.getState(), 7); + + expect(fakeSocket.getState(), 6); expect(await conn.getConnectionState(), XmppConnectionState.connected); expect( conn