Compare commits
	
		
			No commits in common. "7e588f01b0e226a0c6b03f66d5f2b1d055c8715f" and "1cc266c675e7b7ff221ceb6544aca711c00e4569" have entirely different histories.
		
	
	
		
			7e588f01b0
			...
			1cc266c675
		
	
		
| @ -1,7 +1,6 @@ | |||||||
| library moxxmpp; | library moxxmpp; | ||||||
| 
 | 
 | ||||||
| export 'package:moxxmpp/src/connection.dart'; | export 'package:moxxmpp/src/connection.dart'; | ||||||
| export 'package:moxxmpp/src/connectivity.dart'; |  | ||||||
| export 'package:moxxmpp/src/errors.dart'; | export 'package:moxxmpp/src/errors.dart'; | ||||||
| export 'package:moxxmpp/src/events.dart'; | export 'package:moxxmpp/src/events.dart'; | ||||||
| export 'package:moxxmpp/src/iq.dart'; | export 'package:moxxmpp/src/iq.dart'; | ||||||
|  | |||||||
| @ -4,7 +4,6 @@ import 'package:meta/meta.dart'; | |||||||
| import 'package:moxlib/moxlib.dart'; | import 'package:moxlib/moxlib.dart'; | ||||||
| import 'package:moxxmpp/src/awaiter.dart'; | import 'package:moxxmpp/src/awaiter.dart'; | ||||||
| import 'package:moxxmpp/src/buffer.dart'; | import 'package:moxxmpp/src/buffer.dart'; | ||||||
| import 'package:moxxmpp/src/connectivity.dart'; |  | ||||||
| import 'package:moxxmpp/src/errors.dart'; | import 'package:moxxmpp/src/errors.dart'; | ||||||
| import 'package:moxxmpp/src/events.dart'; | import 'package:moxxmpp/src/events.dart'; | ||||||
| import 'package:moxxmpp/src/iq.dart'; | import 'package:moxxmpp/src/iq.dart'; | ||||||
| @ -95,24 +94,22 @@ class XmppConnectionResult { | |||||||
| class XmppConnection { | class XmppConnection { | ||||||
|   XmppConnection( |   XmppConnection( | ||||||
|     ReconnectionPolicy reconnectionPolicy, |     ReconnectionPolicy reconnectionPolicy, | ||||||
|     ConnectivityManager connectivityManager, |  | ||||||
|     this._socket, |     this._socket, | ||||||
|     { |     { | ||||||
|       this.connectionPingDuration = const Duration(minutes: 3), |       this.connectionPingDuration = const Duration(minutes: 3), | ||||||
|       this.connectingTimeout = const Duration(minutes: 2), |       this.connectingTimeout = const Duration(minutes: 2), | ||||||
|     } |     } | ||||||
|   ) : _reconnectionPolicy = reconnectionPolicy, |   ) : _reconnectionPolicy = reconnectionPolicy { | ||||||
|       _connectivityManager = connectivityManager { |       // Allow the reconnection policy to perform reconnections by itself | ||||||
|     // Allow the reconnection policy to perform reconnections by itself |       _reconnectionPolicy.register( | ||||||
|     _reconnectionPolicy.register( |         _attemptReconnection, | ||||||
|       _attemptReconnection, |         _onNetworkConnectionLost, | ||||||
|       _onNetworkConnectionLost, |       ); | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     _socketStream = _socket.getDataStream(); |       _socketStream = _socket.getDataStream(); | ||||||
|     // TODO(Unknown): Handle on done |       // TODO(Unknown): Handle on done | ||||||
|     _socketStream.transform(_streamBuffer).forEach(handleXmlStream); |       _socketStream.transform(_streamBuffer).forEach(handleXmlStream); | ||||||
|     _socket.getEventStream().listen(_handleSocketEvent); |       _socket.getEventStream().listen(_handleSocketEvent); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -125,16 +122,13 @@ class XmppConnection { | |||||||
|   /// The data stream of the socket |   /// The data stream of the socket | ||||||
|   late final Stream<String> _socketStream; |   late final Stream<String> _socketStream; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|   /// Connection settings |   /// Connection settings | ||||||
|   late ConnectionSettings _connectionSettings; |   late ConnectionSettings _connectionSettings; | ||||||
| 
 | 
 | ||||||
|   /// A policy on how to reconnect  |   /// A policy on how to reconnect  | ||||||
|   final ReconnectionPolicy _reconnectionPolicy; |   final ReconnectionPolicy _reconnectionPolicy; | ||||||
| 
 | 
 | ||||||
|   /// The class responsible for preventing errors on initial connection due |  | ||||||
|   /// to no network. |  | ||||||
|   final ConnectivityManager _connectivityManager; |  | ||||||
| 
 |  | ||||||
|   /// A helper for handling await semantics with stanzas |   /// A helper for handling await semantics with stanzas | ||||||
|   final StanzaAwaiter _stanzaAwaiter = StanzaAwaiter(); |   final StanzaAwaiter _stanzaAwaiter = StanzaAwaiter(); | ||||||
|    |    | ||||||
| @ -223,47 +217,63 @@ class XmppConnection { | |||||||
|   /// none can be found. |   /// none can be found. | ||||||
|   T? getNegotiatorById<T extends XmppFeatureNegotiatorBase>(String id) => _featureNegotiators[id] as T?; |   T? getNegotiatorById<T extends XmppFeatureNegotiatorBase>(String id) => _featureNegotiators[id] as T?; | ||||||
|    |    | ||||||
|   /// Registers a list of [XmppManagerBase] sub-classes as managers on this connection. |   /// Registers an [XmppManagerBase] sub-class as a manager on this connection. | ||||||
|   Future<void> registerManagers(List<XmppManagerBase> managers) async { |   /// [sortHandlers] should NOT be touched. It specified if the handler priorities | ||||||
|  |   /// should be set up. The only time this should be false is when called via | ||||||
|  |   /// [registerManagers]. | ||||||
|  |   void registerManager(XmppManagerBase manager, { bool sortHandlers = true }) { | ||||||
|  |     _log.finest('Registering ${manager.getId()}'); | ||||||
|  |     manager.register( | ||||||
|  |       XmppManagerAttributes( | ||||||
|  |         sendStanza: sendStanza, | ||||||
|  |         sendNonza: sendRawXML, | ||||||
|  |         sendEvent: _sendEvent, | ||||||
|  |         getConnectionSettings: () => _connectionSettings, | ||||||
|  |         getManagerById: getManagerById, | ||||||
|  |         isFeatureSupported: _serverFeatures.contains, | ||||||
|  |         getFullJID: () => _connectionSettings.jid.withResource(_resource), | ||||||
|  |         getSocket: () => _socket, | ||||||
|  |         getConnection: () => this, | ||||||
|  |         getNegotiatorById: getNegotiatorById, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     final id = manager.getId(); | ||||||
|  |     _xmppManagers[id] = manager; | ||||||
|  | 
 | ||||||
|  |     if (id == discoManager) { | ||||||
|  |       // NOTE: It is intentional that we do not exclude the [DiscoManager] from this | ||||||
|  |       //       loop. It may also register features. | ||||||
|  |       for (final registeredManager in _xmppManagers.values) { | ||||||
|  |         (manager as DiscoManager).addDiscoFeatures(registeredManager.getDiscoFeatures()); | ||||||
|  |       } | ||||||
|  |     } else if (_xmppManagers.containsKey(discoManager)) { | ||||||
|  |       (_xmppManagers[discoManager]! as DiscoManager).addDiscoFeatures(manager.getDiscoFeatures()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     _incomingStanzaHandlers.addAll(manager.getIncomingStanzaHandlers()); | ||||||
|  |     _incomingPreStanzaHandlers.addAll(manager.getIncomingPreStanzaHandlers()); | ||||||
|  |     _outgoingPreStanzaHandlers.addAll(manager.getOutgoingPreStanzaHandlers()); | ||||||
|  |     _outgoingPostStanzaHandlers.addAll(manager.getOutgoingPostStanzaHandlers()); | ||||||
|  |      | ||||||
|  |     if (sortHandlers) { | ||||||
|  |       _incomingStanzaHandlers.sort(stanzaHandlerSortComparator); | ||||||
|  |       _incomingPreStanzaHandlers.sort(stanzaHandlerSortComparator); | ||||||
|  |       _outgoingPreStanzaHandlers.sort(stanzaHandlerSortComparator); | ||||||
|  |       _outgoingPostStanzaHandlers.sort(stanzaHandlerSortComparator); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Like [registerManager], but for a list of managers. | ||||||
|  |   void registerManagers(List<XmppManagerBase> managers) { | ||||||
|     for (final manager in managers) { |     for (final manager in managers) { | ||||||
|       _log.finest('Registering ${manager.getId()}'); |       registerManager(manager, sortHandlers: false); | ||||||
|       manager.register( |  | ||||||
|         XmppManagerAttributes( |  | ||||||
|           sendStanza: sendStanza, |  | ||||||
|           sendNonza: sendRawXML, |  | ||||||
|           sendEvent: _sendEvent, |  | ||||||
|           getConnectionSettings: () => _connectionSettings, |  | ||||||
|           getManagerById: getManagerById, |  | ||||||
|           isFeatureSupported: _serverFeatures.contains, |  | ||||||
|           getFullJID: () => _connectionSettings.jid.withResource(_resource), |  | ||||||
|           getSocket: () => _socket, |  | ||||||
|           getConnection: () => this, |  | ||||||
|           getNegotiatorById: getNegotiatorById, |  | ||||||
|         ), |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       final id = manager.getId(); |  | ||||||
|       _xmppManagers[id] = manager; |  | ||||||
| 
 |  | ||||||
|       _incomingStanzaHandlers.addAll(manager.getIncomingStanzaHandlers()); |  | ||||||
|       _incomingPreStanzaHandlers.addAll(manager.getIncomingPreStanzaHandlers()); |  | ||||||
|       _outgoingPreStanzaHandlers.addAll(manager.getOutgoingPreStanzaHandlers()); |  | ||||||
|       _outgoingPostStanzaHandlers.addAll(manager.getOutgoingPostStanzaHandlers()); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sort them |     // Sort them | ||||||
|     _incomingStanzaHandlers.sort(stanzaHandlerSortComparator); |     _incomingStanzaHandlers.sort(stanzaHandlerSortComparator); | ||||||
|     _incomingPreStanzaHandlers.sort(stanzaHandlerSortComparator); |  | ||||||
|     _outgoingPreStanzaHandlers.sort(stanzaHandlerSortComparator); |     _outgoingPreStanzaHandlers.sort(stanzaHandlerSortComparator); | ||||||
|     _outgoingPostStanzaHandlers.sort(stanzaHandlerSortComparator); |     _outgoingPostStanzaHandlers.sort(stanzaHandlerSortComparator); | ||||||
| 
 |  | ||||||
|     // Run the post register callbacks |  | ||||||
|     for (final manager in _xmppManagers.values) { |  | ||||||
|       if (!manager.initialized) { |  | ||||||
|         _log.finest('Running post-registration callback for ${manager.getName()}'); |  | ||||||
|         await manager.postRegisterCallback(); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   /// Register a list of negotiator with the connection. |   /// Register a list of negotiator with the connection. | ||||||
| @ -368,7 +378,7 @@ class XmppConnection { | |||||||
|     // Connect again |     // Connect again | ||||||
|     // ignore: cascade_invocations |     // ignore: cascade_invocations | ||||||
|     _log.finest('Calling connect() from _attemptReconnection'); |     _log.finest('Calling connect() from _attemptReconnection'); | ||||||
|     await connect(waitForConnection: true); |     await connect(); | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   /// Called when a stream ending error has occurred |   /// Called when a stream ending error has occurred | ||||||
| @ -391,11 +401,7 @@ class XmppConnection { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (await _connectivityManager.hasConnection()) { |     await _setConnectionState(XmppConnectionState.error); | ||||||
|       await _setConnectionState(XmppConnectionState.error); |  | ||||||
|     } else { |  | ||||||
|       await _setConnectionState(XmppConnectionState.notConnected); |  | ||||||
|     } |  | ||||||
|     await _reconnectionPolicy.onFailure(); |     await _reconnectionPolicy.onFailure(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -826,6 +832,7 @@ class XmppConnection { | |||||||
|         if (_isMandatoryNegotiationDone(_streamFeatures) && !_isNegotiationPossible(_streamFeatures)) { |         if (_isMandatoryNegotiationDone(_streamFeatures) && !_isNegotiationPossible(_streamFeatures)) { | ||||||
|           _log.finest('Negotiations done!'); |           _log.finest('Negotiations done!'); | ||||||
|           _updateRoutingState(RoutingState.handleStanzas); |           _updateRoutingState(RoutingState.handleStanzas); | ||||||
|  |           await _reconnectionPolicy.onSuccess(); | ||||||
|           await _resetIsConnectionRunning(); |           await _resetIsConnectionRunning(); | ||||||
|           await _onNegotiationsDone(); |           await _onNegotiationsDone(); | ||||||
|         } else { |         } else { | ||||||
| @ -850,6 +857,7 @@ class XmppConnection { | |||||||
|         _log.finest('Negotiations done!'); |         _log.finest('Negotiations done!'); | ||||||
| 
 | 
 | ||||||
|         _updateRoutingState(RoutingState.handleStanzas); |         _updateRoutingState(RoutingState.handleStanzas); | ||||||
|  |         await _reconnectionPolicy.onSuccess(); | ||||||
|         await _resetIsConnectionRunning(); |         await _resetIsConnectionRunning(); | ||||||
|         await _onNegotiationsDone(); |         await _onNegotiationsDone(); | ||||||
|       } else { |       } else { | ||||||
| @ -867,6 +875,7 @@ class XmppConnection { | |||||||
|       _log.finest('Negotiator wants to skip the remaining negotiation... Negotiations (assumed) done!'); |       _log.finest('Negotiator wants to skip the remaining negotiation... Negotiations (assumed) done!'); | ||||||
| 
 | 
 | ||||||
|       _updateRoutingState(RoutingState.handleStanzas); |       _updateRoutingState(RoutingState.handleStanzas); | ||||||
|  |       await _reconnectionPolicy.onSuccess(); | ||||||
|       await _resetIsConnectionRunning(); |       await _resetIsConnectionRunning(); | ||||||
|       await _onNegotiationsDone(); |       await _onNegotiationsDone(); | ||||||
|       break; |       break; | ||||||
| @ -978,7 +987,7 @@ class XmppConnection { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<void> _disconnect({required XmppConnectionState state, bool triggeredByUser = true}) async { |   Future<void> _disconnect({required XmppConnectionState state, bool triggeredByUser = true}) async { | ||||||
|     await _reconnectionPolicy.setShouldReconnect(false); |     _reconnectionPolicy.setShouldReconnect(false); | ||||||
| 
 | 
 | ||||||
|     if (triggeredByUser) { |     if (triggeredByUser) { | ||||||
|       getPresenceManager().sendUnavailablePresence(); |       getPresenceManager().sendUnavailablePresence(); | ||||||
| @ -1009,21 +1018,17 @@ class XmppConnection { | |||||||
|    |    | ||||||
|   /// Like [connect] but the Future resolves when the resource binding is either done or |   /// Like [connect] but the Future resolves when the resource binding is either done or | ||||||
|   /// SASL has failed. |   /// SASL has failed. | ||||||
|   Future<XmppConnectionResult> connectAwaitable({ String? lastResource, bool waitForConnection = false }) async { |   Future<XmppConnectionResult> connectAwaitable({ String? lastResource }) async { | ||||||
|     _runPreConnectionAssertions(); |     _runPreConnectionAssertions(); | ||||||
|     await _resetIsConnectionRunning(); |     await _resetIsConnectionRunning(); | ||||||
|     _connectionCompleter = Completer(); |     _connectionCompleter = Completer(); | ||||||
|     _log.finest('Calling connect() from connectAwaitable'); |     _log.finest('Calling connect() from connectAwaitable'); | ||||||
|     await connect( |     await connect(lastResource: lastResource); | ||||||
|       lastResource: lastResource, |  | ||||||
|       waitForConnection: waitForConnection, |  | ||||||
|       shouldReconnect: false, |  | ||||||
|     ); |  | ||||||
|     return _connectionCompleter!.future; |     return _connectionCompleter!.future; | ||||||
|   } |   } | ||||||
|   |   | ||||||
|   /// Start the connection process using the provided connection settings. |   /// Start the connection process using the provided connection settings. | ||||||
|   Future<void> connect({ String? lastResource, bool waitForConnection = false, bool shouldReconnect = true }) async { |   Future<void> connect({ String? lastResource }) async { | ||||||
|     if (_connectionState != XmppConnectionState.notConnected && _connectionState != XmppConnectionState.error) { |     if (_connectionState != XmppConnectionState.notConnected && _connectionState != XmppConnectionState.error) { | ||||||
|       _log.fine('Cancelling this connection attempt as one appears to be already running.'); |       _log.fine('Cancelling this connection attempt as one appears to be already running.'); | ||||||
|       return; |       return; | ||||||
| @ -1031,25 +1036,15 @@ class XmppConnection { | |||||||
|      |      | ||||||
|     _runPreConnectionAssertions(); |     _runPreConnectionAssertions(); | ||||||
|     await _resetIsConnectionRunning(); |     await _resetIsConnectionRunning(); | ||||||
|  |     _reconnectionPolicy.setShouldReconnect(true); | ||||||
|      |      | ||||||
|     if (lastResource != null) { |     if (lastResource != null) { | ||||||
|       setResource(lastResource); |       setResource(lastResource); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (shouldReconnect) { |  | ||||||
|       await _reconnectionPolicy.setShouldReconnect(true); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     await _reconnectionPolicy.reset(); |     await _reconnectionPolicy.reset(); | ||||||
|     await _sendEvent(ConnectingEvent()); |     await _sendEvent(ConnectingEvent()); | ||||||
| 
 | 
 | ||||||
|     // If requested, wait until we have a network connection |  | ||||||
|     if (waitForConnection) { |  | ||||||
|       _log.info('Waiting for okay from connectivityManager'); |  | ||||||
|       await _connectivityManager.waitForConnection(); |  | ||||||
|       _log.info('Got okay from connectivityManager'); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     final smManager = getStreamManagementManager(); |     final smManager = getStreamManagementManager(); | ||||||
|     String? host; |     String? host; | ||||||
|     int? port; |     int? port; | ||||||
|  | |||||||
| @ -1,18 +0,0 @@ | |||||||
| /// This manager class is responsible to tell the moxxmpp XmppConnection |  | ||||||
| /// when a connection can be established or not, regarding the network availability. |  | ||||||
| abstract class ConnectivityManager { |  | ||||||
|   /// Returns true if a network connection is available. If not, returns false. |  | ||||||
|   Future<bool> hasConnection(); |  | ||||||
| 
 |  | ||||||
|   /// Returns a future that resolves once we have a network connection. |  | ||||||
|   Future<void> waitForConnection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// An implementation of [ConnectivityManager] that is always connected. |  | ||||||
| class AlwaysConnectedConnectivityManager extends ConnectivityManager { |  | ||||||
|   @override |  | ||||||
|   Future<bool> hasConnection() async => true; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<void> waitForConnection() async {} |  | ||||||
| } |  | ||||||
| @ -1,21 +1,14 @@ | |||||||
| import 'package:logging/logging.dart'; | import 'package:logging/logging.dart'; | ||||||
| import 'package:meta/meta.dart'; |  | ||||||
| import 'package:moxxmpp/src/events.dart'; | import 'package:moxxmpp/src/events.dart'; | ||||||
| import 'package:moxxmpp/src/managers/attributes.dart'; | import 'package:moxxmpp/src/managers/attributes.dart'; | ||||||
| import 'package:moxxmpp/src/managers/data.dart'; | import 'package:moxxmpp/src/managers/data.dart'; | ||||||
| import 'package:moxxmpp/src/managers/handlers.dart'; | import 'package:moxxmpp/src/managers/handlers.dart'; | ||||||
| import 'package:moxxmpp/src/managers/namespaces.dart'; |  | ||||||
| import 'package:moxxmpp/src/stringxml.dart'; | import 'package:moxxmpp/src/stringxml.dart'; | ||||||
| import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; |  | ||||||
| import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart'; |  | ||||||
| 
 | 
 | ||||||
| abstract class XmppManagerBase { | abstract class XmppManagerBase { | ||||||
|   late final XmppManagerAttributes _managerAttributes; |   late final XmppManagerAttributes _managerAttributes; | ||||||
|   late final Logger _log; |   late final Logger _log; | ||||||
| 
 | 
 | ||||||
|   /// Flag indicating that the post registration callback has been called once. |  | ||||||
|   bool initialized = false; |  | ||||||
|    |  | ||||||
|   /// Registers the callbacks from XmppConnection with the manager |   /// Registers the callbacks from XmppConnection with the manager | ||||||
|   void register(XmppManagerAttributes attributes) { |   void register(XmppManagerAttributes attributes) { | ||||||
|     _managerAttributes = attributes; |     _managerAttributes = attributes; | ||||||
| @ -56,9 +49,6 @@ abstract class XmppManagerBase { | |||||||
|   /// Return a list of features that should be included in a disco response. |   /// Return a list of features that should be included in a disco response. | ||||||
|   List<String> getDiscoFeatures() => []; |   List<String> getDiscoFeatures() => []; | ||||||
|    |    | ||||||
|   /// Return a list of identities that should be included in a disco response. |  | ||||||
|   List<Identity> getDiscoIdentities() => []; |  | ||||||
|    |  | ||||||
|   /// Return the Id (akin to xmlns) of this manager. |   /// Return the Id (akin to xmlns) of this manager. | ||||||
|   String getId(); |   String getId(); | ||||||
| 
 | 
 | ||||||
| @ -74,24 +64,6 @@ abstract class XmppManagerBase { | |||||||
|   /// Returns true if the XEP is supported on the server. If not, returns false |   /// Returns true if the XEP is supported on the server. If not, returns false | ||||||
|   Future<bool> isSupported(); |   Future<bool> isSupported(); | ||||||
|    |    | ||||||
|   /// Called after the registration of all managers against the XmppConnection is done. |  | ||||||
|   /// This method is only called once during the entire lifetime of it. |  | ||||||
|   @mustCallSuper |  | ||||||
|   Future<void> postRegisterCallback() async { |  | ||||||
|     initialized = true; |  | ||||||
| 
 |  | ||||||
|     final disco = getAttributes().getManagerById<DiscoManager>(discoManager); |  | ||||||
|     if (disco != null) { |  | ||||||
|       if (getDiscoFeatures().isNotEmpty) { |  | ||||||
|         disco.addFeatures(getDiscoFeatures()); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (getDiscoIdentities().isNotEmpty) { |  | ||||||
|         disco.addIdentities(getDiscoIdentities()); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   /// Runs all NonzaHandlers of this Manager which match the nonza. Resolves to true if |   /// Runs all NonzaHandlers of this Manager which match the nonza. Resolves to true if | ||||||
|   /// the nonza has been handled by one of the handlers. Resolves to false otherwise. |   /// the nonza has been handled by one of the handlers. Resolves to false otherwise. | ||||||
|   Future<bool> runNonzaHandlers(XMLNode nonza) async { |   Future<bool> runNonzaHandlers(XMLNode nonza) async { | ||||||
|  | |||||||
| @ -1,25 +1,25 @@ | |||||||
| const smManager = 'org.moxxmpp.streammangementmanager'; | const smManager = 'im.moxxmpp.streammangementmanager'; | ||||||
| const discoManager = 'org.moxxmpp.discomanager'; | const discoManager = 'im.moxxmpp.discomanager'; | ||||||
| const messageManager = 'org.moxxmpp.messagemanager'; | const messageManager = 'im.moxxmpp.messagemanager'; | ||||||
| const rosterManager = 'org.moxxmpp.rostermanager'; | const rosterManager = 'im.moxxmpp.rostermanager'; | ||||||
| const presenceManager = 'org.moxxmpp.presencemanager'; | const presenceManager = 'im.moxxmpp.presencemanager'; | ||||||
| const csiManager = 'org.moxxmpp.csimanager'; | const csiManager = 'im.moxxmpp.csimanager'; | ||||||
| const carbonsManager = 'org.moxxmpp.carbonsmanager'; | const carbonsManager = 'im.moxxmpp.carbonsmanager'; | ||||||
| const vcardManager = 'org.moxxmpp.vcardmanager'; | const vcardManager = 'im.moxxmpp.vcardmanager'; | ||||||
| const pubsubManager = 'org.moxxmpp.pubsubmanager'; | const pubsubManager = 'im.moxxmpp.pubsubmanager'; | ||||||
| const userAvatarManager = 'org.moxxmpp.useravatarmanager'; | const userAvatarManager = 'im.moxxmpp.useravatarmanager'; | ||||||
| const stableIdManager = 'org.moxxmpp.stableidmanager'; | const stableIdManager = 'im.moxxmpp.stableidmanager'; | ||||||
| const simsManager = 'org.moxxmpp.simsmanager'; | const simsManager = 'im.moxxmpp.simsmanager'; | ||||||
| const messageDeliveryReceiptManager = 'org.moxxmpp.messagedeliveryreceiptmanager'; | const messageDeliveryReceiptManager = 'im.moxxmpp.messagedeliveryreceiptmanager'; | ||||||
| const chatMarkerManager = 'org.moxxmpp.chatmarkermanager'; | const chatMarkerManager = 'im.moxxmpp.chatmarkermanager'; | ||||||
| const oobManager = 'org.moxxmpp.oobmanager'; | const oobManager = 'im.moxxmpp.oobmanager'; | ||||||
| const sfsManager = 'org.moxxmpp.sfsmanager'; | const sfsManager = 'im.moxxmpp.sfsmanager'; | ||||||
| const messageRepliesManager = 'org.moxxmpp.messagerepliesmanager'; | const messageRepliesManager = 'im.moxxmpp.messagerepliesmanager'; | ||||||
| const blockingManager = 'org.moxxmpp.blockingmanager'; | const blockingManager = 'im.moxxmpp.blockingmanager'; | ||||||
| const httpFileUploadManager = 'org.moxxmpp.httpfileuploadmanager'; | const httpFileUploadManager = 'im.moxxmpp.httpfileuploadmanager'; | ||||||
| const chatStateManager = 'org.moxxmpp.chatstatemanager'; | const chatStateManager = 'im.moxxmpp.chatstatemanager'; | ||||||
| const pingManager = 'org.moxxmpp.ping'; | const pingManager = 'im.moxxmpp.ping'; | ||||||
| const fileUploadNotificationManager = 'org.moxxmpp.fileuploadnotificationmanager'; | const fileUploadNotificationManager = 'im.moxxmpp.fileuploadnotificationmanager'; | ||||||
| const omemoManager = 'org.moxxmpp.omemomanager'; | const omemoManager = 'org.moxxmpp.omemomanager'; | ||||||
| const emeManager = 'org.moxxmpp.ememanager'; | const emeManager = 'org.moxxmpp.ememanager'; | ||||||
| const cryptographicHashManager = 'org.moxxmpp.cryptographichashmanager'; | const cryptographicHashManager = 'org.moxxmpp.cryptographichashmanager'; | ||||||
| @ -28,4 +28,3 @@ const messageRetractionManager = 'org.moxxmpp.messageretractionmanager'; | |||||||
| const lastMessageCorrectionManager = 'org.moxxmpp.lastmessagecorrectionmanager'; | const lastMessageCorrectionManager = 'org.moxxmpp.lastmessagecorrectionmanager'; | ||||||
| const messageReactionsManager = 'org.moxxmpp.messagereactionsmanager'; | const messageReactionsManager = 'org.moxxmpp.messagereactionsmanager'; | ||||||
| const stickersManager = 'org.moxxmpp.stickersmanager'; | const stickersManager = 'org.moxxmpp.stickersmanager'; | ||||||
| const entityCapabilitiesManager = 'org.moxxmpp.entitycapabilities'; |  | ||||||
|  | |||||||
| @ -8,19 +8,17 @@ import 'package:moxxmpp/src/managers/namespaces.dart'; | |||||||
| import 'package:moxxmpp/src/namespaces.dart'; | import 'package:moxxmpp/src/namespaces.dart'; | ||||||
| import 'package:moxxmpp/src/stanza.dart'; | import 'package:moxxmpp/src/stanza.dart'; | ||||||
| import 'package:moxxmpp/src/stringxml.dart'; | import 'package:moxxmpp/src/stringxml.dart'; | ||||||
|  | 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_0115.dart'; | ||||||
|  | import 'package:moxxmpp/src/xeps/xep_0414.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 |  | ||||||
| /// presence. |  | ||||||
| typedef PresencePreSendCallback = Future<List<XMLNode>> Function(); |  | ||||||
| 
 |  | ||||||
| /// A mandatory manager that handles initial presence sending, sending of subscription |  | ||||||
| /// request management requests and triggers events for incoming presence stanzas. |  | ||||||
| class PresenceManager extends XmppManagerBase { | class PresenceManager extends XmppManagerBase { | ||||||
|   PresenceManager() : super(); |   PresenceManager(this._capHashNode) : _capabilityHash = null, super(); | ||||||
|  |   String? _capabilityHash; | ||||||
|  |   final String _capHashNode; | ||||||
| 
 | 
 | ||||||
|   /// The list of pre-send callbacks. |   String get capabilityHashNode => _capHashNode; | ||||||
|   final List<PresencePreSendCallback> _presenceCallbacks = List.empty(growable: true); |  | ||||||
|    |    | ||||||
|   @override |   @override | ||||||
|   String getId() => presenceManager; |   String getId() => presenceManager; | ||||||
| @ -42,11 +40,6 @@ class PresenceManager extends XmppManagerBase { | |||||||
|   @override |   @override | ||||||
|   Future<bool> isSupported() async => true; |   Future<bool> isSupported() async => true; | ||||||
|    |    | ||||||
|   /// Register the pre-send callback [callback]. |  | ||||||
|   void registerPreSendCallback(PresencePreSendCallback callback) { |  | ||||||
|     _presenceCallbacks.add(callback); |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   Future<StanzaHandlerData> _onPresence(Stanza presence, StanzaHandlerData state) async { |   Future<StanzaHandlerData> _onPresence(Stanza presence, StanzaHandlerData state) async { | ||||||
|     final attrs = getAttributes(); |     final attrs = getAttributes(); | ||||||
|     switch (presence.type) { |     switch (presence.type) { | ||||||
| @ -70,26 +63,43 @@ class PresenceManager extends XmppManagerBase { | |||||||
|     return state; |     return state; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Returns the capability hash. | ||||||
|  |   Future<String> getCapabilityHash() async { | ||||||
|  |     final manager = getAttributes().getManagerById(discoManager)! as DiscoManager; | ||||||
|  |     _capabilityHash ??= await calculateCapabilityHash( | ||||||
|  |       DiscoInfo( | ||||||
|  |         manager.getRegisteredDiscoFeatures(), | ||||||
|  |         manager.getIdentities(), | ||||||
|  |         [], | ||||||
|  |         getAttributes().getFullJID(), | ||||||
|  |       ), | ||||||
|  |       getHashByName('sha-1')!, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     return _capabilityHash!; | ||||||
|  |   } | ||||||
|  |    | ||||||
|   /// Sends the initial presence to enable receiving messages. |   /// Sends the initial presence to enable receiving messages. | ||||||
|   Future<void> sendInitialPresence() async { |   Future<void> sendInitialPresence() async { | ||||||
|     final children = List<XMLNode>.from([ |  | ||||||
|       XMLNode( |  | ||||||
|         tag: 'show', |  | ||||||
|         text: 'chat', |  | ||||||
|       ), |  | ||||||
|     ]); |  | ||||||
| 
 |  | ||||||
|     for (final callback in _presenceCallbacks) { |  | ||||||
|       children.addAll( |  | ||||||
|         await callback(), |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     final attrs = getAttributes(); |     final attrs = getAttributes(); | ||||||
|     attrs.sendNonza( |     attrs.sendNonza( | ||||||
|       Stanza.presence( |       Stanza.presence( | ||||||
|         from: attrs.getFullJID().toString(), |         from: attrs.getFullJID().toString(), | ||||||
|         children: children, |         children: [ | ||||||
|  |           XMLNode( | ||||||
|  |             tag: 'show', | ||||||
|  |             text: 'chat', | ||||||
|  |           ), | ||||||
|  |           XMLNode.xmlns( | ||||||
|  |             tag: 'c', | ||||||
|  |             xmlns: capsXmlns, | ||||||
|  |             attributes: { | ||||||
|  |               'hash': 'sha-1', | ||||||
|  |               'node': _capHashNode, | ||||||
|  |               'ver': await getCapabilityHash() | ||||||
|  |             }, | ||||||
|  |           ) | ||||||
|  |         ], | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ import 'dart:async'; | |||||||
| import 'dart:math'; | import 'dart:math'; | ||||||
| import 'package:logging/logging.dart'; | import 'package:logging/logging.dart'; | ||||||
| import 'package:meta/meta.dart'; | import 'package:meta/meta.dart'; | ||||||
| import 'package:moxxmpp/src/util/queue.dart'; |  | ||||||
| import 'package:synchronized/synchronized.dart'; | import 'package:synchronized/synchronized.dart'; | ||||||
| 
 | 
 | ||||||
| /// A callback function to be called when the connection to the server has been lost. | /// A callback function to be called when the connection to the server has been lost. | ||||||
| @ -25,16 +24,10 @@ abstract class ReconnectionPolicy { | |||||||
|   bool _shouldAttemptReconnection = false; |   bool _shouldAttemptReconnection = false; | ||||||
| 
 | 
 | ||||||
|   /// Indicate if a reconnection attempt is currently running. |   /// Indicate if a reconnection attempt is currently running. | ||||||
|   @protected |   bool _isReconnecting = false; | ||||||
|   bool isReconnecting = false; |  | ||||||
| 
 | 
 | ||||||
|   /// And the corresponding lock |   /// And the corresponding lock | ||||||
|   @protected |   final Lock _isReconnectingLock = Lock(); | ||||||
|   final Lock lock = Lock(); |  | ||||||
| 
 |  | ||||||
|   /// The lock for accessing [_shouldAttemptReconnection] |  | ||||||
|   @protected |  | ||||||
|   final Lock shouldReconnectLock = Lock(); |  | ||||||
|    |    | ||||||
|   /// Called by XmppConnection to register the policy. |   /// Called by XmppConnection to register the policy. | ||||||
|   void register(PerformReconnectFunction performReconnect, ConnectionLostCallback triggerConnectionLost) { |   void register(PerformReconnectFunction performReconnect, ConnectionLostCallback triggerConnectionLost) { | ||||||
| @ -55,121 +48,96 @@ abstract class ReconnectionPolicy { | |||||||
|   /// Caled by the XmppConnection when the reconnection was successful. |   /// Caled by the XmppConnection when the reconnection was successful. | ||||||
|   Future<void> onSuccess(); |   Future<void> onSuccess(); | ||||||
| 
 | 
 | ||||||
|   Future<bool> getShouldReconnect() async { |   bool get shouldReconnect => _shouldAttemptReconnection; | ||||||
|     return shouldReconnectLock.synchronized(() => _shouldAttemptReconnection); |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   /// Set whether a reconnection attempt should be made. |   /// Set whether a reconnection attempt should be made. | ||||||
|   Future<void> setShouldReconnect(bool value) async { |   void setShouldReconnect(bool value) { | ||||||
|     return shouldReconnectLock.synchronized(() => _shouldAttemptReconnection = value); |     _shouldAttemptReconnection = value; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Returns true if the manager is currently triggering a reconnection. If not, returns |   /// Returns true if the manager is currently triggering a reconnection. If not, returns | ||||||
|   /// false. |   /// false. | ||||||
|   Future<bool> isReconnectionRunning() async { |   Future<bool> isReconnectionRunning() async { | ||||||
|     return lock.synchronized(() => isReconnecting); |     return _isReconnectingLock.synchronized(() => _isReconnecting); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Set the isReconnecting state to [value]. |   /// Set the _isReconnecting state to [value]. | ||||||
|   @protected |   @protected | ||||||
|   Future<void> setIsReconnecting(bool value) async { |   Future<void> setIsReconnecting(bool value) async { | ||||||
|     await lock.synchronized(() async { |     await _isReconnectingLock.synchronized(() async { | ||||||
|       isReconnecting = value; |       _isReconnecting = value; | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @protected | ||||||
|  |   Future<bool> testAndSetIsReconnecting() async { | ||||||
|  |     return _isReconnectingLock.synchronized(() { | ||||||
|  |       if (_isReconnecting) { | ||||||
|  |         return false; | ||||||
|  |       } else { | ||||||
|  |         _isReconnecting = true; | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// A simple reconnection strategy: Make the reconnection delays exponentially longer | /// A simple reconnection strategy: Make the reconnection delays exponentially longer | ||||||
| /// for every failed attempt. | /// for every failed attempt. | ||||||
| /// NOTE: This ReconnectionPolicy may be broken | /// NOTE: This ReconnectionPolicy may be broken | ||||||
| class RandomBackoffReconnectionPolicy extends ReconnectionPolicy { | class ExponentialBackoffReconnectionPolicy extends ReconnectionPolicy { | ||||||
|   RandomBackoffReconnectionPolicy( |   ExponentialBackoffReconnectionPolicy(this._maxBackoffTime) : super(); | ||||||
|     this._minBackoffTime, |  | ||||||
|     this._maxBackoffTime, |  | ||||||
|   ) : assert(_minBackoffTime < _maxBackoffTime, '_minBackoffTime must be smaller than _maxBackoffTime'), |  | ||||||
|       super(); |  | ||||||
| 
 | 
 | ||||||
|   /// The maximum time in seconds that a backoff should be. |   /// The maximum time in seconds that a backoff step should be. | ||||||
|   final int _maxBackoffTime; |   final int _maxBackoffTime; | ||||||
| 
 | 
 | ||||||
|   /// The minimum time in seconds that a backoff should be. |   /// Amount of consecutive failed reconnections. | ||||||
|   final int _minBackoffTime; |   int _counter = 0; | ||||||
| 
 | 
 | ||||||
|   /// Backoff timer. |   /// Backoff timer. | ||||||
|   Timer? _timer; |   Timer? _timer; | ||||||
| 
 | 
 | ||||||
|   final Lock _timerLock = Lock(); |  | ||||||
| 
 |  | ||||||
|   /// Logger. |   /// Logger. | ||||||
|   final Logger _log = Logger('RandomBackoffReconnectionPolicy'); |   final Logger _log = Logger('ExponentialBackoffReconnectionPolicy'); | ||||||
| 
 |  | ||||||
|   /// Event queue |  | ||||||
|   final AsyncQueue _eventQueue = AsyncQueue(); |  | ||||||
| 
 | 
 | ||||||
|   /// Called when the backoff expired |   /// Called when the backoff expired | ||||||
|   Future<void> _onTimerElapsed() async { |   Future<void> _onTimerElapsed() async { | ||||||
|     _log.fine('Timer elapsed. Waiting for lock'); |     final isReconnecting = await isReconnectionRunning(); | ||||||
|     await lock.synchronized(() async { |     if (shouldReconnect) { | ||||||
|       _log.fine('Lock aquired'); |       if (!isReconnecting) { | ||||||
|       if (!(await getShouldReconnect())) { |         await setIsReconnecting(true); | ||||||
|         _log.fine('Backoff timer expired but getShouldReconnect() returned false'); |         await performReconnect!(); | ||||||
|         return; |       } else { | ||||||
|  |         // Should never happen. | ||||||
|  |         _log.fine('Backoff timer expired but reconnection is running, so doing nothing.'); | ||||||
|       } |       } | ||||||
| 
 |     } | ||||||
|       if (isReconnecting) { |  | ||||||
|         _log.fine('Backoff timer expired but a reconnection is running, so doing nothing.'); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       _log.fine('Triggering reconnect'); |  | ||||||
|       isReconnecting = true; |  | ||||||
|       await performReconnect!(); |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     await _timerLock.synchronized(() { |  | ||||||
|       _timer?.cancel(); |  | ||||||
|       _timer = null; |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Future<void> _reset() async { |  | ||||||
|     _log.finest('Resetting internal state'); |  | ||||||
| 
 |  | ||||||
|     await _timerLock.synchronized(() { |  | ||||||
|       _timer?.cancel(); |  | ||||||
|       _timer = null; |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     await setIsReconnecting(false); |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   @override |   @override | ||||||
|   Future<void> reset() async { |   Future<void> reset() async { | ||||||
|     // ignore: unnecessary_lambdas |     _log.finest('Resetting internal state'); | ||||||
|     await _eventQueue.addJob(() => _reset()); |     _counter = 0; | ||||||
|   } |     await setIsReconnecting(false); | ||||||
| 
 | 
 | ||||||
|   Future<void> _onFailure() async { |     if (_timer != null) { | ||||||
|     final shouldContinue = await _timerLock.synchronized(() { |       _timer!.cancel(); | ||||||
|       return _timer == null; |       _timer = null; | ||||||
|     }); |  | ||||||
|     if (!shouldContinue) { |  | ||||||
|       _log.finest('_onFailure: Not backing off since _timer is already running'); |  | ||||||
|       return; |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     final seconds = Random().nextInt(_maxBackoffTime - _minBackoffTime) + _minBackoffTime; |  | ||||||
|     _log.finest('Failure occured. Starting random backoff with ${seconds}s'); |  | ||||||
|     _timer?.cancel(); |  | ||||||
| 
 |  | ||||||
|     _timer = Timer(Duration(seconds: seconds), _onTimerElapsed); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<void> onFailure() async { |   Future<void> onFailure() async { | ||||||
|     // ignore: unnecessary_lambdas |     _log.finest('Failure occured. Starting exponential backoff'); | ||||||
|     await _eventQueue.addJob(() => _onFailure()); |     _counter++; | ||||||
|  | 
 | ||||||
|  |     if (_timer != null) { | ||||||
|  |       _timer!.cancel(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Wait at max 80 seconds. | ||||||
|  |     final seconds = min(min(pow(2, _counter).toInt(), 80), _maxBackoffTime); | ||||||
|  |     _timer = Timer(Duration(seconds: seconds), _onTimerElapsed); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|  | |||||||
| @ -1,56 +0,0 @@ | |||||||
| import 'dart:async'; |  | ||||||
| import 'dart:collection'; |  | ||||||
| import 'package:meta/meta.dart'; |  | ||||||
| import 'package:synchronized/synchronized.dart'; |  | ||||||
| 
 |  | ||||||
| /// A job to be submitted to an [AsyncQueue]. |  | ||||||
| typedef AsyncQueueJob = Future<void> Function(); |  | ||||||
| 
 |  | ||||||
| /// A (hopefully) async-safe queue that attempts to force |  | ||||||
| /// in-order execution of its jobs. |  | ||||||
| class AsyncQueue { |  | ||||||
|   /// The lock for accessing [AsyncQueue._lock] and [AsyncQueue._running]. |  | ||||||
|   final Lock _lock = Lock(); |  | ||||||
| 
 |  | ||||||
|   /// The actual job queue. |  | ||||||
|   final Queue<AsyncQueueJob> _queue = Queue<AsyncQueueJob>(); |  | ||||||
|    |  | ||||||
|   /// Indicates whether we are currently executing a job. |  | ||||||
|   bool _running = false; |  | ||||||
| 
 |  | ||||||
|   @visibleForTesting |  | ||||||
|   Queue<AsyncQueueJob> get queue => _queue; |  | ||||||
| 
 |  | ||||||
|   @visibleForTesting |  | ||||||
|   bool get isRunning => _running; |  | ||||||
|    |  | ||||||
|   /// Adds a job [job] to the queue. |  | ||||||
|   Future<void> addJob(AsyncQueueJob job) async { |  | ||||||
|     await _lock.synchronized(() { |  | ||||||
|       _queue.add(job); |  | ||||||
| 
 |  | ||||||
|       if (!_running && _queue.isNotEmpty) { |  | ||||||
|         _running = true; |  | ||||||
|         unawaited(_popJob()); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Future<void> clear() async { |  | ||||||
|     await _lock.synchronized(_queue.clear); |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   Future<void> _popJob() async { |  | ||||||
|     final job = _queue.removeFirst(); |  | ||||||
|     final future = job(); |  | ||||||
|     await future; |  | ||||||
| 
 |  | ||||||
|     await _lock.synchronized(() { |  | ||||||
|       if (_queue.isNotEmpty) { |  | ||||||
|         unawaited(_popJob()); |  | ||||||
|       } else { |  | ||||||
|         _running = false; |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -3,6 +3,7 @@ import 'package:moxxmpp/src/namespaces.dart'; | |||||||
| import 'package:moxxmpp/src/stringxml.dart'; | import 'package:moxxmpp/src/stringxml.dart'; | ||||||
| 
 | 
 | ||||||
| class DataFormOption { | class DataFormOption { | ||||||
|  | 
 | ||||||
|   const DataFormOption({ required this.value, this.label }); |   const DataFormOption({ required this.value, this.label }); | ||||||
|   final String? label; |   final String? label; | ||||||
|   final String value; |   final String value; | ||||||
| @ -22,6 +23,7 @@ class DataFormOption { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class DataFormField { | class DataFormField { | ||||||
|  | 
 | ||||||
|   const DataFormField({ |   const DataFormField({ | ||||||
|       required this.options, |       required this.options, | ||||||
|       required this.values, |       required this.values, | ||||||
| @ -58,6 +60,7 @@ class DataFormField { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class DataForm { | class DataForm { | ||||||
|  | 
 | ||||||
|   const DataForm({ |   const DataForm({ | ||||||
|       required this.type, |       required this.type, | ||||||
|       required this.instructions, |       required this.instructions, | ||||||
|  | |||||||
| @ -6,20 +6,20 @@ import 'package:moxxmpp/src/stringxml.dart'; | |||||||
| 
 | 
 | ||||||
| Stanza buildDiscoInfoQueryStanza(String entity, String? node) { | Stanza buildDiscoInfoQueryStanza(String entity, String? node) { | ||||||
|   return Stanza.iq(to: entity, type: 'get', children: [ |   return Stanza.iq(to: entity, type: 'get', children: [ | ||||||
|     XMLNode.xmlns( |       XMLNode.xmlns( | ||||||
|       tag: 'query', |         tag: 'query', | ||||||
|       xmlns: discoInfoXmlns, |         xmlns: discoInfoXmlns, | ||||||
|       attributes: node != null ? { 'node': node } : {}, |         attributes: node != null ? { 'node': node } : {}, | ||||||
|     ) |       ) | ||||||
|   ],); |   ],); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Stanza buildDiscoItemsQueryStanza(String entity, { String? node }) { | Stanza buildDiscoItemsQueryStanza(String entity, { String? node }) { | ||||||
|   return Stanza.iq(to: entity, type: 'get', children: [ |   return Stanza.iq(to: entity, type: 'get', children: [ | ||||||
|     XMLNode.xmlns( |       XMLNode.xmlns( | ||||||
|       tag: 'query', |         tag: 'query', | ||||||
|       xmlns: discoItemsXmlns, |         xmlns: discoItemsXmlns, | ||||||
|       attributes: node != null ? { 'node': node } : {}, |         attributes: node != null ? { 'node': node } : {}, | ||||||
|     ) |       ) | ||||||
|   ],); |   ],); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,10 +1,9 @@ | |||||||
| import 'package:meta/meta.dart'; |  | ||||||
| import 'package:moxxmpp/src/jid.dart'; | import 'package:moxxmpp/src/jid.dart'; | ||||||
| import 'package:moxxmpp/src/namespaces.dart'; |  | ||||||
| import 'package:moxxmpp/src/stringxml.dart'; | import 'package:moxxmpp/src/stringxml.dart'; | ||||||
| import 'package:moxxmpp/src/xeps/xep_0004.dart'; | import 'package:moxxmpp/src/xeps/xep_0004.dart'; | ||||||
| 
 | 
 | ||||||
| class Identity { | class Identity { | ||||||
|  | 
 | ||||||
|   const Identity({ required this.category, required this.type, this.name, this.lang }); |   const Identity({ required this.category, required this.type, this.name, this.lang }); | ||||||
|   final String category; |   final String category; | ||||||
|   final String type; |   final String type; | ||||||
| @ -24,96 +23,24 @@ class Identity { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @immutable |  | ||||||
| class DiscoInfo { | class DiscoInfo { | ||||||
|  | 
 | ||||||
|   const DiscoInfo( |   const DiscoInfo( | ||||||
|     this.features, |     this.features, | ||||||
|     this.identities, |     this.identities, | ||||||
|     this.extendedInfo, |     this.extendedInfo, | ||||||
|     this.node, |  | ||||||
|     this.jid, |     this.jid, | ||||||
|   ); |   ); | ||||||
| 
 |  | ||||||
|   factory DiscoInfo.fromQuery(XMLNode query, JID jid) { |  | ||||||
|     final features = List<String>.empty(growable: true); |  | ||||||
|     final identities = List<Identity>.empty(growable: true); |  | ||||||
|     final extendedInfo = List<DataForm>.empty(growable: true); |  | ||||||
| 
 |  | ||||||
|     for (final element in query.children) { |  | ||||||
|       if (element.tag == 'feature') { |  | ||||||
|         features.add(element.attributes['var']! as String); |  | ||||||
|       } else if (element.tag == 'identity') { |  | ||||||
|         identities.add( |  | ||||||
|           Identity( |  | ||||||
|             category: element.attributes['category']! as String, |  | ||||||
|             type: element.attributes['type']! as String, |  | ||||||
|             name: element.attributes['name'] as String?, |  | ||||||
|           ), |  | ||||||
|         ); |  | ||||||
|       } else if (element.tag == 'x' && element.attributes['xmlns'] == dataFormsXmlns) { |  | ||||||
|         extendedInfo.add( |  | ||||||
|           parseDataForm(element), |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return DiscoInfo( |  | ||||||
|       features, |  | ||||||
|       identities, |  | ||||||
|       extendedInfo, |  | ||||||
|       query.attributes['node'] as String?, |  | ||||||
|       jid, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   final List<String> features; |   final List<String> features; | ||||||
|   final List<Identity> identities; |   final List<Identity> identities; | ||||||
|   final List<DataForm> extendedInfo; |   final List<DataForm> extendedInfo; | ||||||
|   final String? node; |   final JID jid; | ||||||
|   final JID? jid; |  | ||||||
| 
 |  | ||||||
|   XMLNode toXml() { |  | ||||||
|     return XMLNode.xmlns( |  | ||||||
|       tag: 'query', |  | ||||||
|       xmlns: discoInfoXmlns, |  | ||||||
|       attributes: node != null ? |  | ||||||
|         <String, String>{ 'node': node!, } : |  | ||||||
|         <String, String>{}, |  | ||||||
|       children: [ |  | ||||||
|         ...identities.map((identity) => identity.toXMLNode()), |  | ||||||
|         ...features.map((feature) => XMLNode( |  | ||||||
|           tag: 'feature', |  | ||||||
|           attributes: { 'var': feature, }, |  | ||||||
|         ),), |  | ||||||
|          |  | ||||||
|         if (extendedInfo.isNotEmpty) |  | ||||||
|           ...extendedInfo.map((ei) => ei.toXml()), |  | ||||||
|       ], |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @immutable |  | ||||||
| class DiscoItem { | class DiscoItem { | ||||||
|  | 
 | ||||||
|   const DiscoItem({ required this.jid, this.node, this.name }); |   const DiscoItem({ required this.jid, this.node, this.name }); | ||||||
|   final String jid; |   final String jid; | ||||||
|   final String? node; |   final String? node; | ||||||
|   final String? name; |   final String? name; | ||||||
| 
 |  | ||||||
|   XMLNode toXml() { |  | ||||||
|     final attributes = { |  | ||||||
|       'jid': jid, |  | ||||||
|     }; |  | ||||||
|     if (node != null) { |  | ||||||
|       attributes['node'] = node!; |  | ||||||
|     } |  | ||||||
|     if (name != null) { |  | ||||||
|       attributes['name'] = name!; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return XMLNode( |  | ||||||
|       tag: 'node', |  | ||||||
|       attributes: attributes, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,21 +7,17 @@ import 'package:moxxmpp/src/managers/data.dart'; | |||||||
| import 'package:moxxmpp/src/managers/handlers.dart'; | import 'package:moxxmpp/src/managers/handlers.dart'; | ||||||
| import 'package:moxxmpp/src/managers/namespaces.dart'; | import 'package:moxxmpp/src/managers/namespaces.dart'; | ||||||
| import 'package:moxxmpp/src/namespaces.dart'; | import 'package:moxxmpp/src/namespaces.dart'; | ||||||
|  | import 'package:moxxmpp/src/presence.dart'; | ||||||
| import 'package:moxxmpp/src/stanza.dart'; | import 'package:moxxmpp/src/stanza.dart'; | ||||||
| import 'package:moxxmpp/src/stringxml.dart'; | import 'package:moxxmpp/src/stringxml.dart'; | ||||||
| import 'package:moxxmpp/src/types/result.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/errors.dart'; | ||||||
| import 'package:moxxmpp/src/xeps/xep_0030/helpers.dart'; | import 'package:moxxmpp/src/xeps/xep_0030/helpers.dart'; | ||||||
| import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; | import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; | ||||||
| import 'package:moxxmpp/src/xeps/xep_0115.dart'; | import 'package:moxxmpp/src/xeps/xep_0115.dart'; | ||||||
| import 'package:synchronized/synchronized.dart'; | import 'package:synchronized/synchronized.dart'; | ||||||
| 
 | 
 | ||||||
| /// Callback that is called when a disco#info requests is received on a given node. |  | ||||||
| typedef DiscoInfoRequestCallback = Future<DiscoInfo> Function(); |  | ||||||
| 
 |  | ||||||
| /// Callback that is called when a disco#items requests is received on a given node. |  | ||||||
| typedef DiscoItemsRequestCallback = Future<List<DiscoItem>> Function(); |  | ||||||
| 
 |  | ||||||
| @immutable | @immutable | ||||||
| class DiscoCacheKey { | class DiscoCacheKey { | ||||||
|   const DiscoCacheKey(this.jid, this.node); |   const DiscoCacheKey(this.jid, this.node); | ||||||
| @ -37,48 +33,32 @@ class DiscoCacheKey { | |||||||
|   int get hashCode => jid.hashCode ^ node.hashCode; |   int get hashCode => jid.hashCode ^ node.hashCode; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// This manager implements XEP-0030 by providing a way of performing disco#info and |  | ||||||
| /// disco#items requests and answering those requests. |  | ||||||
| /// A caching mechanism is also provided. |  | ||||||
| class DiscoManager extends XmppManagerBase { | class DiscoManager extends XmppManagerBase { | ||||||
|   /// [identities] is a list of disco identities that should be added by default |   DiscoManager() | ||||||
|   /// to a disco#info response. |     : _features = List.empty(growable: true), | ||||||
|   DiscoManager(List<Identity> identities) |       _capHashCache = {}, | ||||||
|     : _identities = List<Identity>.from(identities), |       _capHashInfoCache = {}, | ||||||
|  |       _discoInfoCache = {}, | ||||||
|  |       _runningInfoQueries = {}, | ||||||
|  |       _cacheLock = Lock(), | ||||||
|       super(); |       super(); | ||||||
| 
 |  | ||||||
|   /// Our features |   /// Our features | ||||||
|   final List<String> _features = List.empty(growable: true); |   final List<String> _features; | ||||||
| 
 |  | ||||||
|   /// Disco identities that we advertise |  | ||||||
|   final List<Identity> _identities; |  | ||||||
| 
 | 
 | ||||||
|   /// Map full JID to Capability hashes |   /// Map full JID to Capability hashes | ||||||
|   final Map<String, CapabilityHashInfo> _capHashCache = {}; |   final Map<String, CapabilityHashInfo> _capHashCache; | ||||||
| 
 | 
 | ||||||
|   /// Map capability hash to the disco info |   /// Map capability hash to the disco info | ||||||
|   final Map<String, DiscoInfo> _capHashInfoCache = {}; |   final Map<String, DiscoInfo> _capHashInfoCache; | ||||||
| 
 | 
 | ||||||
|   /// Map full JID to Disco Info |   /// Map full JID to Disco Info | ||||||
|   final Map<DiscoCacheKey, DiscoInfo> _discoInfoCache = {}; |   final Map<DiscoCacheKey, DiscoInfo> _discoInfoCache; | ||||||
| 
 | 
 | ||||||
|   /// Mapping the full JID to a list of running requests |   /// Mapping the full JID to a list of running requests | ||||||
|   final Map<DiscoCacheKey, List<Completer<Result<DiscoError, DiscoInfo>>>> _runningInfoQueries = {}; |   final Map<DiscoCacheKey, List<Completer<Result<DiscoError, DiscoInfo>>>> _runningInfoQueries; | ||||||
| 
 | 
 | ||||||
|   /// Cache lock |   /// Cache lock | ||||||
|   final Lock _cacheLock = Lock(); |   final Lock _cacheLock; | ||||||
| 
 |  | ||||||
|   /// disco#info callbacks: node -> Callback |  | ||||||
|   final Map<String, DiscoInfoRequestCallback> _discoInfoCallbacks = {}; |  | ||||||
| 
 |  | ||||||
|   /// disco#items callbacks: node -> Callback |  | ||||||
|   final Map<String, DiscoItemsRequestCallback> _discoItemsCallbacks = {}; |  | ||||||
| 
 |  | ||||||
|   /// The list of identities that are registered. |  | ||||||
|   List<Identity> get identities => _identities; |  | ||||||
| 
 |  | ||||||
|   /// The list of disco features that are registered. |  | ||||||
|   List<String> get features => _features; |  | ||||||
| 
 | 
 | ||||||
|   @visibleForTesting |   @visibleForTesting | ||||||
|   bool hasInfoQueriesRunning() => _runningInfoQueries.isNotEmpty; |   bool hasInfoQueriesRunning() => _runningInfoQueries.isNotEmpty; | ||||||
| @ -126,19 +106,9 @@ class DiscoManager extends XmppManagerBase { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   /// Register a callback [callback] for a disco#info query on [node]. |  | ||||||
|   void registerInfoCallback(String node, DiscoInfoRequestCallback callback) { |  | ||||||
|     _discoInfoCallbacks[node] = callback; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /// Register a callback [callback] for a disco#items query on [node]. |  | ||||||
|   void registerItemsCallback(String node, DiscoItemsRequestCallback callback) { |  | ||||||
|     _discoItemsCallbacks[node] = callback; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   /// Adds a list of features to the possible disco info response. |   /// Adds a list of features to the possible disco info response. | ||||||
|   /// This function only adds features that are not already present in the disco features. |   /// This function only adds features that are not already present in the disco features. | ||||||
|   void addFeatures(List<String> features) { |   void addDiscoFeatures(List<String> features) { | ||||||
|     for (final feat in features) { |     for (final feat in features) { | ||||||
|       if (!_features.contains(feat)) { |       if (!_features.contains(feat)) { | ||||||
|         _features.add(feat); |         _features.add(feat); | ||||||
| @ -146,16 +116,6 @@ class DiscoManager extends XmppManagerBase { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Adds a list of identities to the possible disco info response. |  | ||||||
|   /// This function only adds features that are not already present in the disco features. |  | ||||||
|   void addIdentities(List<Identity> identities) { |  | ||||||
|     for (final identity in identities) { |  | ||||||
|       if (!_identities.contains(identity)) { |  | ||||||
|         _identities.add(identity); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   Future<void> _onPresence(JID from, Stanza presence) async { |   Future<void> _onPresence(JID from, Stanza presence) async { | ||||||
|     final c = presence.firstTag('c', xmlns: capsXmlns); |     final c = presence.firstTag('c', xmlns: capsXmlns); | ||||||
|     if (c == null) return; |     if (c == null) return; | ||||||
| @ -186,33 +146,45 @@ class DiscoManager extends XmppManagerBase { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   /// Returns the [DiscoInfo] object that would be used as the response to a disco#info |   /// Returns the list of disco features registered. | ||||||
|   /// query against our bare JID with no node. The results node attribute is set |   List<String> getRegisteredDiscoFeatures() => _features; | ||||||
|   /// to [node]. |    | ||||||
|   DiscoInfo getDiscoInfo(String? node) { |   /// May be overriden. Specifies the identities which will be returned in a disco info response. | ||||||
|     return DiscoInfo( |   List<Identity> getIdentities() => const [ Identity(category: 'client', type: 'pc', name: 'moxxmpp', lang: 'en') ]; | ||||||
|       _features, |  | ||||||
|       _identities, |  | ||||||
|       const [], |  | ||||||
|       node, |  | ||||||
|       null, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|    |    | ||||||
|   Future<StanzaHandlerData> _onDiscoInfoRequest(Stanza stanza, StanzaHandlerData state) async { |   Future<StanzaHandlerData> _onDiscoInfoRequest(Stanza stanza, StanzaHandlerData state) async { | ||||||
|     if (stanza.type != 'get') return state; |     if (stanza.type != 'get') return state; | ||||||
| 
 | 
 | ||||||
|  |     final presence = getAttributes().getManagerById(presenceManager)! as PresenceManager; | ||||||
|     final query = stanza.firstTag('query', xmlns: discoInfoXmlns)!; |     final query = stanza.firstTag('query', xmlns: discoInfoXmlns)!; | ||||||
|     final node = query.attributes['node'] as String?; |     final node = query.attributes['node'] as String?; | ||||||
|  |     final capHash = await presence.getCapabilityHash(); | ||||||
|  |     final isCapabilityNode = node == '${presence.capabilityHashNode}#$capHash'; | ||||||
| 
 | 
 | ||||||
|     if (_discoInfoCallbacks.containsKey(node)) { |     if (!isCapabilityNode && node != null) { | ||||||
|       // We can now assume that node != null |  | ||||||
|       final result = await _discoInfoCallbacks[node]!(); |  | ||||||
|       await reply( |       await reply( | ||||||
|         state, |         state, | ||||||
|         'result', |         'error', | ||||||
|         [ |         [ | ||||||
|           result.toXml(), |           XMLNode.xmlns( | ||||||
|  |             tag: 'query', | ||||||
|  |             xmlns: discoInfoXmlns, | ||||||
|  |             attributes: <String, String>{ | ||||||
|  |               'node': node | ||||||
|  |             }, | ||||||
|  |           ), | ||||||
|  |           XMLNode( | ||||||
|  |             tag: 'error', | ||||||
|  |             attributes: <String, String>{ | ||||||
|  |               'type': 'cancel' | ||||||
|  |             }, | ||||||
|  |             children: [ | ||||||
|  |               XMLNode.xmlns( | ||||||
|  |                 tag: 'not-allowed', | ||||||
|  |                 xmlns: fullStanzaXmlns, | ||||||
|  |               ) | ||||||
|  |             ], | ||||||
|  |           ), | ||||||
|         ], |         ], | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
| @ -223,7 +195,24 @@ class DiscoManager extends XmppManagerBase { | |||||||
|       state, |       state, | ||||||
|       'result', |       'result', | ||||||
|       [ |       [ | ||||||
|         getDiscoInfo(node).toXml(), |         XMLNode.xmlns( | ||||||
|  |           tag: 'query', | ||||||
|  |           xmlns: discoInfoXmlns, | ||||||
|  |           attributes: { | ||||||
|  |             ...!isCapabilityNode ? {} : { | ||||||
|  |               'node': '${presence.capabilityHashNode}#$capHash' | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           children: [ | ||||||
|  |             ...getIdentities().map((identity) => identity.toXMLNode()), | ||||||
|  |             ..._features.map((feat) { | ||||||
|  |               return XMLNode( | ||||||
|  |                 tag: 'feature', | ||||||
|  |                 attributes: <String, dynamic>{ 'var': feat }, | ||||||
|  |               ); | ||||||
|  |             }), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|       ], |       ], | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -234,20 +223,30 @@ class DiscoManager extends XmppManagerBase { | |||||||
|     if (stanza.type != 'get') return state; |     if (stanza.type != 'get') return state; | ||||||
| 
 | 
 | ||||||
|     final query = stanza.firstTag('query', xmlns: discoItemsXmlns)!; |     final query = stanza.firstTag('query', xmlns: discoItemsXmlns)!; | ||||||
|     final node = query.attributes['node'] as String?; |     if (query.attributes['node'] != null) { | ||||||
|     if (_discoItemsCallbacks.containsKey(node)) { |       // TODO(Unknown): Handle the node we specified for XEP-0115 | ||||||
|       final result = await _discoItemsCallbacks[node]!(); |  | ||||||
|       await reply( |       await reply( | ||||||
|         state, |         state, | ||||||
|         'result', |         'error', | ||||||
|         [ |         [ | ||||||
|           XMLNode.xmlns( |           XMLNode.xmlns( | ||||||
|             tag: 'query', |             tag: 'query', | ||||||
|             xmlns: discoItemsXmlns, |             xmlns: discoItemsXmlns, | ||||||
|             attributes: <String, String>{ |             attributes: <String, String>{ | ||||||
|               'node': node!, |               'node': query.attributes['node']! as String, | ||||||
|             }, |             }, | ||||||
|             children: result.map((item) => item.toXml()).toList(), |           ), | ||||||
|  |           XMLNode( | ||||||
|  |             tag: 'error', | ||||||
|  |             attributes: <String, dynamic>{ | ||||||
|  |               'type': 'cancel' | ||||||
|  |             }, | ||||||
|  |             children: [ | ||||||
|  |               XMLNode.xmlns( | ||||||
|  |                 tag: 'not-allowed', | ||||||
|  |                 xmlns: fullStanzaXmlns, | ||||||
|  |               ), | ||||||
|  |             ], | ||||||
|           ), |           ), | ||||||
|         ], |         ], | ||||||
|       ); |       ); | ||||||
| @ -255,7 +254,18 @@ class DiscoManager extends XmppManagerBase { | |||||||
|       return state.copyWith(done: true); |       return state.copyWith(done: true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return state; |     await reply( | ||||||
|  |       state, | ||||||
|  |       'result', | ||||||
|  |       [ | ||||||
|  |         XMLNode.xmlns( | ||||||
|  |           tag: 'query', | ||||||
|  |           xmlns: discoItemsXmlns, | ||||||
|  |         ), | ||||||
|  |       ], | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     return state.copyWith(done: true); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<void> _exitDiscoInfoCriticalSection(DiscoCacheKey key, Result<DiscoError, DiscoInfo> result) async { |   Future<void> _exitDiscoInfoCriticalSection(DiscoCacheKey key, Result<DiscoError, DiscoInfo> result) async { | ||||||
| @ -312,17 +322,34 @@ class DiscoManager extends XmppManagerBase { | |||||||
|       return result; |       return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (stanza.attributes['type'] == 'error') { |     final error = stanza.firstTag('error'); | ||||||
|       //final error = stanza.firstTag('error'); |     if (error != null && stanza.attributes['type'] == 'error') { | ||||||
|       final result = Result<DiscoError, DiscoInfo>(ErrorResponseDiscoError()); |       final result = Result<DiscoError, DiscoInfo>(ErrorResponseDiscoError()); | ||||||
|       await _exitDiscoInfoCriticalSection(cacheKey, result); |       await _exitDiscoInfoCriticalSection(cacheKey, result); | ||||||
|       return result; |       return result; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     final features = List<String>.empty(growable: true); | ||||||
|  |     final identities = List<Identity>.empty(growable: true); | ||||||
|  | 
 | ||||||
|  |     for (final element in query.children) { | ||||||
|  |       if (element.tag == 'feature') { | ||||||
|  |         features.add(element.attributes['var']! as String); | ||||||
|  |       } else if (element.tag == 'identity') { | ||||||
|  |         identities.add(Identity( | ||||||
|  |           category: element.attributes['category']! as String, | ||||||
|  |           type: element.attributes['type']! as String, | ||||||
|  |           name: element.attributes['name'] as String?, | ||||||
|  |         ),); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     final result = Result<DiscoError, DiscoInfo>( |     final result = Result<DiscoError, DiscoInfo>( | ||||||
|       DiscoInfo.fromQuery( |       DiscoInfo( | ||||||
|         query, |         features, | ||||||
|         JID.fromString(entity), |         identities, | ||||||
|  |         query.findTags('x', xmlns: dataFormsXmlns).map(parseDataForm).toList(), | ||||||
|  |         JID.fromString(stanza.attributes['from']! as String), | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|     await _exitDiscoInfoCriticalSection(cacheKey, result); |     await _exitDiscoInfoCriticalSection(cacheKey, result); | ||||||
| @ -340,8 +367,8 @@ class DiscoManager extends XmppManagerBase { | |||||||
|     final query = stanza.firstTag('query'); |     final query = stanza.firstTag('query'); | ||||||
|     if (query == null) return Result(InvalidResponseDiscoError()); |     if (query == null) return Result(InvalidResponseDiscoError()); | ||||||
| 
 | 
 | ||||||
|     if (stanza.type == 'error') { |     final error = stanza.firstTag('error'); | ||||||
|       //final error = stanza.firstTag('error'); |     if (error != null && stanza.type == 'error') { | ||||||
|       //print("Disco Items error: " + error.toXml()); |       //print("Disco Items error: " + error.toXml()); | ||||||
|       return Result(ErrorResponseDiscoError()); |       return Result(ErrorResponseDiscoError()); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,18 +1,10 @@ | |||||||
| import 'dart:convert'; | import 'dart:convert'; | ||||||
| import 'package:cryptography/cryptography.dart'; | import 'package:cryptography/cryptography.dart'; | ||||||
| import 'package:meta/meta.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/presence.dart'; |  | ||||||
| import 'package:moxxmpp/src/rfcs/rfc_4790.dart'; | import 'package:moxxmpp/src/rfcs/rfc_4790.dart'; | ||||||
| import 'package:moxxmpp/src/stringxml.dart'; |  | ||||||
| import 'package:moxxmpp/src/xeps/xep_0030/types.dart'; | 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_0414.dart'; |  | ||||||
| 
 | 
 | ||||||
| @immutable |  | ||||||
| class CapabilityHashInfo { | class CapabilityHashInfo { | ||||||
|  | 
 | ||||||
|   const CapabilityHashInfo(this.ver, this.node, this.hash); |   const CapabilityHashInfo(this.ver, this.node, this.hash); | ||||||
|   final String ver; |   final String ver; | ||||||
|   final String node; |   final String node; | ||||||
| @ -65,86 +57,3 @@ Future<String> calculateCapabilityHash(DiscoInfo info, HashAlgorithm algorithm) | |||||||
|    |    | ||||||
|   return base64.encode((await algorithm.hash(utf8.encode(buffer.toString()))).bytes); |   return base64.encode((await algorithm.hash(utf8.encode(buffer.toString()))).bytes); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| /// A manager implementing the advertising of XEP-0115. It responds to the |  | ||||||
| /// disco#info requests on the specified node with the information provided by |  | ||||||
| /// the DiscoManager. |  | ||||||
| /// NOTE: This manager requires that the DiscoManager is also registered. |  | ||||||
| class EntityCapabilitiesManager extends XmppManagerBase { |  | ||||||
|   EntityCapabilitiesManager(this._capabilityHashBase) : super(); |  | ||||||
| 
 |  | ||||||
|   /// The string that is both the node under which we advertise the disco info |  | ||||||
|   /// and the base for the actual node on which we respond to disco#info requests. |  | ||||||
|   final String _capabilityHashBase; |  | ||||||
| 
 |  | ||||||
|   /// The cached capability hash. |  | ||||||
|   String? _capabilityHash; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   String getName() => 'EntityCapabilitiesManager'; |  | ||||||
|    |  | ||||||
|   @override |  | ||||||
|   String getId() => entityCapabilitiesManager; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Future<bool> isSupported() async => true; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   List<String> getDiscoFeatures() => [ |  | ||||||
|     capsXmlns, |  | ||||||
|   ]; |  | ||||||
| 
 |  | ||||||
|   /// Computes, if required, the capability hash of the data provided by |  | ||||||
|   /// the DiscoManager. |  | ||||||
|   Future<String> getCapabilityHash() async { |  | ||||||
|     _capabilityHash ??= await calculateCapabilityHash( |  | ||||||
|       getAttributes() |  | ||||||
|         .getManagerById<DiscoManager>(discoManager)! |  | ||||||
|         .getDiscoInfo(null), |  | ||||||
|       getHashByName('sha-1')!, |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     return _capabilityHash!; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Future<String> _getNode() async { |  | ||||||
|     final hash = await getCapabilityHash(); |  | ||||||
|     return '$_capabilityHashBase#$hash'; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   Future<DiscoInfo> _onInfoQuery() async { |  | ||||||
|     return getAttributes() |  | ||||||
|       .getManagerById<DiscoManager>(discoManager)! |  | ||||||
|       .getDiscoInfo(await _getNode()); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Future<List<XMLNode>> _prePresenceSent() async { |  | ||||||
|     return [ |  | ||||||
|       XMLNode.xmlns( |  | ||||||
|         tag: 'c', |  | ||||||
|         xmlns: capsXmlns, |  | ||||||
|         attributes: { |  | ||||||
|           'hash': 'sha-1', |  | ||||||
|           'node': _capabilityHashBase, |  | ||||||
|           'ver': await getCapabilityHash(), |  | ||||||
|         }, |  | ||||||
|       ), |  | ||||||
|     ]; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   @override |  | ||||||
|   Future<void> postRegisterCallback() async { |  | ||||||
|     await super.postRegisterCallback(); |  | ||||||
|      |  | ||||||
|     getAttributes().getManagerById<DiscoManager>(discoManager)!.registerInfoCallback( |  | ||||||
|         await _getNode(), |  | ||||||
|         _onInfoQuery, |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|     getAttributes() |  | ||||||
|       .getManagerById<PresenceManager>(presenceManager)! |  | ||||||
|       .registerPreSendCallback( |  | ||||||
|         _prePresenceSent, |  | ||||||
|       ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -9,14 +9,14 @@ import 'package:moxxmpp/src/stanza.dart'; | |||||||
| /// Data summarizing the XEP-0461 data. | /// Data summarizing the XEP-0461 data. | ||||||
| class ReplyData { | class ReplyData { | ||||||
|   const ReplyData({ |   const ReplyData({ | ||||||
|  |     required this.to, | ||||||
|     required this.id, |     required this.id, | ||||||
|     this.to, |  | ||||||
|     this.start, |     this.start, | ||||||
|     this.end, |     this.end, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   /// The bare JID to whom the reply applies to |   /// The bare JID to whom the reply applies to | ||||||
|   final String? to; |   final String to; | ||||||
| 
 | 
 | ||||||
|   /// The stanza ID of the message that is replied to |   /// The stanza ID of the message that is replied to | ||||||
|   final String id; |   final String id; | ||||||
| @ -72,11 +72,6 @@ class MessageRepliesManager extends XmppManagerBase { | |||||||
|   @override |   @override | ||||||
|   String getId() => messageRepliesManager; |   String getId() => messageRepliesManager; | ||||||
| 
 | 
 | ||||||
|   @override |  | ||||||
|   List<String> getDiscoFeatures() => [ |  | ||||||
|     replyXmlns, |  | ||||||
|   ]; |  | ||||||
|    |  | ||||||
|   @override |   @override | ||||||
|   List<StanzaHandler> getIncomingStanzaHandlers() => [ |   List<StanzaHandler> getIncomingStanzaHandlers() => [ | ||||||
|     StanzaHandler( |     StanzaHandler( | ||||||
| @ -95,7 +90,7 @@ class MessageRepliesManager extends XmppManagerBase { | |||||||
|   Future<StanzaHandlerData> _onMessage(Stanza stanza, StanzaHandlerData state) async { |   Future<StanzaHandlerData> _onMessage(Stanza stanza, StanzaHandlerData state) async { | ||||||
|     final reply = stanza.firstTag('reply', xmlns: replyXmlns)!; |     final reply = stanza.firstTag('reply', xmlns: replyXmlns)!; | ||||||
|     final id = reply.attributes['id']! as String; |     final id = reply.attributes['id']! as String; | ||||||
|     final to = reply.attributes['to'] as String?; |     final to = reply.attributes['to']! as String; | ||||||
|     int? start; |     int? start; | ||||||
|     int? end; |     int? end; | ||||||
| 
 | 
 | ||||||
| @ -107,13 +102,11 @@ class MessageRepliesManager extends XmppManagerBase { | |||||||
|       end = int.parse(body.attributes['end']! as String); |       end = int.parse(body.attributes['end']! as String); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return state.copyWith( |     return state.copyWith(reply: ReplyData( | ||||||
|       reply: ReplyData( |  | ||||||
|         id: id, |         id: id, | ||||||
|         to: to, |         to: to, | ||||||
|         start: start, |         start: start, | ||||||
|         end: end, |         end: end, | ||||||
|       ), |     ),); | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,276 +0,0 @@ | |||||||
| <?xml version='1.0' encoding='UTF-8'?> |  | ||||||
| <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' |  | ||||||
|          xmlns='http://usefulinc.com/ns/doap#' |  | ||||||
| 	 xmlns:foaf='http://xmlns.com/foaf/0.1/' |  | ||||||
| 	 xmlns:xmpp='https://linkmauve.fr/ns/xmpp-doap#'> |  | ||||||
|   <Project xml:lang='en'> |  | ||||||
|     <name>moxxmpp</name> |  | ||||||
|     <created>2021-12-26</created> |  | ||||||
|     <homepage rdf:resource='https://codeberg.org/moxxy/moxxmpp'/> |  | ||||||
|     <os>Linux</os> |  | ||||||
|     <os>Windows</os> |  | ||||||
|     <os>macOS</os> |  | ||||||
|     <os>Android</os> |  | ||||||
|     <os>iOS</os> |  | ||||||
| 
 |  | ||||||
|     <programming-language>Dart</programming-language> |  | ||||||
| 
 |  | ||||||
|     <maintainer> |  | ||||||
|       <foaf:Person> |  | ||||||
| 	<foaf:name>Alexander "Polynomdivision"</foaf:name> |  | ||||||
| 	<foaf:homepage rdf:resource="https://blog.polynom.me" /> |  | ||||||
|       </foaf:Person> |  | ||||||
|     </maintainer> |  | ||||||
| 
 |  | ||||||
|     <implements rdf:resource="https://xmpp.org/rfcs/rfc6120.html" /> |  | ||||||
| 
 |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0004.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>2.13.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0030.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>2.5rc3</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0054.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>1.2</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0060.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>1.24.1</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0066.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:note xml:lang="en">Only jabber:x:oob</xmpp:note> |  | ||||||
| 	<xmpp:version>1.5</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0084.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:note xml:lang="en">Receiving data</xmpp:note> |  | ||||||
| 	<xmpp:version>1.1.4</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0085.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>2.1</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0115.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>1.5.2</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0153.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>1.1</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0184.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>1.4.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0191.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>1.3.0</xmpp:version> |  | ||||||
| 	<xmpp:note xml:lang="en">Not plugged into the UI</xmpp:note> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0198.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>1.6</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0280.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>1.0.1</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0297.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:note xml:lang="en">Exists only as part of support for XEP-0280</xmpp:note> |  | ||||||
| 	<xmpp:version>1.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0300.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:note xml:lang="en">Supports only Sha256, Sha512 and blake2b512</xmpp:note> |  | ||||||
| 	<xmpp:version>1.0.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0308.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>1.2.1</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0333.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:note xml:lang="en">Read-only support</xmpp:note> |  | ||||||
| 	<xmpp:version>0.4</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0334.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:note xml:lang="en">Write-only support</xmpp:note> |  | ||||||
| 	<xmpp:version>0.3.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0352.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>1.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0359.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.6.1</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0363.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>1.1.0</xmpp:version> |  | ||||||
| 	<xmpp:note xml:lang="en">Only handles the success case; not accessible via the App</xmpp:note> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0368.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>1.1.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0380.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.4.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0384.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.8.3</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0420.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>0.4.1</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0424.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.3.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0444.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.1.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0446.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.2.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0447.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.1.2</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0448.html" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>0.2.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0449.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.1.1</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0461.html" /> |  | ||||||
| 	<xmpp:status>complete</xmpp:status> |  | ||||||
| 	<xmpp:version>0.2.0</xmpp:version> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://codeberg.org/moxxy/custom-xeps/src/branch/master/xep-xxxx-extensible-file-thumbnails.md" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>0.2.1</xmpp:version> |  | ||||||
| 	<xmpp:note xml:lang="en">Only Blurhash is implemented</xmpp:note> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|     <implements> |  | ||||||
|       <xmpp:SupportedXep> |  | ||||||
| 	<xmpp:xep rdf:resource="https://codeberg.org/moxxy/custom-xeps/src/branch/master/xep-xxxx-file-upload-notification.md" /> |  | ||||||
| 	<xmpp:status>partial</xmpp:status> |  | ||||||
| 	<xmpp:version>0.0.5</xmpp:version> |  | ||||||
| 	<xmpp:note xml:lang="en">Sending and receiving implemented; cancellation not implemented</xmpp:note> |  | ||||||
|       </xmpp:SupportedXep> |  | ||||||
|     </implements> |  | ||||||
|   </Project> |  | ||||||
| </rdf:RDF> |  | ||||||
| @ -1,43 +0,0 @@ | |||||||
| import 'package:moxxmpp/src/util/queue.dart'; |  | ||||||
| import 'package:test/test.dart'; |  | ||||||
| 
 |  | ||||||
| void main() { |  | ||||||
|   test('Test the async queue', () async { |  | ||||||
|     final queue = AsyncQueue(); |  | ||||||
|     int future1Finish = 0; |  | ||||||
|     int future2Finish = 0; |  | ||||||
|     int future3Finish = 0; |  | ||||||
| 
 |  | ||||||
|     await queue.addJob(() => Future<void>.delayed(const Duration(seconds: 3), () => future1Finish = DateTime.now().millisecondsSinceEpoch)); |  | ||||||
|     await queue.addJob(() => Future<void>.delayed(const Duration(seconds: 3), () => future2Finish = DateTime.now().millisecondsSinceEpoch)); |  | ||||||
|     await queue.addJob(() => Future<void>.delayed(const Duration(seconds: 3), () => future3Finish = DateTime.now().millisecondsSinceEpoch)); |  | ||||||
| 
 |  | ||||||
|     await Future<void>.delayed(const Duration(seconds: 12)); |  | ||||||
| 
 |  | ||||||
|     // The three futures must be done |  | ||||||
|     expect(future1Finish != 0, true); |  | ||||||
|     expect(future2Finish != 0, true); |  | ||||||
|     expect(future3Finish != 0, true); |  | ||||||
| 
 |  | ||||||
|     // The end times of the futures must be ordered (on a timeline) |  | ||||||
|     // |-- future1Finish -- future2Finish -- future3Finish --| |  | ||||||
|     expect( |  | ||||||
|       future1Finish < future2Finish && future1Finish < future3Finish, |  | ||||||
|       true, |  | ||||||
|     ); |  | ||||||
|     expect( |  | ||||||
|       future2Finish < future3Finish && future2Finish > future1Finish, |  | ||||||
|       true, |  | ||||||
|     ); |  | ||||||
|     expect( |  | ||||||
|       future3Finish > future1Finish && future3Finish > future2Finish, |  | ||||||
|       true, |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     // The queue must be empty at the end |  | ||||||
|     expect(queue.queue.isEmpty, true); |  | ||||||
| 
 |  | ||||||
|     // The queue must not be executing anything at the end |  | ||||||
|     expect(queue.isRunning, false); |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
| @ -54,20 +54,16 @@ void main() { | |||||||
|     ], |     ], | ||||||
|   ); |   ); | ||||||
|    |    | ||||||
|   final connection = XmppConnection( |   final connection = XmppConnection(TestingReconnectionPolicy(), stubSocket) | ||||||
|     TestingReconnectionPolicy(), |     ..registerFeatureNegotiators([ | ||||||
|     AlwaysConnectedConnectivityManager(), |  | ||||||
|     stubSocket, |  | ||||||
|   )..registerFeatureNegotiators([ |  | ||||||
|       StubNegotiator1(), |       StubNegotiator1(), | ||||||
|       StubNegotiator2(), |       StubNegotiator2(), | ||||||
|     ]) |     ]) | ||||||
|     ..registerManagers([ |     ..registerManagers([ | ||||||
|       PresenceManager(), |       PresenceManager('http://moxxmpp.example'), | ||||||
|       RosterManager(TestingRosterStateManager('', [])), |       RosterManager(TestingRosterStateManager('', [])), | ||||||
|       DiscoManager([]), |       DiscoManager(), | ||||||
|       PingManager(), |       PingManager(), | ||||||
|       EntityCapabilitiesManager('http://moxxmpp.example'), |  | ||||||
|     ]) |     ]) | ||||||
|     ..setConnectionSettings( |     ..setConnectionSettings( | ||||||
|       ConnectionSettings( |       ConnectionSettings( | ||||||
|  | |||||||
| @ -3,14 +3,14 @@ import 'package:test/test.dart'; | |||||||
| 
 | 
 | ||||||
| void main() { | void main() { | ||||||
|   test('Parsing', () { |   test('Parsing', () { | ||||||
|     const testData = "<x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>urn:xmpp:dataforms:softwareinfo</value></field><field var='ip_version' type='text-multi' ><value>ipv4</value><value>ipv6</value></field><field var='os'><value>Mac</value></field><field var='os_version'><value>10.5.1</value></field><field var='software'><value>Psi</value></field><field var='software_version'><value>0.11</value></field></x>"; |       const testData = "<x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>urn:xmpp:dataforms:softwareinfo</value></field><field var='ip_version' type='text-multi' ><value>ipv4</value><value>ipv6</value></field><field var='os'><value>Mac</value></field><field var='os_version'><value>10.5.1</value></field><field var='software'><value>Psi</value></field><field var='software_version'><value>0.11</value></field></x>"; | ||||||
| 
 | 
 | ||||||
|     final form = parseDataForm(XMLNode.fromString(testData)); |       final form = parseDataForm(XMLNode.fromString(testData)); | ||||||
|     expect(form.getFieldByVar('FORM_TYPE')?.values.first, 'urn:xmpp:dataforms:softwareinfo'); |       expect(form.getFieldByVar('FORM_TYPE')?.values.first, 'urn:xmpp:dataforms:softwareinfo'); | ||||||
|     expect(form.getFieldByVar('ip_version')?.values, [ 'ipv4', 'ipv6' ]); |       expect(form.getFieldByVar('ip_version')?.values, [ 'ipv4', 'ipv6' ]); | ||||||
|     expect(form.getFieldByVar('os')?.values.first, 'Mac'); |       expect(form.getFieldByVar('os')?.values.first, 'Mac'); | ||||||
|     expect(form.getFieldByVar('os_version')?.values.first, '10.5.1'); |       expect(form.getFieldByVar('os_version')?.values.first, '10.5.1'); | ||||||
|     expect(form.getFieldByVar('software')?.values.first, 'Psi'); |       expect(form.getFieldByVar('software')?.values.first, 'Psi'); | ||||||
|     expect(form.getFieldByVar('software_version')?.values.first, '0.11'); |       expect(form.getFieldByVar('software_version')?.values.first, '0.11'); | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -53,7 +53,7 @@ void main() { | |||||||
|           ignoreId: true, |           ignoreId: true, | ||||||
|         ), |         ), | ||||||
|         StringExpectation( |         StringExpectation( | ||||||
|           "<presence xmlns='jabber:client' from='polynomdivision@test.server/MU29eEZn'><show>chat</show><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://moxxmpp.example' ver='3QvQ2RAy45XBDhArjxy/vEWMl+E=' /></presence>", |           "<presence xmlns='jabber:client' from='polynomdivision@test.server/MU29eEZn'><show>chat</show><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://moxxmpp.example' ver='QRTBC5cg/oYd+UOTYazSQR4zb/I=' /></presence>", | ||||||
|           '', |           '', | ||||||
|         ), |         ), | ||||||
|         StanzaExpectation( |         StanzaExpectation( | ||||||
| @ -65,11 +65,7 @@ void main() { | |||||||
| 
 | 
 | ||||||
|       ], |       ], | ||||||
|     ); |     ); | ||||||
|     final XmppConnection conn = XmppConnection( |     final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), socket: fakeSocket); | ||||||
|       TestingReconnectionPolicy(), |  | ||||||
|       AlwaysConnectedConnectivityManager(), |  | ||||||
|       fakeSocket, |  | ||||||
|     ); |  | ||||||
|     conn.setConnectionSettings(ConnectionSettings( |     conn.setConnectionSettings(ConnectionSettings( | ||||||
|         jid: JID.fromString('polynomdivision@test.server'), |         jid: JID.fromString('polynomdivision@test.server'), | ||||||
|         password: 'aaaa', |         password: 'aaaa', | ||||||
| @ -77,11 +73,10 @@ void main() { | |||||||
|         allowPlainAuth: true, |         allowPlainAuth: true, | ||||||
|     ),); |     ),); | ||||||
|     conn.registerManagers([ |     conn.registerManagers([ | ||||||
|       PresenceManager(), |       PresenceManager('http://moxxmpp.example'), | ||||||
|       RosterManager(TestingRosterStateManager(null, [])), |       RosterManager(), | ||||||
|       DiscoManager([]), |       DiscoManager(), | ||||||
|       PingManager(), |       PingManager(), | ||||||
|       EntityCapabilitiesManager('http://moxxmpp.example'), |  | ||||||
|     ]); |     ]); | ||||||
|     conn.registerFeatureNegotiators( |     conn.registerFeatureNegotiators( | ||||||
|       [ |       [ | ||||||
| @ -4,167 +4,164 @@ import 'package:test/test.dart'; | |||||||
| 
 | 
 | ||||||
| void main() { | void main() { | ||||||
|   test('Test XEP example', () async { |   test('Test XEP example', () async { | ||||||
|     final data = DiscoInfo( |       final data = DiscoInfo( | ||||||
|       [ |         [ | ||||||
|         'http://jabber.org/protocol/caps', |           'http://jabber.org/protocol/caps', | ||||||
|         'http://jabber.org/protocol/disco#info', |           'http://jabber.org/protocol/disco#info', | ||||||
|         'http://jabber.org/protocol/disco#items', |           'http://jabber.org/protocol/disco#items', | ||||||
|         'http://jabber.org/protocol/muc' |           'http://jabber.org/protocol/muc' | ||||||
|       ], |         ], | ||||||
|       [ |         [ | ||||||
|         Identity( |           Identity( | ||||||
|           category: 'client', |             category: 'client', | ||||||
|           type: 'pc', |             type: 'pc', | ||||||
|           name: 'Exodus 0.9.1', |             name: 'Exodus 0.9.1', | ||||||
|         ) |           ) | ||||||
|       ], |         ], | ||||||
|       [], |         [], | ||||||
|       null, |         JID.fromString('some@user.local/test'), | ||||||
|       JID.fromString('some@user.local/test'), |       ); | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     final hash = await calculateCapabilityHash(data, Sha1()); |       final hash = await calculateCapabilityHash(data, Sha1()); | ||||||
|     expect(hash, 'QgayPKawpkPSDYmwT/WM94uAlu0='); |       expect(hash, 'QgayPKawpkPSDYmwT/WM94uAlu0='); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   test('Test complex generation example', () async { |   test('Test complex generation example', () async { | ||||||
|     const extDiscoDataString = "<x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>urn:xmpp:dataforms:softwareinfo</value></field><field var='ip_version' type='text-multi' ><value>ipv4</value><value>ipv6</value></field><field var='os'><value>Mac</value></field><field var='os_version'><value>10.5.1</value></field><field var='software'><value>Psi</value></field><field var='software_version'><value>0.11</value></field></x>"; |       const extDiscoDataString = "<x xmlns='jabber:x:data' type='result'><field var='FORM_TYPE' type='hidden'><value>urn:xmpp:dataforms:softwareinfo</value></field><field var='ip_version' type='text-multi' ><value>ipv4</value><value>ipv6</value></field><field var='os'><value>Mac</value></field><field var='os_version'><value>10.5.1</value></field><field var='software'><value>Psi</value></field><field var='software_version'><value>0.11</value></field></x>"; | ||||||
|     final data = DiscoInfo( |       final data = DiscoInfo( | ||||||
|       [ |         [ | ||||||
|         'http://jabber.org/protocol/caps', |           'http://jabber.org/protocol/caps', | ||||||
|         'http://jabber.org/protocol/disco#info', |           'http://jabber.org/protocol/disco#info', | ||||||
|         'http://jabber.org/protocol/disco#items', |           'http://jabber.org/protocol/disco#items', | ||||||
|         'http://jabber.org/protocol/muc' |           'http://jabber.org/protocol/muc' | ||||||
|       ], |         ], | ||||||
|       [ |         [ | ||||||
|         const Identity( |           const Identity( | ||||||
|           category: 'client', |             category: 'client', | ||||||
|           type: 'pc', |             type: 'pc', | ||||||
|           name: 'Psi 0.11', |             name: 'Psi 0.11', | ||||||
|           lang: 'en', |             lang: 'en', | ||||||
|         ), |           ), | ||||||
|         const Identity( |           const Identity( | ||||||
|           category: 'client', |             category: 'client', | ||||||
|           type: 'pc', |             type: 'pc', | ||||||
|           name: 'Ψ 0.11', |             name: 'Ψ 0.11', | ||||||
|           lang: 'el', |             lang: 'el', | ||||||
|         ), |           ), | ||||||
|       ], |         ], | ||||||
|       [ parseDataForm(XMLNode.fromString(extDiscoDataString)) ], |         [ parseDataForm(XMLNode.fromString(extDiscoDataString)) ], | ||||||
|       null, |         JID.fromString('some@user.local/test'), | ||||||
|       JID.fromString('some@user.local/test'), |       ); | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     final hash = await calculateCapabilityHash(data, Sha1()); |       final hash = await calculateCapabilityHash(data, Sha1()); | ||||||
|     expect(hash, 'q07IKJEyjvHSyhy//CH0CxmKi8w='); |       expect(hash, 'q07IKJEyjvHSyhy//CH0CxmKi8w='); | ||||||
|   }); |   }); | ||||||
|    |    | ||||||
|   test('Test Gajim capability hash computation', () async { |   test('Test Gajim capability hash computation', () async { | ||||||
|     // TODO: This one fails |       // TODO: This one fails | ||||||
|     /* |       /* | ||||||
|     final data = DiscoInfo( |       final data = DiscoInfo( | ||||||
|       features: [ |         features: [ | ||||||
|         "http://jabber.org/protocol/bytestreams", |           "http://jabber.org/protocol/bytestreams", | ||||||
|         "http://jabber.org/protocol/muc", |           "http://jabber.org/protocol/muc", | ||||||
|         "http://jabber.org/protocol/commands", |           "http://jabber.org/protocol/commands", | ||||||
|         "http://jabber.org/protocol/disco#info", |           "http://jabber.org/protocol/disco#info", | ||||||
|         "jabber:iq:last", |           "jabber:iq:last", | ||||||
|         "jabber:x:data", |           "jabber:x:data", | ||||||
|         "jabber:x:encrypted", |           "jabber:x:encrypted", | ||||||
|         "urn:xmpp:ping", |           "urn:xmpp:ping", | ||||||
|         "http://jabber.org/protocol/chatstates", |           "http://jabber.org/protocol/chatstates", | ||||||
|         "urn:xmpp:receipts", |           "urn:xmpp:receipts", | ||||||
|         "urn:xmpp:time", |           "urn:xmpp:time", | ||||||
|         "jabber:iq:version", |           "jabber:iq:version", | ||||||
|         "http://jabber.org/protocol/rosterx", |           "http://jabber.org/protocol/rosterx", | ||||||
|         "urn:xmpp:sec-label:0", |           "urn:xmpp:sec-label:0", | ||||||
|         "jabber:x:conference", |           "jabber:x:conference", | ||||||
|         "urn:xmpp:message-correct:0", |           "urn:xmpp:message-correct:0", | ||||||
|         "urn:xmpp:chat-markers:0", |           "urn:xmpp:chat-markers:0", | ||||||
|         "urn:xmpp:eme:0", |           "urn:xmpp:eme:0", | ||||||
|         "http://jabber.org/protocol/xhtml-im", |           "http://jabber.org/protocol/xhtml-im", | ||||||
|         "urn:xmpp:hashes:2", |           "urn:xmpp:hashes:2", | ||||||
|         "urn:xmpp:hash-function-text-names:md5", |           "urn:xmpp:hash-function-text-names:md5", | ||||||
|         "urn:xmpp:hash-function-text-names:sha-1", |           "urn:xmpp:hash-function-text-names:sha-1", | ||||||
|         "urn:xmpp:hash-function-text-names:sha-256", |           "urn:xmpp:hash-function-text-names:sha-256", | ||||||
|         "urn:xmpp:hash-function-text-names:sha-512", |           "urn:xmpp:hash-function-text-names:sha-512", | ||||||
|         "urn:xmpp:hash-function-text-names:sha3-256", |           "urn:xmpp:hash-function-text-names:sha3-256", | ||||||
|         "urn:xmpp:hash-function-text-names:sha3-512", |           "urn:xmpp:hash-function-text-names:sha3-512", | ||||||
|         "urn:xmpp:hash-function-text-names:id-blake2b256", |           "urn:xmpp:hash-function-text-names:id-blake2b256", | ||||||
|         "urn:xmpp:hash-function-text-names:id-blake2b512", |           "urn:xmpp:hash-function-text-names:id-blake2b512", | ||||||
|         "urn:xmpp:jingle:1", |           "urn:xmpp:jingle:1", | ||||||
|         "urn:xmpp:jingle:apps:file-transfer:5", |           "urn:xmpp:jingle:apps:file-transfer:5", | ||||||
|         "urn:xmpp:jingle:security:xtls:0", |           "urn:xmpp:jingle:security:xtls:0", | ||||||
|         "urn:xmpp:jingle:transports:s5b:1", |           "urn:xmpp:jingle:transports:s5b:1", | ||||||
|         "urn:xmpp:jingle:transports:ibb:1", |           "urn:xmpp:jingle:transports:ibb:1", | ||||||
|         "urn:xmpp:avatar:metadata+notify", |           "urn:xmpp:avatar:metadata+notify", | ||||||
|         "urn:xmpp:message-moderate:0", |           "urn:xmpp:message-moderate:0", | ||||||
|         "http://jabber.org/protocol/tune+notify", |           "http://jabber.org/protocol/tune+notify", | ||||||
|         "http://jabber.org/protocol/geoloc+notify", |           "http://jabber.org/protocol/geoloc+notify", | ||||||
|         "http://jabber.org/protocol/nick+notify", |           "http://jabber.org/protocol/nick+notify", | ||||||
|         "eu.siacs.conversations.axolotl.devicelist+notify", |           "eu.siacs.conversations.axolotl.devicelist+notify", | ||||||
|       ], |         ], | ||||||
|       identities: [ |         identities: [ | ||||||
|         Identity( |           Identity( | ||||||
|           category: "client", |             category: "client", | ||||||
|           type: "pc", |             type: "pc", | ||||||
|           name: "Gajim" |             name: "Gajim" | ||||||
|         ) |           ) | ||||||
|       ] |         ] | ||||||
|     ); |       ); | ||||||
| 
 | 
 | ||||||
|     final hash = await calculateCapabilityHash(data, Sha1()); |       final hash = await calculateCapabilityHash(data, Sha1()); | ||||||
|     expect(hash, "T7fOZrtBnV8sDA2fFTS59vyOyUs="); |       expect(hash, "T7fOZrtBnV8sDA2fFTS59vyOyUs="); | ||||||
|     */ |       */ | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   test('Test Conversations hash computation', () async { |   test('Test Conversations hash computation', () async { | ||||||
|     final data = DiscoInfo( |       final data = DiscoInfo( | ||||||
|       [ |         [ | ||||||
|         'eu.siacs.conversations.axolotl.devicelist+notify', |           'eu.siacs.conversations.axolotl.devicelist+notify', | ||||||
|         'http://jabber.org/protocol/caps', |           'http://jabber.org/protocol/caps', | ||||||
|         'http://jabber.org/protocol/chatstates', |           'http://jabber.org/protocol/chatstates', | ||||||
|         'http://jabber.org/protocol/disco#info', |           'http://jabber.org/protocol/disco#info', | ||||||
|         'http://jabber.org/protocol/muc', |           'http://jabber.org/protocol/muc', | ||||||
|         'http://jabber.org/protocol/nick+notify', |           'http://jabber.org/protocol/nick+notify', | ||||||
|         'jabber:iq:version', |           'jabber:iq:version', | ||||||
|         'jabber:x:conference', |           'jabber:x:conference', | ||||||
|         'jabber:x:oob', |           'jabber:x:oob', | ||||||
|         'storage:bookmarks+notify', |           'storage:bookmarks+notify', | ||||||
|         'urn:xmpp:avatar:metadata+notify', |           'urn:xmpp:avatar:metadata+notify', | ||||||
|         'urn:xmpp:chat-markers:0', |           'urn:xmpp:chat-markers:0', | ||||||
|         'urn:xmpp:jingle-message:0', |           'urn:xmpp:jingle-message:0', | ||||||
|         'urn:xmpp:jingle:1', |           'urn:xmpp:jingle:1', | ||||||
|         'urn:xmpp:jingle:apps:dtls:0', |           'urn:xmpp:jingle:apps:dtls:0', | ||||||
|         'urn:xmpp:jingle:apps:file-transfer:3', |           'urn:xmpp:jingle:apps:file-transfer:3', | ||||||
|         'urn:xmpp:jingle:apps:file-transfer:4', |           'urn:xmpp:jingle:apps:file-transfer:4', | ||||||
|         'urn:xmpp:jingle:apps:file-transfer:5', |           'urn:xmpp:jingle:apps:file-transfer:5', | ||||||
|         'urn:xmpp:jingle:apps:rtp:1', |           'urn:xmpp:jingle:apps:rtp:1', | ||||||
|         'urn:xmpp:jingle:apps:rtp:audio', |           'urn:xmpp:jingle:apps:rtp:audio', | ||||||
|         'urn:xmpp:jingle:apps:rtp:video', |           'urn:xmpp:jingle:apps:rtp:video', | ||||||
|         'urn:xmpp:jingle:jet-omemo:0', |           'urn:xmpp:jingle:jet-omemo:0', | ||||||
|         'urn:xmpp:jingle:jet:0', |           'urn:xmpp:jingle:jet:0', | ||||||
|         'urn:xmpp:jingle:transports:ibb:1', |           'urn:xmpp:jingle:transports:ibb:1', | ||||||
|         'urn:xmpp:jingle:transports:ice-udp:1', |           'urn:xmpp:jingle:transports:ice-udp:1', | ||||||
|         'urn:xmpp:jingle:transports:s5b:1', |           'urn:xmpp:jingle:transports:s5b:1', | ||||||
|         'urn:xmpp:message-correct:0', |           'urn:xmpp:message-correct:0', | ||||||
|         'urn:xmpp:ping', |           'urn:xmpp:ping', | ||||||
|         'urn:xmpp:receipts', |           'urn:xmpp:receipts', | ||||||
|         'urn:xmpp:time' |           'urn:xmpp:time' | ||||||
|       ], |         ], | ||||||
|       [ |         [ | ||||||
|         Identity( |           Identity( | ||||||
|           category: 'client', |             category: 'client', | ||||||
|           type: 'phone', |             type: 'phone', | ||||||
|           name: 'Conversations', |             name: 'Conversations', | ||||||
|         ) |           ) | ||||||
|       ], |         ], | ||||||
|       [], |         [], | ||||||
|       null, |         JID.fromString('user@server.local/test'), | ||||||
|       JID.fromString('user@server.local/test'), |       ); | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     final hash = await calculateCapabilityHash(data, Sha1()); |       final hash = await calculateCapabilityHash(data, Sha1()); | ||||||
|     expect(hash, 'zcIke+Rk13ah4d1pwDG7bEZsVwA='); |       expect(hash, 'zcIke+Rk13ah4d1pwDG7bEZsVwA='); | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ XmppManagerAttributes mkAttributes(void Function(Stanza) callback) { | |||||||
|     isFeatureSupported: (_) => false, |     isFeatureSupported: (_) => false, | ||||||
|     getFullJID: () => JID.fromString('hallo@example.server/uwu'), |     getFullJID: () => JID.fromString('hallo@example.server/uwu'), | ||||||
|     getSocket: () => StubTCPSocket(play: []), |     getSocket: () => StubTCPSocket(play: []), | ||||||
|     getConnection: () => XmppConnection(TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), StubTCPSocket(play: [])), |     getConnection: () => XmppConnection(TestingReconnectionPolicy(), StubTCPSocket(play: [])), | ||||||
|     getNegotiatorById: getNegotiatorNullStub, |     getNegotiatorById: getNegotiatorNullStub, | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| @ -233,11 +233,7 @@ void main() { | |||||||
|         ] |         ] | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|       final XmppConnection conn = XmppConnection( |       final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), fakeSocket); | ||||||
|         TestingReconnectionPolicy(), |  | ||||||
|         AlwaysConnectedConnectivityManager(), |  | ||||||
|         fakeSocket, |  | ||||||
|       ); |  | ||||||
|       conn.setConnectionSettings(ConnectionSettings( |       conn.setConnectionSettings(ConnectionSettings( | ||||||
|           jid: JID.fromString('polynomdivision@test.server'), |           jid: JID.fromString('polynomdivision@test.server'), | ||||||
|           password: 'aaaa', |           password: 'aaaa', | ||||||
| @ -246,13 +242,12 @@ void main() { | |||||||
|       ),); |       ),); | ||||||
|       final sm = StreamManagementManager(); |       final sm = StreamManagementManager(); | ||||||
|       conn.registerManagers([ |       conn.registerManagers([ | ||||||
|         PresenceManager(), |           PresenceManager('http://moxxmpp.example'), | ||||||
|         RosterManager(TestingRosterStateManager('', [])), |           RosterManager(TestingRosterStateManager('', [])), | ||||||
|         DiscoManager([]), |           DiscoManager(), | ||||||
|         PingManager(), |           PingManager(), | ||||||
|         sm, |           sm, | ||||||
|         CarbonsManager()..forceEnable(), |           CarbonsManager()..forceEnable(), | ||||||
|         EntityCapabilitiesManager('http://moxxmpp.example'), |  | ||||||
|       ]); |       ]); | ||||||
|       conn.registerFeatureNegotiators( |       conn.registerFeatureNegotiators( | ||||||
|         [ |         [ | ||||||
| @ -348,7 +343,7 @@ void main() { | |||||||
|             '<enabled xmlns="urn:xmpp:sm:3" id="some-long-sm-id" resume="true" />', |             '<enabled xmlns="urn:xmpp:sm:3" id="some-long-sm-id" resume="true" />', | ||||||
|           ), |           ), | ||||||
|           StringExpectation( |           StringExpectation( | ||||||
|             "<presence xmlns='jabber:client' from='polynomdivision@test.server/MU29eEZn'><show>chat</show><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://moxxmpp.example' ver='3QvQ2RAy45XBDhArjxy/vEWMl+E=' /></presence>", |             "<presence xmlns='jabber:client' from='polynomdivision@test.server/MU29eEZn'><show>chat</show><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://moxxmpp.example' ver='QRTBC5cg/oYd+UOTYazSQR4zb/I=' /></presence>", | ||||||
|             '<iq type="result" />', |             '<iq type="result" />', | ||||||
|           ), |           ), | ||||||
|           StanzaExpectation( |           StanzaExpectation( | ||||||
| @ -360,11 +355,7 @@ void main() { | |||||||
|         ] |         ] | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|       final XmppConnection conn = XmppConnection( |       final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), fakeSocket); | ||||||
|         TestingReconnectionPolicy(), |  | ||||||
|         AlwaysConnectedConnectivityManager(), |  | ||||||
|         fakeSocket, |  | ||||||
|       ); |  | ||||||
|       conn.setConnectionSettings(ConnectionSettings( |       conn.setConnectionSettings(ConnectionSettings( | ||||||
|           jid: JID.fromString('polynomdivision@test.server'), |           jid: JID.fromString('polynomdivision@test.server'), | ||||||
|           password: 'aaaa', |           password: 'aaaa', | ||||||
| @ -373,13 +364,12 @@ void main() { | |||||||
|       ),); |       ),); | ||||||
|       final sm = StreamManagementManager(); |       final sm = StreamManagementManager(); | ||||||
|       conn.registerManagers([ |       conn.registerManagers([ | ||||||
|           PresenceManager(), |           PresenceManager('http://moxxmpp.example'), | ||||||
|           RosterManager(TestingRosterStateManager('', [])), |           RosterManager(TestingRosterStateManager('', [])), | ||||||
|           DiscoManager([]), |           DiscoManager(), | ||||||
|           PingManager(), |           PingManager(), | ||||||
|           sm, |           sm, | ||||||
|           CarbonsManager()..forceEnable(), |           CarbonsManager()..forceEnable(), | ||||||
|           EntityCapabilitiesManager('http://moxxmpp.example'), |  | ||||||
|       ]); |       ]); | ||||||
|       conn.registerFeatureNegotiators( |       conn.registerFeatureNegotiators( | ||||||
|         [ |         [ | ||||||
| @ -520,11 +510,7 @@ void main() { | |||||||
|         ] |         ] | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|       final XmppConnection conn = XmppConnection( |       final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), fakeSocket); | ||||||
|         TestingReconnectionPolicy(), |  | ||||||
|         AlwaysConnectedConnectivityManager(), |  | ||||||
|         fakeSocket, |  | ||||||
|       ); |  | ||||||
|       conn.setConnectionSettings(ConnectionSettings( |       conn.setConnectionSettings(ConnectionSettings( | ||||||
|           jid: JID.fromString('polynomdivision@test.server'), |           jid: JID.fromString('polynomdivision@test.server'), | ||||||
|           password: 'aaaa', |           password: 'aaaa', | ||||||
| @ -532,9 +518,9 @@ void main() { | |||||||
|           allowPlainAuth: true, |           allowPlainAuth: true, | ||||||
|       ),); |       ),); | ||||||
|       conn.registerManagers([ |       conn.registerManagers([ | ||||||
|           PresenceManager(), |           PresenceManager('http://moxxmpp.example'), | ||||||
|           RosterManager(TestingRosterStateManager('', [])), |           RosterManager(TestingRosterStateManager('', [])), | ||||||
|           DiscoManager([]), |           DiscoManager(), | ||||||
|           PingManager(), |           PingManager(), | ||||||
|           StreamManagementManager(), |           StreamManagementManager(), | ||||||
|       ]); |       ]); | ||||||
| @ -616,11 +602,7 @@ void main() { | |||||||
|         ] |         ] | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|       final XmppConnection conn = XmppConnection( |       final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), fakeSocket); | ||||||
|         TestingReconnectionPolicy(), |  | ||||||
|         AlwaysConnectedConnectivityManager(), |  | ||||||
|         fakeSocket, |  | ||||||
|       ); |  | ||||||
|       conn.setConnectionSettings(ConnectionSettings( |       conn.setConnectionSettings(ConnectionSettings( | ||||||
|           jid: JID.fromString('polynomdivision@test.server'), |           jid: JID.fromString('polynomdivision@test.server'), | ||||||
|           password: 'aaaa', |           password: 'aaaa', | ||||||
| @ -628,9 +610,9 @@ void main() { | |||||||
|           allowPlainAuth: true, |           allowPlainAuth: true, | ||||||
|       ),); |       ),); | ||||||
|       conn.registerManagers([ |       conn.registerManagers([ | ||||||
|           PresenceManager(), |           PresenceManager('http://moxxmpp.example'), | ||||||
|           RosterManager(TestingRosterStateManager('', [])), |           RosterManager(TestingRosterStateManager('', [])), | ||||||
|           DiscoManager([]), |           DiscoManager(), | ||||||
|           PingManager(), |           PingManager(), | ||||||
|           StreamManagementManager(), |           StreamManagementManager(), | ||||||
|       ]); |       ]); | ||||||
| @ -712,11 +694,7 @@ void main() { | |||||||
|         ] |         ] | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|       final XmppConnection conn = XmppConnection( |       final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), fakeSocket); | ||||||
|         TestingReconnectionPolicy(), |  | ||||||
|         AlwaysConnectedConnectivityManager(), |  | ||||||
|         fakeSocket, |  | ||||||
|       ); |  | ||||||
|       conn.setConnectionSettings(ConnectionSettings( |       conn.setConnectionSettings(ConnectionSettings( | ||||||
|           jid: JID.fromString('polynomdivision@test.server'), |           jid: JID.fromString('polynomdivision@test.server'), | ||||||
|           password: 'aaaa', |           password: 'aaaa', | ||||||
| @ -724,9 +702,9 @@ void main() { | |||||||
|           allowPlainAuth: true, |           allowPlainAuth: true, | ||||||
|       ),); |       ),); | ||||||
|       conn.registerManagers([ |       conn.registerManagers([ | ||||||
|           PresenceManager(), |           PresenceManager('http://moxxmpp.example'), | ||||||
|           RosterManager(TestingRosterStateManager('', [])), |           RosterManager(TestingRosterStateManager('', [])), | ||||||
|           DiscoManager([]), |           DiscoManager(), | ||||||
|           PingManager(), |           PingManager(), | ||||||
|           StreamManagementManager(), |           StreamManagementManager(), | ||||||
|       ]); |       ]); | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ void main() { | |||||||
|       isFeatureSupported: (_) => false, |       isFeatureSupported: (_) => false, | ||||||
|       getFullJID: () => JID.fromString('bob@xmpp.example/uwu'), |       getFullJID: () => JID.fromString('bob@xmpp.example/uwu'), | ||||||
|       getSocket: () => StubTCPSocket(play: []), |       getSocket: () => StubTCPSocket(play: []), | ||||||
|       getConnection: () => XmppConnection(TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), StubTCPSocket(play: [])), |       getConnection: () => XmppConnection(TestingReconnectionPolicy(), StubTCPSocket(play: [])), | ||||||
|       getNegotiatorById: getNegotiatorNullStub, |       getNegotiatorById: getNegotiatorNullStub, | ||||||
|     ); |     ); | ||||||
|     final manager = CarbonsManager(); |     final manager = CarbonsManager(); | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ void main() { | |||||||
|           isFeatureSupported: (_) => false, |           isFeatureSupported: (_) => false, | ||||||
|           getFullJID: () => JID.fromString('some.user@example.server/aaaaa'), |           getFullJID: () => JID.fromString('some.user@example.server/aaaaa'), | ||||||
|           getSocket: () => StubTCPSocket(play: []), |           getSocket: () => StubTCPSocket(play: []), | ||||||
|           getConnection: () => XmppConnection(TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), StubTCPSocket(play: [])), |           getConnection: () => XmppConnection(TestingReconnectionPolicy(), StubTCPSocket(play: [])), | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
| @ -79,7 +79,7 @@ void main() { | |||||||
|           isFeatureSupported: (_) => false, |           isFeatureSupported: (_) => false, | ||||||
|           getFullJID: () => JID.fromString('some.user@example.server/aaaaa'), |           getFullJID: () => JID.fromString('some.user@example.server/aaaaa'), | ||||||
|           getSocket: () => StubTCPSocket(play: []), |           getSocket: () => StubTCPSocket(play: []), | ||||||
|           getConnection: () => XmppConnection(TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), StubTCPSocket(play: [])), |           getConnection: () => XmppConnection(TestingReconnectionPolicy(), StubTCPSocket(play: [])), | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,52 +3,52 @@ import 'package:test/test.dart'; | |||||||
| 
 | 
 | ||||||
| void main() { | void main() { | ||||||
|   group('Test the XEP-0363 header preparation', () { |   group('Test the XEP-0363 header preparation', () { | ||||||
|     test('invariance', () { |       test('invariance', () { | ||||||
|       final headers = { |           final headers = { | ||||||
|         'authorization': 'Basic Base64String==', |             'authorization': 'Basic Base64String==', | ||||||
|         'cookie': 'foo=bar; user=romeo' |             'cookie': 'foo=bar; user=romeo' | ||||||
|       }; |           }; | ||||||
|       expect( |           expect( | ||||||
|         prepareHeaders(headers), |             prepareHeaders(headers), | ||||||
|         headers, |             headers, | ||||||
|       ); |           ); | ||||||
|     }); |       }); | ||||||
|     test('invariance through uppercase', () { |       test('invariance through uppercase', () { | ||||||
|       final headers = { |           final headers = { | ||||||
|         'Authorization': 'Basic Base64String==', |             'Authorization': 'Basic Base64String==', | ||||||
|         'Cookie': 'foo=bar; user=romeo' |             'Cookie': 'foo=bar; user=romeo' | ||||||
|       }; |           }; | ||||||
|       expect( |           expect( | ||||||
|         prepareHeaders(headers), |             prepareHeaders(headers), | ||||||
|         headers, |             headers, | ||||||
|       ); |           ); | ||||||
|     }); |       }); | ||||||
|     test('remove unspecified headers', () { |       test('remove unspecified headers', () { | ||||||
|       final headers = { |           final headers = { | ||||||
|         'Authorization': 'Basic Base64String==', |             'Authorization': 'Basic Base64String==', | ||||||
|         'Cookie': 'foo=bar; user=romeo', |             'Cookie': 'foo=bar; user=romeo', | ||||||
|         'X-Tracking': 'Base64String==' |             'X-Tracking': 'Base64String==' | ||||||
|       }; |           }; | ||||||
|       expect( |           expect( | ||||||
|         prepareHeaders(headers), |             prepareHeaders(headers), | ||||||
|         { |             { | ||||||
|           'Authorization': 'Basic Base64String==', |               'Authorization': 'Basic Base64String==', | ||||||
|           'Cookie': 'foo=bar; user=romeo', |               'Cookie': 'foo=bar; user=romeo', | ||||||
|         } |             } | ||||||
|       ); |           ); | ||||||
|     }); |       }); | ||||||
|     test('remove newlines', () { |       test('remove newlines', () { | ||||||
|       final headers = { |           final headers = { | ||||||
|         'Authorization': '\n\nBasic Base64String==\n\n', |             'Authorization': '\n\nBasic Base64String==\n\n', | ||||||
|         '\nCookie\r\n': 'foo=bar; user=romeo', |             '\nCookie\r\n': 'foo=bar; user=romeo', | ||||||
|       }; |           }; | ||||||
|       expect( |           expect( | ||||||
|         prepareHeaders(headers), |             prepareHeaders(headers), | ||||||
|         { |             { | ||||||
|           'Authorization': 'Basic Base64String==', |               'Authorization': 'Basic Base64String==', | ||||||
|           'Cookie': 'foo=bar; user=romeo', |               'Cookie': 'foo=bar; user=romeo', | ||||||
|         } |             } | ||||||
|       ); |           ); | ||||||
|     }); |       }); | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ Future<bool> testRosterManager(String bareJid, String resource, String stanzaStr | |||||||
|       isFeatureSupported: (_) => false, |       isFeatureSupported: (_) => false, | ||||||
|       getFullJID: () => JID.fromString('$bareJid/$resource'), |       getFullJID: () => JID.fromString('$bareJid/$resource'), | ||||||
|       getSocket: () => StubTCPSocket(play: []), |       getSocket: () => StubTCPSocket(play: []), | ||||||
|       getConnection: () => XmppConnection(TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), StubTCPSocket(play: [])), |       getConnection: () => XmppConnection(TestingReconnectionPolicy(), StubTCPSocket(play: [])), | ||||||
|   ),); |   ),); | ||||||
| 
 | 
 | ||||||
|   final stanza = Stanza.fromXMLNode(XMLNode.fromString(stanzaString)); |   final stanza = Stanza.fromXMLNode(XMLNode.fromString(stanzaString)); | ||||||
| @ -118,10 +118,7 @@ void main() { | |||||||
|         ], |         ], | ||||||
|       ); |       ); | ||||||
|       // TODO: This test is broken since we query the server and enable carbons |       // TODO: This test is broken since we query the server and enable carbons | ||||||
|       final XmppConnection conn = XmppConnection( |       final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), fakeSocket); | ||||||
|         TestingReconnectionPolicy(), |  | ||||||
|         AlwaysConnectedConnectivityManager(), |  | ||||||
|         fakeSocket); |  | ||||||
|       conn.setConnectionSettings(ConnectionSettings( |       conn.setConnectionSettings(ConnectionSettings( | ||||||
|           jid: JID.fromString('polynomdivision@test.server'), |           jid: JID.fromString('polynomdivision@test.server'), | ||||||
|           password: 'aaaa', |           password: 'aaaa', | ||||||
| @ -129,12 +126,11 @@ void main() { | |||||||
|           allowPlainAuth: true, |           allowPlainAuth: true, | ||||||
|       ),); |       ),); | ||||||
|       conn.registerManagers([ |       conn.registerManagers([ | ||||||
|         PresenceManager(), |         PresenceManager('http://moxxmpp.example'), | ||||||
|         RosterManager(TestingRosterStateManager('', [])), |         RosterManager(TestingRosterStateManager('', [])), | ||||||
|         DiscoManager([]), |         DiscoManager(), | ||||||
|         PingManager(), |         PingManager(), | ||||||
|         StreamManagementManager(), |         StreamManagementManager(), | ||||||
|         EntityCapabilitiesManager('http://moxxmpp.example'), |  | ||||||
|       ]); |       ]); | ||||||
|       conn.registerFeatureNegotiators( |       conn.registerFeatureNegotiators( | ||||||
|         [ |         [ | ||||||
| @ -176,11 +172,7 @@ void main() { | |||||||
|         ], |         ], | ||||||
|       ); |       ); | ||||||
|       var receivedEvent = false; |       var receivedEvent = false; | ||||||
|       final XmppConnection conn = XmppConnection( |       final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), fakeSocket); | ||||||
|         TestingReconnectionPolicy(), |  | ||||||
|         AlwaysConnectedConnectivityManager(), |  | ||||||
|         fakeSocket, |  | ||||||
|       ); |  | ||||||
|       conn.setConnectionSettings(ConnectionSettings( |       conn.setConnectionSettings(ConnectionSettings( | ||||||
|         jid: JID.fromString('polynomdivision@test.server'), |         jid: JID.fromString('polynomdivision@test.server'), | ||||||
|         password: 'aaaa', |         password: 'aaaa', | ||||||
| @ -188,11 +180,10 @@ void main() { | |||||||
|         allowPlainAuth: true, |         allowPlainAuth: true, | ||||||
|       ),); |       ),); | ||||||
|       conn.registerManagers([ |       conn.registerManagers([ | ||||||
|         PresenceManager(), |         PresenceManager('http://moxxmpp.example'), | ||||||
|         RosterManager(TestingRosterStateManager('', [])), |         RosterManager(TestingRosterStateManager('', [])), | ||||||
|         DiscoManager([]), |         DiscoManager(), | ||||||
|         PingManager(), |         PingManager(), | ||||||
|         EntityCapabilitiesManager('http://moxxmpp.example'), |  | ||||||
|       ]); |       ]); | ||||||
|       conn.registerFeatureNegotiators([ |       conn.registerFeatureNegotiators([ | ||||||
|         SaslPlainNegotiator() |         SaslPlainNegotiator() | ||||||
| @ -235,11 +226,7 @@ void main() { | |||||||
|         ], |         ], | ||||||
|       ); |       ); | ||||||
|       var receivedEvent = false; |       var receivedEvent = false; | ||||||
|       final XmppConnection conn = XmppConnection( |       final XmppConnection conn = XmppConnection(TestingReconnectionPolicy(), fakeSocket); | ||||||
|         TestingReconnectionPolicy(), |  | ||||||
|         AlwaysConnectedConnectivityManager(), |  | ||||||
|         fakeSocket, |  | ||||||
|       ); |  | ||||||
|       conn.setConnectionSettings(ConnectionSettings( |       conn.setConnectionSettings(ConnectionSettings( | ||||||
|           jid: JID.fromString('polynomdivision@test.server'), |           jid: JID.fromString('polynomdivision@test.server'), | ||||||
|           password: 'aaaa', |           password: 'aaaa', | ||||||
| @ -247,11 +234,10 @@ void main() { | |||||||
|           allowPlainAuth: true, |           allowPlainAuth: true, | ||||||
|       ),); |       ),); | ||||||
|       conn.registerManagers([ |       conn.registerManagers([ | ||||||
|         PresenceManager(), |         PresenceManager('http://moxxmpp.example'), | ||||||
|         RosterManager(TestingRosterStateManager('', [])), |         RosterManager(TestingRosterStateManager('', [])), | ||||||
|         DiscoManager([]), |         DiscoManager(), | ||||||
|         PingManager(), |         PingManager(), | ||||||
|         EntityCapabilitiesManager('http://moxxmpp.example'), |  | ||||||
|       ]); |       ]); | ||||||
|       conn.registerFeatureNegotiators([ |       conn.registerFeatureNegotiators([ | ||||||
|         SaslPlainNegotiator() |         SaslPlainNegotiator() | ||||||
| @ -340,7 +326,7 @@ void main() { | |||||||
|               isFeatureSupported: (_) => false, |               isFeatureSupported: (_) => false, | ||||||
|               getFullJID: () => JID.fromString('some.user@example.server/aaaaa'), |               getFullJID: () => JID.fromString('some.user@example.server/aaaaa'), | ||||||
|               getSocket: () => StubTCPSocket(play: []), |               getSocket: () => StubTCPSocket(play: []), | ||||||
|               getConnection: () => XmppConnection(TestingReconnectionPolicy(), AlwaysConnectedConnectivityManager(), StubTCPSocket(play: [])), |               getConnection: () => XmppConnection(TestingReconnectionPolicy(), StubTCPSocket(play: [])), | ||||||
|           ),); |           ),); | ||||||
| 
 | 
 | ||||||
|           // NOTE: Based on https://gultsch.de/gajim_roster_push_and_message_interception.html |           // NOTE: Based on https://gultsch.de/gajim_roster_push_and_message_interception.html | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user