parent
							
								
									bff4a6f707
								
							
						
					
					
						commit
						1bd61076ea
					
				@ -223,63 +223,47 @@ class XmppConnection {
 | 
			
		||||
  /// none can be found.
 | 
			
		||||
  T? getNegotiatorById<T extends XmppFeatureNegotiatorBase>(String id) => _featureNegotiators[id] as T?;
 | 
			
		||||
  
 | 
			
		||||
  /// Registers an [XmppManagerBase] sub-class as a manager on this connection.
 | 
			
		||||
  /// [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) {
 | 
			
		||||
  /// Registers a list of [XmppManagerBase] sub-classes as managers on this connection.
 | 
			
		||||
  Future<void> registerManagers(List<XmppManagerBase> managers) async {
 | 
			
		||||
    for (final manager in managers) {
 | 
			
		||||
      registerManager(manager, sortHandlers: false);
 | 
			
		||||
      _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;
 | 
			
		||||
 | 
			
		||||
      _incomingStanzaHandlers.addAll(manager.getIncomingStanzaHandlers());
 | 
			
		||||
      _incomingPreStanzaHandlers.addAll(manager.getIncomingPreStanzaHandlers());
 | 
			
		||||
      _outgoingPreStanzaHandlers.addAll(manager.getOutgoingPreStanzaHandlers());
 | 
			
		||||
      _outgoingPostStanzaHandlers.addAll(manager.getOutgoingPostStanzaHandlers());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sort them
 | 
			
		||||
    _incomingStanzaHandlers.sort(stanzaHandlerSortComparator);
 | 
			
		||||
    _incomingPreStanzaHandlers.sort(stanzaHandlerSortComparator);
 | 
			
		||||
    _outgoingPreStanzaHandlers.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.
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,21 @@
 | 
			
		||||
import 'package:logging/logging.dart';
 | 
			
		||||
import 'package:meta/meta.dart';
 | 
			
		||||
import 'package:moxxmpp/src/events.dart';
 | 
			
		||||
import 'package:moxxmpp/src/managers/attributes.dart';
 | 
			
		||||
import 'package:moxxmpp/src/managers/data.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/xeps/xep_0030/types.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
 | 
			
		||||
 | 
			
		||||
abstract class XmppManagerBase {
 | 
			
		||||
  late final XmppManagerAttributes _managerAttributes;
 | 
			
		||||
  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
 | 
			
		||||
  void register(XmppManagerAttributes attributes) {
 | 
			
		||||
    _managerAttributes = attributes;
 | 
			
		||||
@ -48,6 +55,9 @@ abstract class XmppManagerBase {
 | 
			
		||||
 | 
			
		||||
  /// Return a list of features that should be included in a disco response.
 | 
			
		||||
  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.
 | 
			
		||||
  String getId();
 | 
			
		||||
@ -63,6 +73,24 @@ abstract class XmppManagerBase {
 | 
			
		||||
 | 
			
		||||
  /// Returns true if the XEP is supported on the server. If not, returns false
 | 
			
		||||
  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
 | 
			
		||||
  /// the nonza has been handled by one of the handlers. Resolves to false otherwise.
 | 
			
		||||
 | 
			
		||||
@ -28,3 +28,4 @@ const messageRetractionManager = 'org.moxxmpp.messageretractionmanager';
 | 
			
		||||
const lastMessageCorrectionManager = 'org.moxxmpp.lastmessagecorrectionmanager';
 | 
			
		||||
const messageReactionsManager = 'org.moxxmpp.messagereactionsmanager';
 | 
			
		||||
const stickersManager = 'org.moxxmpp.stickersmanager';
 | 
			
		||||
const entityCapabilitiesManager = 'org.moxxmpp.entitycapabilities';
 | 
			
		||||
 | 
			
		||||
@ -8,17 +8,19 @@ import 'package:moxxmpp/src/managers/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
import 'package:moxxmpp/src/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 {
 | 
			
		||||
  PresenceManager(this._capHashNode) : _capabilityHash = null, super();
 | 
			
		||||
  String? _capabilityHash;
 | 
			
		||||
  final String _capHashNode;
 | 
			
		||||
  PresenceManager() : super();
 | 
			
		||||
 | 
			
		||||
  String get capabilityHashNode => _capHashNode;
 | 
			
		||||
  /// The list of pre-send callbacks.
 | 
			
		||||
  final List<PresencePreSendCallback> _presenceCallbacks = List.empty(growable: true);
 | 
			
		||||
  
 | 
			
		||||
  @override
 | 
			
		||||
  String getId() => presenceManager;
 | 
			
		||||
@ -39,6 +41,11 @@ class PresenceManager extends XmppManagerBase {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  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 {
 | 
			
		||||
    final attrs = getAttributes();
 | 
			
		||||
@ -63,43 +70,26 @@ class PresenceManager extends XmppManagerBase {
 | 
			
		||||
    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.
 | 
			
		||||
  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();
 | 
			
		||||
    attrs.sendNonza(
 | 
			
		||||
      Stanza.presence(
 | 
			
		||||
        from: attrs.getFullJID().toString(),
 | 
			
		||||
        children: [
 | 
			
		||||
          XMLNode(
 | 
			
		||||
            tag: 'show',
 | 
			
		||||
            text: 'chat',
 | 
			
		||||
          ),
 | 
			
		||||
          XMLNode.xmlns(
 | 
			
		||||
            tag: 'c',
 | 
			
		||||
            xmlns: capsXmlns,
 | 
			
		||||
            attributes: {
 | 
			
		||||
              'hash': 'sha-1',
 | 
			
		||||
              'node': _capHashNode,
 | 
			
		||||
              'ver': await getCapabilityHash()
 | 
			
		||||
            },
 | 
			
		||||
          )
 | 
			
		||||
        ],
 | 
			
		||||
        children: children,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
 | 
			
		||||
class DataFormOption {
 | 
			
		||||
 | 
			
		||||
  const DataFormOption({ required this.value, this.label });
 | 
			
		||||
  final String? label;
 | 
			
		||||
  final String value;
 | 
			
		||||
@ -23,7 +22,6 @@ class DataFormOption {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DataFormField {
 | 
			
		||||
 | 
			
		||||
  const DataFormField({
 | 
			
		||||
      required this.options,
 | 
			
		||||
      required this.values,
 | 
			
		||||
@ -60,7 +58,6 @@ class DataFormField {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DataForm {
 | 
			
		||||
 | 
			
		||||
  const DataForm({
 | 
			
		||||
      required this.type,
 | 
			
		||||
      required this.instructions,
 | 
			
		||||
 | 
			
		||||
@ -6,20 +6,20 @@ import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
 | 
			
		||||
Stanza buildDiscoInfoQueryStanza(String entity, String? node) {
 | 
			
		||||
  return Stanza.iq(to: entity, type: 'get', children: [
 | 
			
		||||
      XMLNode.xmlns(
 | 
			
		||||
        tag: 'query',
 | 
			
		||||
        xmlns: discoInfoXmlns,
 | 
			
		||||
        attributes: node != null ? { 'node': node } : {},
 | 
			
		||||
      )
 | 
			
		||||
    XMLNode.xmlns(
 | 
			
		||||
      tag: 'query',
 | 
			
		||||
      xmlns: discoInfoXmlns,
 | 
			
		||||
      attributes: node != null ? { 'node': node } : {},
 | 
			
		||||
    )
 | 
			
		||||
  ],);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stanza buildDiscoItemsQueryStanza(String entity, { String? node }) {
 | 
			
		||||
  return Stanza.iq(to: entity, type: 'get', children: [
 | 
			
		||||
      XMLNode.xmlns(
 | 
			
		||||
        tag: 'query',
 | 
			
		||||
        xmlns: discoItemsXmlns,
 | 
			
		||||
        attributes: node != null ? { 'node': node } : {},
 | 
			
		||||
      )
 | 
			
		||||
    XMLNode.xmlns(
 | 
			
		||||
      tag: 'query',
 | 
			
		||||
      xmlns: discoItemsXmlns,
 | 
			
		||||
      attributes: node != null ? { 'node': node } : {},
 | 
			
		||||
    )
 | 
			
		||||
  ],);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,10 @@
 | 
			
		||||
import 'package:meta/meta.dart';
 | 
			
		||||
import 'package:moxxmpp/src/jid.dart';
 | 
			
		||||
import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0004.dart';
 | 
			
		||||
 | 
			
		||||
class Identity {
 | 
			
		||||
 | 
			
		||||
  const Identity({ required this.category, required this.type, this.name, this.lang });
 | 
			
		||||
  final String category;
 | 
			
		||||
  final String type;
 | 
			
		||||
@ -23,24 +24,96 @@ class Identity {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@immutable
 | 
			
		||||
class DiscoInfo {
 | 
			
		||||
 | 
			
		||||
  const DiscoInfo(
 | 
			
		||||
    this.features,
 | 
			
		||||
    this.identities,
 | 
			
		||||
    this.extendedInfo,
 | 
			
		||||
    this.node,
 | 
			
		||||
    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<Identity> identities;
 | 
			
		||||
  final List<DataForm> extendedInfo;
 | 
			
		||||
  final JID jid;
 | 
			
		||||
  final String? node;
 | 
			
		||||
  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 {
 | 
			
		||||
 | 
			
		||||
  const DiscoItem({ required this.jid, this.node, this.name });
 | 
			
		||||
  final String jid;
 | 
			
		||||
  final String? node;
 | 
			
		||||
  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,17 +7,21 @@ import 'package:moxxmpp/src/managers/data.dart';
 | 
			
		||||
import 'package:moxxmpp/src/managers/handlers.dart';
 | 
			
		||||
import 'package:moxxmpp/src/managers/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/presence.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
import 'package:moxxmpp/src/types/result.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0004.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0030/errors.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0030/helpers.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0115.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
 | 
			
		||||
class DiscoCacheKey {
 | 
			
		||||
  const DiscoCacheKey(this.jid, this.node);
 | 
			
		||||
@ -33,33 +37,49 @@ class DiscoCacheKey {
 | 
			
		||||
  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 {
 | 
			
		||||
  DiscoManager()
 | 
			
		||||
    : _features = List.empty(growable: true),
 | 
			
		||||
      _capHashCache = {},
 | 
			
		||||
      _capHashInfoCache = {},
 | 
			
		||||
      _discoInfoCache = {},
 | 
			
		||||
      _runningInfoQueries = {},
 | 
			
		||||
      _cacheLock = Lock(),
 | 
			
		||||
  /// [identities] is a list of disco identities that should be added by default
 | 
			
		||||
  /// to a disco#info response.
 | 
			
		||||
  DiscoManager(List<Identity> identities)
 | 
			
		||||
    : _identities = List<Identity>.from(identities),
 | 
			
		||||
      super();
 | 
			
		||||
  /// Our features
 | 
			
		||||
  final List<String> _features;
 | 
			
		||||
 | 
			
		||||
  /// Our features
 | 
			
		||||
  final List<String> _features = List.empty(growable: true);
 | 
			
		||||
 | 
			
		||||
  /// Disco identities that we advertise
 | 
			
		||||
  final List<Identity> _identities;
 | 
			
		||||
  
 | 
			
		||||
  /// Map full JID to Capability hashes
 | 
			
		||||
  final Map<String, CapabilityHashInfo> _capHashCache;
 | 
			
		||||
  final Map<String, CapabilityHashInfo> _capHashCache = {};
 | 
			
		||||
 | 
			
		||||
  /// Map capability hash to the disco info
 | 
			
		||||
  final Map<String, DiscoInfo> _capHashInfoCache;
 | 
			
		||||
  final Map<String, DiscoInfo> _capHashInfoCache = {};
 | 
			
		||||
 | 
			
		||||
  /// 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
 | 
			
		||||
  final Map<DiscoCacheKey, List<Completer<Result<DiscoError, DiscoInfo>>>> _runningInfoQueries;
 | 
			
		||||
  final Map<DiscoCacheKey, List<Completer<Result<DiscoError, DiscoInfo>>>> _runningInfoQueries = {};
 | 
			
		||||
 | 
			
		||||
  /// Cache lock
 | 
			
		||||
  final Lock _cacheLock;
 | 
			
		||||
  final Lock _cacheLock = Lock();
 | 
			
		||||
 | 
			
		||||
  /// 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
 | 
			
		||||
  bool hasInfoQueriesRunning() => _runningInfoQueries.isNotEmpty;
 | 
			
		||||
 | 
			
		||||
@ -105,10 +125,20 @@ 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.
 | 
			
		||||
  /// This function only adds features that are not already present in the disco features.
 | 
			
		||||
  void addDiscoFeatures(List<String> features) {
 | 
			
		||||
  void addFeatures(List<String> features) {
 | 
			
		||||
    for (final feat in features) {
 | 
			
		||||
      if (!_features.contains(feat)) {
 | 
			
		||||
        _features.add(feat);
 | 
			
		||||
@ -116,6 +146,16 @@ 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 {
 | 
			
		||||
    final c = presence.firstTag('c', xmlns: capsXmlns);
 | 
			
		||||
    if (c == null) return;
 | 
			
		||||
@ -146,45 +186,33 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /// Returns the list of disco features registered.
 | 
			
		||||
  List<String> getRegisteredDiscoFeatures() => _features;
 | 
			
		||||
  
 | 
			
		||||
  /// May be overriden. Specifies the identities which will be returned in a disco info response.
 | 
			
		||||
  List<Identity> getIdentities() => const [ Identity(category: 'client', type: 'pc', name: 'moxxmpp', lang: 'en') ];
 | 
			
		||||
  /// Returns the [DiscoInfo] object that would be used as the response to a disco#info
 | 
			
		||||
  /// query against our bare JID with no node. The results node attribute is set
 | 
			
		||||
  /// to [node].
 | 
			
		||||
  DiscoInfo getDiscoInfo(String? node) {
 | 
			
		||||
    return DiscoInfo(
 | 
			
		||||
      _features,
 | 
			
		||||
      _identities,
 | 
			
		||||
      const [],
 | 
			
		||||
      node,
 | 
			
		||||
      null,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<StanzaHandlerData> _onDiscoInfoRequest(Stanza stanza, StanzaHandlerData state) async {
 | 
			
		||||
    if (stanza.type != 'get') return state;
 | 
			
		||||
 | 
			
		||||
    final presence = getAttributes().getManagerById(presenceManager)! as PresenceManager;
 | 
			
		||||
    final query = stanza.firstTag('query', xmlns: discoInfoXmlns)!;
 | 
			
		||||
    final node = query.attributes['node'] as String?;
 | 
			
		||||
    final capHash = await presence.getCapabilityHash();
 | 
			
		||||
    final isCapabilityNode = node == '${presence.capabilityHashNode}#$capHash';
 | 
			
		||||
 | 
			
		||||
    if (!isCapabilityNode && node != null) {
 | 
			
		||||
    if (_discoInfoCallbacks.containsKey(node)) {
 | 
			
		||||
      // We can now assume that node != null
 | 
			
		||||
      final result = await _discoInfoCallbacks[node]!();
 | 
			
		||||
      await reply(
 | 
			
		||||
        state,
 | 
			
		||||
        'error',
 | 
			
		||||
        'result',
 | 
			
		||||
        [
 | 
			
		||||
          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,
 | 
			
		||||
              )
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          result.toXml(),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@ -195,24 +223,7 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
      state,
 | 
			
		||||
      'result',
 | 
			
		||||
      [
 | 
			
		||||
        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 },
 | 
			
		||||
              );
 | 
			
		||||
            }),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        getDiscoInfo(node).toXml(),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -223,30 +234,20 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
    if (stanza.type != 'get') return state;
 | 
			
		||||
 | 
			
		||||
    final query = stanza.firstTag('query', xmlns: discoItemsXmlns)!;
 | 
			
		||||
    if (query.attributes['node'] != null) {
 | 
			
		||||
      // TODO(Unknown): Handle the node we specified for XEP-0115
 | 
			
		||||
    final node = query.attributes['node'] as String?;
 | 
			
		||||
    if (_discoItemsCallbacks.containsKey(node)) {
 | 
			
		||||
      final result = await _discoItemsCallbacks[node]!();
 | 
			
		||||
      await reply(
 | 
			
		||||
        state,
 | 
			
		||||
        'error',
 | 
			
		||||
        'result',
 | 
			
		||||
        [
 | 
			
		||||
          XMLNode.xmlns(
 | 
			
		||||
            tag: 'query',
 | 
			
		||||
            xmlns: discoItemsXmlns,
 | 
			
		||||
            attributes: <String, String>{
 | 
			
		||||
              'node': query.attributes['node']! as String,
 | 
			
		||||
              'node': node!,
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          XMLNode(
 | 
			
		||||
            tag: 'error',
 | 
			
		||||
            attributes: <String, dynamic>{
 | 
			
		||||
              'type': 'cancel'
 | 
			
		||||
            },
 | 
			
		||||
            children: [
 | 
			
		||||
              XMLNode.xmlns(
 | 
			
		||||
                tag: 'not-allowed',
 | 
			
		||||
                xmlns: fullStanzaXmlns,
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
            children: result.map((item) => item.toXml()).toList(),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
@ -254,18 +255,7 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
      return state.copyWith(done: true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await reply(
 | 
			
		||||
      state,
 | 
			
		||||
      'result',
 | 
			
		||||
      [
 | 
			
		||||
        XMLNode.xmlns(
 | 
			
		||||
          tag: 'query',
 | 
			
		||||
          xmlns: discoItemsXmlns,
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _exitDiscoInfoCriticalSection(DiscoCacheKey key, Result<DiscoError, DiscoInfo> result) async {
 | 
			
		||||
@ -322,34 +312,17 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
      return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final error = stanza.firstTag('error');
 | 
			
		||||
    if (error != null && stanza.attributes['type'] == 'error') {
 | 
			
		||||
    if (stanza.attributes['type'] == 'error') {
 | 
			
		||||
      //final error = stanza.firstTag('error');
 | 
			
		||||
      final result = Result<DiscoError, DiscoInfo>(ErrorResponseDiscoError());
 | 
			
		||||
      await _exitDiscoInfoCriticalSection(cacheKey, 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>(
 | 
			
		||||
      DiscoInfo(
 | 
			
		||||
        features,
 | 
			
		||||
        identities,
 | 
			
		||||
        query.findTags('x', xmlns: dataFormsXmlns).map(parseDataForm).toList(),
 | 
			
		||||
        JID.fromString(stanza.attributes['from']! as String),
 | 
			
		||||
      DiscoInfo.fromQuery(
 | 
			
		||||
        query,
 | 
			
		||||
        JID.fromString(entity),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    await _exitDiscoInfoCriticalSection(cacheKey, result);
 | 
			
		||||
@ -367,8 +340,8 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
    final query = stanza.firstTag('query');
 | 
			
		||||
    if (query == null) return Result(InvalidResponseDiscoError());
 | 
			
		||||
 | 
			
		||||
    final error = stanza.firstTag('error');
 | 
			
		||||
    if (error != null && stanza.type == 'error') {
 | 
			
		||||
    if (stanza.type == 'error') {
 | 
			
		||||
      //final error = stanza.firstTag('error');
 | 
			
		||||
      //print("Disco Items error: " + error.toXml());
 | 
			
		||||
      return Result(ErrorResponseDiscoError());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,18 @@
 | 
			
		||||
import 'dart:convert';
 | 
			
		||||
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/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_0414.dart';
 | 
			
		||||
 | 
			
		||||
@immutable
 | 
			
		||||
class CapabilityHashInfo {
 | 
			
		||||
 | 
			
		||||
  const CapabilityHashInfo(this.ver, this.node, this.hash);
 | 
			
		||||
  final String ver;
 | 
			
		||||
  final String node;
 | 
			
		||||
@ -57,3 +65,86 @@ Future<String> calculateCapabilityHash(DiscoInfo info, HashAlgorithm algorithm)
 | 
			
		||||
  
 | 
			
		||||
  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,
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -63,10 +63,11 @@ void main() {
 | 
			
		||||
      StubNegotiator2(),
 | 
			
		||||
    ])
 | 
			
		||||
    ..registerManagers([
 | 
			
		||||
      PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
      PresenceManager(),
 | 
			
		||||
      RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
      DiscoManager(),
 | 
			
		||||
      DiscoManager([]),
 | 
			
		||||
      PingManager(),
 | 
			
		||||
      EntityCapabilitiesManager('http://moxxmpp.example'),
 | 
			
		||||
    ])
 | 
			
		||||
    ..setConnectionSettings(
 | 
			
		||||
      ConnectionSettings(
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,7 @@ void main() {
 | 
			
		||||
          ignoreId: true,
 | 
			
		||||
        ),
 | 
			
		||||
        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='QRTBC5cg/oYd+UOTYazSQR4zb/I=' /></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='3QvQ2RAy45XBDhArjxy/vEWMl+E=' /></presence>",
 | 
			
		||||
          '',
 | 
			
		||||
        ),
 | 
			
		||||
        StanzaExpectation(
 | 
			
		||||
@ -77,10 +77,11 @@ void main() {
 | 
			
		||||
        allowPlainAuth: true,
 | 
			
		||||
    ),);
 | 
			
		||||
    conn.registerManagers([
 | 
			
		||||
      PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
      PresenceManager(),
 | 
			
		||||
      RosterManager(TestingRosterStateManager(null, [])),
 | 
			
		||||
      DiscoManager(),
 | 
			
		||||
      DiscoManager([]),
 | 
			
		||||
      PingManager(),
 | 
			
		||||
      EntityCapabilitiesManager('http://moxxmpp.example'),
 | 
			
		||||
    ]);
 | 
			
		||||
    conn.registerFeatureNegotiators(
 | 
			
		||||
      [
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ void main() {
 | 
			
		||||
        )
 | 
			
		||||
      ],
 | 
			
		||||
      [],
 | 
			
		||||
      null,
 | 
			
		||||
      JID.fromString('some@user.local/test'),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -50,6 +51,7 @@ void main() {
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
      [ parseDataForm(XMLNode.fromString(extDiscoDataString)) ],
 | 
			
		||||
      null,
 | 
			
		||||
      JID.fromString('some@user.local/test'),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -158,6 +160,7 @@ void main() {
 | 
			
		||||
        )
 | 
			
		||||
      ],
 | 
			
		||||
      [],
 | 
			
		||||
      null,
 | 
			
		||||
      JID.fromString('user@server.local/test'),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -246,12 +246,13 @@ void main() {
 | 
			
		||||
      ),);
 | 
			
		||||
      final sm = StreamManagementManager();
 | 
			
		||||
      conn.registerManagers([
 | 
			
		||||
          PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
          RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
          DiscoManager(),
 | 
			
		||||
          PingManager(),
 | 
			
		||||
          sm,
 | 
			
		||||
          CarbonsManager()..forceEnable(),
 | 
			
		||||
        PresenceManager(),
 | 
			
		||||
        RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
        DiscoManager([]),
 | 
			
		||||
        PingManager(),
 | 
			
		||||
        sm,
 | 
			
		||||
        CarbonsManager()..forceEnable(),
 | 
			
		||||
        EntityCapabilitiesManager('http://moxxmpp.example'),
 | 
			
		||||
      ]);
 | 
			
		||||
      conn.registerFeatureNegotiators(
 | 
			
		||||
        [
 | 
			
		||||
@ -347,7 +348,7 @@ void main() {
 | 
			
		||||
            '<enabled xmlns="urn:xmpp:sm:3" id="some-long-sm-id" resume="true" />',
 | 
			
		||||
          ),
 | 
			
		||||
          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='QRTBC5cg/oYd+UOTYazSQR4zb/I=' /></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='3QvQ2RAy45XBDhArjxy/vEWMl+E=' /></presence>",
 | 
			
		||||
            '<iq type="result" />',
 | 
			
		||||
          ),
 | 
			
		||||
          StanzaExpectation(
 | 
			
		||||
@ -372,12 +373,13 @@ void main() {
 | 
			
		||||
      ),);
 | 
			
		||||
      final sm = StreamManagementManager();
 | 
			
		||||
      conn.registerManagers([
 | 
			
		||||
          PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
          PresenceManager(),
 | 
			
		||||
          RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
          DiscoManager(),
 | 
			
		||||
          DiscoManager([]),
 | 
			
		||||
          PingManager(),
 | 
			
		||||
          sm,
 | 
			
		||||
          CarbonsManager()..forceEnable(),
 | 
			
		||||
          EntityCapabilitiesManager('http://moxxmpp.example'),
 | 
			
		||||
      ]);
 | 
			
		||||
      conn.registerFeatureNegotiators(
 | 
			
		||||
        [
 | 
			
		||||
@ -530,9 +532,9 @@ void main() {
 | 
			
		||||
          allowPlainAuth: true,
 | 
			
		||||
      ),);
 | 
			
		||||
      conn.registerManagers([
 | 
			
		||||
          PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
          PresenceManager(),
 | 
			
		||||
          RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
          DiscoManager(),
 | 
			
		||||
          DiscoManager([]),
 | 
			
		||||
          PingManager(),
 | 
			
		||||
          StreamManagementManager(),
 | 
			
		||||
      ]);
 | 
			
		||||
@ -626,9 +628,9 @@ void main() {
 | 
			
		||||
          allowPlainAuth: true,
 | 
			
		||||
      ),);
 | 
			
		||||
      conn.registerManagers([
 | 
			
		||||
          PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
          PresenceManager(),
 | 
			
		||||
          RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
          DiscoManager(),
 | 
			
		||||
          DiscoManager([]),
 | 
			
		||||
          PingManager(),
 | 
			
		||||
          StreamManagementManager(),
 | 
			
		||||
      ]);
 | 
			
		||||
@ -722,9 +724,9 @@ void main() {
 | 
			
		||||
          allowPlainAuth: true,
 | 
			
		||||
      ),);
 | 
			
		||||
      conn.registerManagers([
 | 
			
		||||
          PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
          PresenceManager(),
 | 
			
		||||
          RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
          DiscoManager(),
 | 
			
		||||
          DiscoManager([]),
 | 
			
		||||
          PingManager(),
 | 
			
		||||
          StreamManagementManager(),
 | 
			
		||||
      ]);
 | 
			
		||||
 | 
			
		||||
@ -129,11 +129,12 @@ void main() {
 | 
			
		||||
          allowPlainAuth: true,
 | 
			
		||||
      ),);
 | 
			
		||||
      conn.registerManagers([
 | 
			
		||||
        PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
        PresenceManager(),
 | 
			
		||||
        RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
        DiscoManager(),
 | 
			
		||||
        DiscoManager([]),
 | 
			
		||||
        PingManager(),
 | 
			
		||||
        StreamManagementManager(),
 | 
			
		||||
        EntityCapabilitiesManager('http://moxxmpp.example'),
 | 
			
		||||
      ]);
 | 
			
		||||
      conn.registerFeatureNegotiators(
 | 
			
		||||
        [
 | 
			
		||||
@ -187,10 +188,11 @@ void main() {
 | 
			
		||||
        allowPlainAuth: true,
 | 
			
		||||
      ),);
 | 
			
		||||
      conn.registerManagers([
 | 
			
		||||
        PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
        PresenceManager(),
 | 
			
		||||
        RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
        DiscoManager(),
 | 
			
		||||
        DiscoManager([]),
 | 
			
		||||
        PingManager(),
 | 
			
		||||
        EntityCapabilitiesManager('http://moxxmpp.example'),
 | 
			
		||||
      ]);
 | 
			
		||||
      conn.registerFeatureNegotiators([
 | 
			
		||||
        SaslPlainNegotiator()
 | 
			
		||||
@ -245,10 +247,11 @@ void main() {
 | 
			
		||||
          allowPlainAuth: true,
 | 
			
		||||
      ),);
 | 
			
		||||
      conn.registerManagers([
 | 
			
		||||
        PresenceManager('http://moxxmpp.example'),
 | 
			
		||||
        PresenceManager(),
 | 
			
		||||
        RosterManager(TestingRosterStateManager('', [])),
 | 
			
		||||
        DiscoManager(),
 | 
			
		||||
        DiscoManager([]),
 | 
			
		||||
        PingManager(),
 | 
			
		||||
        EntityCapabilitiesManager('http://moxxmpp.example'),
 | 
			
		||||
      ]);
 | 
			
		||||
      conn.registerFeatureNegotiators([
 | 
			
		||||
        SaslPlainNegotiator()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user