feat(all): Move all managers to the new data system
This commit is contained in:
		
							parent
							
								
									8270185027
								
							
						
					
					
						commit
						79d7e3ba64
					
				@ -27,7 +27,9 @@ import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
import 'package:moxxmpp/src/types/result.dart';
 | 
			
		||||
import 'package:moxxmpp/src/util/queue.dart';
 | 
			
		||||
import 'package:moxxmpp/src/util/typed_map.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0198/types.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0198/xep_0198.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0352.dart';
 | 
			
		||||
import 'package:synchronized/synchronized.dart';
 | 
			
		||||
@ -474,8 +476,8 @@ class XmppConnection {
 | 
			
		||||
      initial: StanzaHandlerData(
 | 
			
		||||
        false,
 | 
			
		||||
        false,
 | 
			
		||||
        null,
 | 
			
		||||
        newStanza,
 | 
			
		||||
        TypedMap(),
 | 
			
		||||
        encrypted: details.encrypted,
 | 
			
		||||
        forceEncryption: details.forceEncryption,
 | 
			
		||||
      ),
 | 
			
		||||
@ -531,14 +533,15 @@ class XmppConnection {
 | 
			
		||||
 | 
			
		||||
    // Run post-send handlers
 | 
			
		||||
    _log.fine('Running post stanza handlers..');
 | 
			
		||||
    final extensions = TypedMap()
 | 
			
		||||
      ..set(StreamManagementData(details.excludeFromStreamManagement));
 | 
			
		||||
    await _runOutgoingPostStanzaHandlers(
 | 
			
		||||
      newStanza,
 | 
			
		||||
      initial: StanzaHandlerData(
 | 
			
		||||
        false,
 | 
			
		||||
        false,
 | 
			
		||||
        null,
 | 
			
		||||
        newStanza,
 | 
			
		||||
        excludeFromStreamManagement: details.excludeFromStreamManagement,
 | 
			
		||||
        extensions,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    _log.fine('Done');
 | 
			
		||||
@ -653,7 +656,7 @@ class XmppConnection {
 | 
			
		||||
    Stanza stanza, {
 | 
			
		||||
    StanzaHandlerData? initial,
 | 
			
		||||
  }) async {
 | 
			
		||||
    var state = initial ?? StanzaHandlerData(false, false, null, stanza);
 | 
			
		||||
    var state = initial ?? StanzaHandlerData(false, false, stanza, TypedMap());
 | 
			
		||||
    for (final handler in handlers) {
 | 
			
		||||
      if (handler.matches(state.stanza)) {
 | 
			
		||||
        state = await handler.callback(state.stanza, state);
 | 
			
		||||
@ -728,7 +731,7 @@ class XmppConnection {
 | 
			
		||||
    // it.
 | 
			
		||||
    final incomingPreHandlers = await _runIncomingPreStanzaHandlers(stanza);
 | 
			
		||||
    final prefix = incomingPreHandlers.encrypted &&
 | 
			
		||||
            incomingPreHandlers.other['encryption_error'] == null
 | 
			
		||||
            incomingPreHandlers.encryptionError == null
 | 
			
		||||
        ? '(Encrypted) '
 | 
			
		||||
        : '';
 | 
			
		||||
    _log.finest('<== $prefix${incomingPreHandlers.stanza.toXml()}');
 | 
			
		||||
@ -747,10 +750,10 @@ class XmppConnection {
 | 
			
		||||
      initial: StanzaHandlerData(
 | 
			
		||||
        false,
 | 
			
		||||
        incomingPreHandlers.cancel,
 | 
			
		||||
        incomingPreHandlers.cancelReason,
 | 
			
		||||
        incomingPreHandlers.stanza,
 | 
			
		||||
        incomingPreHandlers.extensions,
 | 
			
		||||
        encrypted: incomingPreHandlers.encrypted,
 | 
			
		||||
        other: incomingPreHandlers.other,
 | 
			
		||||
        cancelReason: incomingPreHandlers.cancelReason,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    if (!incomingHandlers.done) {
 | 
			
		||||
 | 
			
		||||
@ -1,82 +1,45 @@
 | 
			
		||||
import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0066.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0085.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0203.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0380.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0385.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0424.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0444.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0461.dart';
 | 
			
		||||
import 'package:moxxmpp/src/util/typed_map.dart';
 | 
			
		||||
 | 
			
		||||
part 'data.freezed.dart';
 | 
			
		||||
class StanzaHandlerData {
 | 
			
		||||
  StanzaHandlerData(
 | 
			
		||||
    this.done,
 | 
			
		||||
    this.cancel,
 | 
			
		||||
    this.stanza,
 | 
			
		||||
    this.extensions, {
 | 
			
		||||
    this.cancelReason,
 | 
			
		||||
    this.encryptionError,
 | 
			
		||||
    this.encrypted = false,
 | 
			
		||||
    this.forceEncryption = false,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
class StanzaHandlerData with _$StanzaHandlerData {
 | 
			
		||||
  factory StanzaHandlerData(
 | 
			
		||||
    // Indicates to the runner that processing is now done. This means that all
 | 
			
		||||
    // pre-processing is done and no other handlers should be consulted.
 | 
			
		||||
    bool done,
 | 
			
		||||
    // Indicates to the runner that processing is to be cancelled and no further handlers
 | 
			
		||||
    // should run. The stanza also will not be sent.
 | 
			
		||||
    bool cancel,
 | 
			
		||||
    // The reason why we cancelled the processing and sending
 | 
			
		||||
    dynamic cancelReason,
 | 
			
		||||
    // The stanza that is being dealt with. SHOULD NOT be overwritten, unless it is absolutely
 | 
			
		||||
    // necessary, e.g. with Message Carbons or OMEMO
 | 
			
		||||
    Stanza stanza, {
 | 
			
		||||
    // Whether the stanza is retransmitted. Only useful in the context of outgoing
 | 
			
		||||
    // stanza handlers. MUST NOT be overwritten.
 | 
			
		||||
    @Default(false) bool retransmitted,
 | 
			
		||||
    StatelessMediaSharingData? sims,
 | 
			
		||||
    StatelessFileSharingData? sfs,
 | 
			
		||||
    OOBData? oob,
 | 
			
		||||
  /// Indicates to the runner that processing is now done. This means that all
 | 
			
		||||
  /// pre-processing is done and no other handlers should be consulted.
 | 
			
		||||
  bool done;
 | 
			
		||||
 | 
			
		||||
    // XEP-0359 <origin-id />'s id attribute, if available.
 | 
			
		||||
    String? originId,
 | 
			
		||||
  /// Indicates to the runner that processing is to be cancelled and no further handlers
 | 
			
		||||
  /// should run. The stanza also will not be sent.
 | 
			
		||||
  bool cancel;
 | 
			
		||||
 | 
			
		||||
    // XEP-0359 <stanza-id /> elements, if available.
 | 
			
		||||
    List<StanzaId>? stanzaIds,
 | 
			
		||||
    ReplyData? reply,
 | 
			
		||||
    ChatState? chatState,
 | 
			
		||||
    @Default(false) bool isCarbon,
 | 
			
		||||
    @Default(false) bool deliveryReceiptRequested,
 | 
			
		||||
    @Default(false) bool isMarkable,
 | 
			
		||||
    // File Upload Notifications
 | 
			
		||||
    // A notification
 | 
			
		||||
    FileMetadataData? fun,
 | 
			
		||||
    // The stanza id this replaces
 | 
			
		||||
    String? funReplacement,
 | 
			
		||||
    // The stanza id this cancels
 | 
			
		||||
    String? funCancellation,
 | 
			
		||||
    // Whether the stanza was received encrypted
 | 
			
		||||
    @Default(false) bool encrypted,
 | 
			
		||||
    // If true, forces the encryption manager to encrypt to the JID, even if it
 | 
			
		||||
    // would not normally. In the case of OMEMO: If shouldEncrypt returns false
 | 
			
		||||
    // but forceEncryption is true, then the OMEMO manager will try to encrypt
 | 
			
		||||
    // to the JID anyway.
 | 
			
		||||
    @Default(false) bool forceEncryption,
 | 
			
		||||
    // The stated type of encryption used, if any was used
 | 
			
		||||
    ExplicitEncryptionType? encryptionType,
 | 
			
		||||
    // Delayed Delivery
 | 
			
		||||
    DelayedDelivery? delayedDelivery,
 | 
			
		||||
    // This is for stanza handlers that are not part of the XMPP library but still need
 | 
			
		||||
    // pass data around.
 | 
			
		||||
    @Default(<String, dynamic>{}) Map<String, dynamic> other,
 | 
			
		||||
    // If non-null, then it indicates the origin Id of the message that should be
 | 
			
		||||
    // retracted
 | 
			
		||||
    MessageRetractionData? messageRetraction,
 | 
			
		||||
    // If non-null, then the message is a correction for the specified stanza Id
 | 
			
		||||
    String? lastMessageCorrectionSid,
 | 
			
		||||
    // Reactions data
 | 
			
		||||
    MessageReactions? messageReactions,
 | 
			
		||||
    // The Id of the sticker pack this sticker belongs to
 | 
			
		||||
    String? stickerPackId,
 | 
			
		||||
    // Flag indicating whether the stanza should be excluded from stream management's
 | 
			
		||||
    // resending behaviour
 | 
			
		||||
    @Default(false) bool excludeFromStreamManagement,
 | 
			
		||||
  }) = _StanzaHandlerData;
 | 
			
		||||
  /// The reason why we cancelled the processing and sending.
 | 
			
		||||
  Object? cancelReason;
 | 
			
		||||
 | 
			
		||||
  /// The reason why an encryption or decryption failed.
 | 
			
		||||
  Object? encryptionError;
 | 
			
		||||
 | 
			
		||||
  /// The stanza that is being dealt with. SHOULD NOT be overwritten, unless it is
 | 
			
		||||
  /// absolutely necessary, e.g. with Message Carbons or OMEMO.
 | 
			
		||||
  Stanza stanza;
 | 
			
		||||
 | 
			
		||||
  /// Whether the stanza was received encrypted
 | 
			
		||||
  bool encrypted;
 | 
			
		||||
 | 
			
		||||
  // If true, forces the encryption manager to encrypt to the JID, even if it
 | 
			
		||||
  // would not normally. In the case of OMEMO: If shouldEncrypt returns false
 | 
			
		||||
  // but forceEncryption is true, then the OMEMO manager will try to encrypt
 | 
			
		||||
  // to the JID anyway.
 | 
			
		||||
  bool forceEncryption;
 | 
			
		||||
 | 
			
		||||
  /// Additional data from other managers.
 | 
			
		||||
  final TypedMap extensions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,15 +12,18 @@ import 'package:moxxmpp/src/xeps/staging/file_upload_notification.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0066.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0085.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0184.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0280.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0308.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0333.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0334.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0385.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0424.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0444.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0448.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0449.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0461.dart';
 | 
			
		||||
 | 
			
		||||
/// Data used to build a message stanza.
 | 
			
		||||
@ -100,7 +103,7 @@ class MessageManager extends XmppManagerBase {
 | 
			
		||||
    final hints = List<MessageProcessingHint>.empty(growable: true);
 | 
			
		||||
    for (final element
 | 
			
		||||
        in message.findTagsByXmlns(messageProcessingHintsXmlns)) {
 | 
			
		||||
      hints.add(messageProcessingHintFromXml(element));
 | 
			
		||||
      hints.add(MessageProcessingHint.fromName(element.tag));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getAttributes().sendEvent(
 | 
			
		||||
@ -109,32 +112,37 @@ class MessageManager extends XmppManagerBase {
 | 
			
		||||
        fromJid: JID.fromString(message.attributes['from']! as String),
 | 
			
		||||
        toJid: JID.fromString(message.attributes['to']! as String),
 | 
			
		||||
        sid: message.attributes['id']! as String,
 | 
			
		||||
        originId: state.originId,
 | 
			
		||||
        stanzaIds: state.stanzaIds,
 | 
			
		||||
        isCarbon: state.isCarbon,
 | 
			
		||||
        deliveryReceiptRequested: state.deliveryReceiptRequested,
 | 
			
		||||
        isMarkable: state.isMarkable,
 | 
			
		||||
        originId: state.extensions.get<StableIdData>()?.originId,
 | 
			
		||||
        stanzaIds: state.extensions.get<StableIdData>()?.stanzaIds,
 | 
			
		||||
        isCarbon: state.extensions.get<CarbonsData>()?.isCarbon ?? false,
 | 
			
		||||
        deliveryReceiptRequested: state.extensions
 | 
			
		||||
                .get<MessageDeliveryReceiptData>()
 | 
			
		||||
                ?.receiptRequested ??
 | 
			
		||||
            false,
 | 
			
		||||
        isMarkable: state.extensions.get<ChatMarkerData>()?.isMarkable ?? false,
 | 
			
		||||
        type: message.attributes['type'] as String?,
 | 
			
		||||
        oob: state.oob,
 | 
			
		||||
        sfs: state.sfs,
 | 
			
		||||
        sims: state.sims,
 | 
			
		||||
        reply: state.reply,
 | 
			
		||||
        chatState: state.chatState,
 | 
			
		||||
        fun: state.fun,
 | 
			
		||||
        funReplacement: state.funReplacement,
 | 
			
		||||
        funCancellation: state.funCancellation,
 | 
			
		||||
        oob: state.extensions.get<OOBData>(),
 | 
			
		||||
        sfs: state.extensions.get<StatelessFileSharingData>(),
 | 
			
		||||
        sims: state.extensions.get<StatelessMediaSharingData>(),
 | 
			
		||||
        reply: state.extensions.get<ReplyData>(),
 | 
			
		||||
        chatState: state.extensions.get<ChatState>(),
 | 
			
		||||
        fun: state.extensions.get<FileUploadNotificationData>()?.metadata,
 | 
			
		||||
        funReplacement:
 | 
			
		||||
            state.extensions.get<FileUploadNotificationReplacementData>()?.id,
 | 
			
		||||
        funCancellation:
 | 
			
		||||
            state.extensions.get<FileUploadNotificationCancellationData>()?.id,
 | 
			
		||||
        encrypted: state.encrypted,
 | 
			
		||||
        messageRetraction: state.messageRetraction,
 | 
			
		||||
        messageCorrectionId: state.lastMessageCorrectionSid,
 | 
			
		||||
        messageReactions: state.messageReactions,
 | 
			
		||||
        messageRetraction: state.extensions.get<MessageRetractionData>(),
 | 
			
		||||
        messageCorrectionId: state.extensions.get<MessageRetractionData>()?.id,
 | 
			
		||||
        messageReactions: state.extensions.get<MessageReactions>(),
 | 
			
		||||
        messageProcessingHints: hints.isEmpty ? null : hints,
 | 
			
		||||
        stickerPackId: state.stickerPackId,
 | 
			
		||||
        other: state.other,
 | 
			
		||||
        stickerPackId: state.extensions.get<StickersData>()?.stickerPackId,
 | 
			
		||||
        other: {},
 | 
			
		||||
        error: StanzaError.fromStanza(message),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Send a message to to with the content body. If deliveryRequest is true, then
 | 
			
		||||
@ -142,7 +150,7 @@ class MessageManager extends XmppManagerBase {
 | 
			
		||||
  /// If id is non-null, then it will be the id of the message stanza.
 | 
			
		||||
  /// element to this id. If originId is non-null, then it will create an "origin-id"
 | 
			
		||||
  /// child in the message stanza and set its id to originId.
 | 
			
		||||
  void sendMessage(MessageDetails details) {
 | 
			
		||||
  Future<void> sendMessage(MessageDetails details) async {
 | 
			
		||||
    assert(
 | 
			
		||||
      implies(
 | 
			
		||||
        details.quoteBody != null,
 | 
			
		||||
@ -216,7 +224,7 @@ class MessageManager extends XmppManagerBase {
 | 
			
		||||
      stanza.addChild(makeChatMarkerMarkable());
 | 
			
		||||
    }
 | 
			
		||||
    if (details.originId != null) {
 | 
			
		||||
      stanza.addChild(makeOriginIdElement(details.originId!));
 | 
			
		||||
      stanza.addChild(StableIdData(details.originId, null).toOriginIdElement());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (details.sfs != null) {
 | 
			
		||||
@ -226,17 +234,13 @@ class MessageManager extends XmppManagerBase {
 | 
			
		||||
      if (source is StatelessFileSharingUrlSource &&
 | 
			
		||||
          details.setOOBFallbackBody) {
 | 
			
		||||
        // SFS recommends OOB as a fallback
 | 
			
		||||
        stanza.addChild(constructOOBNode(OOBData(url: source.url)));
 | 
			
		||||
        stanza.addChild(OOBData(source.url, null).toXML());
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (details.chatState != null) {
 | 
			
		||||
      stanza.addChild(
 | 
			
		||||
        // TODO(Unknown): Move this into xep_0085.dart
 | 
			
		||||
        XMLNode.xmlns(
 | 
			
		||||
          tag: chatStateToString(details.chatState!),
 | 
			
		||||
          xmlns: chatStateXmlns,
 | 
			
		||||
        ),
 | 
			
		||||
        details.chatState!.toXML(),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -293,9 +297,9 @@ class MessageManager extends XmppManagerBase {
 | 
			
		||||
 | 
			
		||||
    if (details.lastMessageCorrectionId != null) {
 | 
			
		||||
      stanza.addChild(
 | 
			
		||||
        makeLastMessageCorrectionEdit(
 | 
			
		||||
        LastMessageCorrectionData(
 | 
			
		||||
          details.lastMessageCorrectionId!,
 | 
			
		||||
        ),
 | 
			
		||||
        ).toXML(),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -305,7 +309,7 @@ class MessageManager extends XmppManagerBase {
 | 
			
		||||
 | 
			
		||||
    if (details.messageProcessingHints != null) {
 | 
			
		||||
      for (final hint in details.messageProcessingHints!) {
 | 
			
		||||
        stanza.addChild(hint.toXml());
 | 
			
		||||
        stanza.addChild(hint.toXML());
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -321,7 +325,7 @@ class MessageManager extends XmppManagerBase {
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getAttributes().sendStanza(
 | 
			
		||||
    await getAttributes().sendStanza(
 | 
			
		||||
      StanzaDetails(
 | 
			
		||||
        stanza,
 | 
			
		||||
        awaitable: false,
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ class PresenceManager extends XmppManagerBase {
 | 
			
		||||
              from: JID.fromString(presence.from!),
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
          return state.copyWith(done: true);
 | 
			
		||||
          return state..done = true;
 | 
			
		||||
        }
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
@ -78,7 +78,7 @@ class PresenceManager extends XmppManagerBase {
 | 
			
		||||
      getAttributes().sendEvent(
 | 
			
		||||
        PresenceReceivedEvent(JID.fromString(presence.from!), presence),
 | 
			
		||||
      );
 | 
			
		||||
      return state.copyWith(done: true);
 | 
			
		||||
      return state..done = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state;
 | 
			
		||||
 | 
			
		||||
@ -145,7 +145,7 @@ class RosterManager extends XmppManagerBase {
 | 
			
		||||
      logger.warning(
 | 
			
		||||
        'Roster push invalid! Unexpected from attribute: ${stanza.toXml()}',
 | 
			
		||||
      );
 | 
			
		||||
      return state.copyWith(done: true);
 | 
			
		||||
      return state..done = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final query = stanza.firstTag('query', xmlns: rosterXmlns)!;
 | 
			
		||||
@ -154,7 +154,7 @@ class RosterManager extends XmppManagerBase {
 | 
			
		||||
 | 
			
		||||
    if (item == null) {
 | 
			
		||||
      logger.warning('Received empty roster push');
 | 
			
		||||
      return state.copyWith(done: true);
 | 
			
		||||
      return state..done = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unawaited(
 | 
			
		||||
@ -177,7 +177,7 @@ class RosterManager extends XmppManagerBase {
 | 
			
		||||
      [],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Shared code between requesting rosters without and with roster versioning, if
 | 
			
		||||
 | 
			
		||||
@ -7,9 +7,32 @@ import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
 | 
			
		||||
 | 
			
		||||
/// NOTE: Specified by https://github.com/PapaTutuWawa/custom-xeps/blob/master/xep-xxxx-file-upload-notifications.md
 | 
			
		||||
 | 
			
		||||
const fileUploadNotificationXmlns = 'proto:urn:xmpp:fun:0';
 | 
			
		||||
 | 
			
		||||
/// Indicates a file upload notification.
 | 
			
		||||
class FileUploadNotificationData {
 | 
			
		||||
  const FileUploadNotificationData(this.metadata);
 | 
			
		||||
 | 
			
		||||
  /// The file metadata indicated in the upload notification.
 | 
			
		||||
  final FileMetadataData metadata;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Indicates that a file upload has been cancelled.
 | 
			
		||||
class FileUploadNotificationCancellationData {
 | 
			
		||||
  const FileUploadNotificationCancellationData(this.id);
 | 
			
		||||
 | 
			
		||||
  /// The id of the upload notifiaction that is cancelled.
 | 
			
		||||
  final String id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Indicates that a file upload has been completed.
 | 
			
		||||
class FileUploadNotificationReplacementData {
 | 
			
		||||
  const FileUploadNotificationReplacementData(this.id);
 | 
			
		||||
 | 
			
		||||
  /// The id of the upload notifiaction that is replaced.
 | 
			
		||||
  final String id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class FileUploadNotificationManager extends XmppManagerBase {
 | 
			
		||||
  FileUploadNotificationManager() : super(fileUploadNotificationManager);
 | 
			
		||||
 | 
			
		||||
@ -47,11 +70,14 @@ class FileUploadNotificationManager extends XmppManagerBase {
 | 
			
		||||
  ) async {
 | 
			
		||||
    final funElement =
 | 
			
		||||
        message.firstTag('file-upload', xmlns: fileUploadNotificationXmlns)!;
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      fun: FileMetadataData.fromXML(
 | 
			
		||||
        funElement.firstTag('file', xmlns: fileMetadataXmlns)!,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        FileUploadNotificationData(
 | 
			
		||||
          FileMetadataData.fromXML(
 | 
			
		||||
            funElement.firstTag('file', xmlns: fileMetadataXmlns)!,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<StanzaHandlerData> _onFileUploadNotificationReplacementReceived(
 | 
			
		||||
@ -60,9 +86,12 @@ class FileUploadNotificationManager extends XmppManagerBase {
 | 
			
		||||
  ) async {
 | 
			
		||||
    final element =
 | 
			
		||||
        message.firstTag('replaces', xmlns: fileUploadNotificationXmlns)!;
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      funReplacement: element.attributes['id']! as String,
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        FileUploadNotificationReplacementData(
 | 
			
		||||
          element.attributes['id']! as String,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<StanzaHandlerData> _onFileUploadNotificationCancellationReceived(
 | 
			
		||||
@ -71,8 +100,11 @@ class FileUploadNotificationManager extends XmppManagerBase {
 | 
			
		||||
  ) async {
 | 
			
		||||
    final element =
 | 
			
		||||
        message.firstTag('cancels', xmlns: fileUploadNotificationXmlns)!;
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      funCancellation: element.attributes['id']! as String,
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        FileUploadNotificationCancellationData(
 | 
			
		||||
          element.attributes['id']! as String,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -184,7 +184,7 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return state.copyWith(done: true);
 | 
			
		||||
      return state..done = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await reply(
 | 
			
		||||
@ -195,7 +195,7 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<StanzaHandlerData> _onDiscoItemsRequest(
 | 
			
		||||
@ -223,7 +223,7 @@ class DiscoManager extends XmppManagerBase {
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return state.copyWith(done: true);
 | 
			
		||||
      return state..done = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state;
 | 
			
		||||
 | 
			
		||||
@ -76,7 +76,7 @@ class VCardManager extends XmppManagerBase {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VCardPhoto? _parseVCardPhoto(XMLNode? node) {
 | 
			
		||||
 | 
			
		||||
@ -114,7 +114,7 @@ class PubSubManager extends XmppManagerBase {
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<int> _getNodeItemCount(JID jid, String node) async {
 | 
			
		||||
 | 
			
		||||
@ -8,26 +8,24 @@ import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
 | 
			
		||||
/// A data class representing the jabber:x:oob tag.
 | 
			
		||||
class OOBData {
 | 
			
		||||
  const OOBData({this.url, this.desc});
 | 
			
		||||
  const OOBData(this.url, this.desc);
 | 
			
		||||
 | 
			
		||||
  /// The communicated URL of the OOB data
 | 
			
		||||
  final String? url;
 | 
			
		||||
 | 
			
		||||
  /// The description of the url.
 | 
			
		||||
  final String? desc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XMLNode constructOOBNode(OOBData data) {
 | 
			
		||||
  final children = List<XMLNode>.empty(growable: true);
 | 
			
		||||
 | 
			
		||||
  if (data.url != null) {
 | 
			
		||||
    children.add(XMLNode(tag: 'url', text: data.url));
 | 
			
		||||
  XMLNode toXML() {
 | 
			
		||||
    return XMLNode.xmlns(
 | 
			
		||||
      tag: 'x',
 | 
			
		||||
      xmlns: oobDataXmlns,
 | 
			
		||||
      children: [
 | 
			
		||||
        if (url != null) XMLNode(tag: 'url', text: url),
 | 
			
		||||
        if (desc != null) XMLNode(tag: 'desc', text: desc),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  if (data.desc != null) {
 | 
			
		||||
    children.add(XMLNode(tag: 'desc', text: data.desc));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return XMLNode.xmlns(
 | 
			
		||||
    tag: 'x',
 | 
			
		||||
    xmlns: oobDataXmlns,
 | 
			
		||||
    children: children,
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class OOBManager extends XmppManagerBase {
 | 
			
		||||
@ -59,11 +57,12 @@ class OOBManager extends XmppManagerBase {
 | 
			
		||||
    final url = x.firstTag('url');
 | 
			
		||||
    final desc = x.firstTag('desc');
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      oob: OOBData(
 | 
			
		||||
        url: url?.innerText(),
 | 
			
		||||
        desc: desc?.innerText(),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        OOBData(
 | 
			
		||||
          url?.innerText(),
 | 
			
		||||
          desc?.innerText(),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,39 +6,54 @@ import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
 | 
			
		||||
enum ChatState { active, composing, paused, inactive, gone }
 | 
			
		||||
enum ChatState {
 | 
			
		||||
  active,
 | 
			
		||||
  composing,
 | 
			
		||||
  paused,
 | 
			
		||||
  inactive,
 | 
			
		||||
  gone;
 | 
			
		||||
 | 
			
		||||
ChatState chatStateFromString(String raw) {
 | 
			
		||||
  switch (raw) {
 | 
			
		||||
    case 'active':
 | 
			
		||||
      {
 | 
			
		||||
  factory ChatState.fromString(String state) {
 | 
			
		||||
    switch (state) {
 | 
			
		||||
      case 'active':
 | 
			
		||||
        return ChatState.active;
 | 
			
		||||
      }
 | 
			
		||||
    case 'composing':
 | 
			
		||||
      {
 | 
			
		||||
      case 'composing':
 | 
			
		||||
        return ChatState.composing;
 | 
			
		||||
      }
 | 
			
		||||
    case 'paused':
 | 
			
		||||
      {
 | 
			
		||||
      case 'paused':
 | 
			
		||||
        return ChatState.paused;
 | 
			
		||||
      }
 | 
			
		||||
    case 'inactive':
 | 
			
		||||
      {
 | 
			
		||||
      case 'inactive':
 | 
			
		||||
        return ChatState.inactive;
 | 
			
		||||
      }
 | 
			
		||||
    case 'gone':
 | 
			
		||||
      {
 | 
			
		||||
      case 'gone':
 | 
			
		||||
        return ChatState.gone;
 | 
			
		||||
      }
 | 
			
		||||
    default:
 | 
			
		||||
      {
 | 
			
		||||
      default:
 | 
			
		||||
        return ChatState.gone;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    switch (this) {
 | 
			
		||||
      case ChatState.active:
 | 
			
		||||
        return 'active';
 | 
			
		||||
      case ChatState.composing:
 | 
			
		||||
        return 'composing';
 | 
			
		||||
      case ChatState.paused:
 | 
			
		||||
        return 'paused';
 | 
			
		||||
      case ChatState.inactive:
 | 
			
		||||
        return 'inactive';
 | 
			
		||||
      case ChatState.gone:
 | 
			
		||||
        return 'gone';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  XMLNode toXML() {
 | 
			
		||||
    return XMLNode.xmlns(
 | 
			
		||||
      tag: toString(),
 | 
			
		||||
      xmlns: chatStateXmlns,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String chatStateToString(ChatState state) => state.toString().split('.').last;
 | 
			
		||||
 | 
			
		||||
class ChatStateManager extends XmppManagerBase {
 | 
			
		||||
  ChatStateManager() : super(chatStateManager);
 | 
			
		||||
 | 
			
		||||
@ -64,61 +79,27 @@ class ChatStateManager extends XmppManagerBase {
 | 
			
		||||
    StanzaHandlerData state,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final element = state.stanza.firstTagByXmlns(chatStateXmlns)!;
 | 
			
		||||
    ChatState? chatState;
 | 
			
		||||
 | 
			
		||||
    switch (element.tag) {
 | 
			
		||||
      case 'active':
 | 
			
		||||
        {
 | 
			
		||||
          chatState = ChatState.active;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'composing':
 | 
			
		||||
        {
 | 
			
		||||
          chatState = ChatState.composing;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'paused':
 | 
			
		||||
        {
 | 
			
		||||
          chatState = ChatState.paused;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'inactive':
 | 
			
		||||
        {
 | 
			
		||||
          chatState = ChatState.inactive;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'gone':
 | 
			
		||||
        {
 | 
			
		||||
          chatState = ChatState.gone;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        {
 | 
			
		||||
          logger.warning("Received invalid chat state '${element.tag}'");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(chatState: chatState);
 | 
			
		||||
    state.extensions.set(ChatState.fromString(element.tag));
 | 
			
		||||
    return state;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Send a chat state notification to [to]. You can specify the type attribute
 | 
			
		||||
  /// of the message with [messageType].
 | 
			
		||||
  void sendChatState(
 | 
			
		||||
  Future<void> sendChatState(
 | 
			
		||||
    ChatState state,
 | 
			
		||||
    String to, {
 | 
			
		||||
    String messageType = 'chat',
 | 
			
		||||
  }) {
 | 
			
		||||
    final tagName = state.toString().split('.').last;
 | 
			
		||||
 | 
			
		||||
    getAttributes().sendStanza(
 | 
			
		||||
  }) async {
 | 
			
		||||
    await getAttributes().sendStanza(
 | 
			
		||||
      StanzaDetails(
 | 
			
		||||
        Stanza.message(
 | 
			
		||||
          to: to,
 | 
			
		||||
          type: messageType,
 | 
			
		||||
          children: [
 | 
			
		||||
            XMLNode.xmlns(tag: tagName, xmlns: chatStateXmlns),
 | 
			
		||||
            XMLNode.xmlns(tag: state.toString(), xmlns: chatStateXmlns),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        awaitable: false,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,14 @@ import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
 | 
			
		||||
class MessageDeliveryReceiptData {
 | 
			
		||||
  const MessageDeliveryReceiptData(this.receiptRequested);
 | 
			
		||||
 | 
			
		||||
  /// Indicates whether a delivery receipt is requested or not.
 | 
			
		||||
  final bool receiptRequested;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: Merge those two functions into [MessageDeliveryReceiptData]
 | 
			
		||||
XMLNode makeMessageDeliveryRequest() {
 | 
			
		||||
  return XMLNode.xmlns(
 | 
			
		||||
    tag: 'request',
 | 
			
		||||
@ -56,7 +64,7 @@ class MessageDeliveryReceiptManager extends XmppManagerBase {
 | 
			
		||||
    Stanza message,
 | 
			
		||||
    StanzaHandlerData state,
 | 
			
		||||
  ) async {
 | 
			
		||||
    return state.copyWith(deliveryReceiptRequested: true);
 | 
			
		||||
    return state..extensions.set(const MessageDeliveryReceiptData(true));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<StanzaHandlerData> _onDeliveryReceiptReceived(
 | 
			
		||||
@ -64,16 +72,16 @@ class MessageDeliveryReceiptManager extends XmppManagerBase {
 | 
			
		||||
    StanzaHandlerData state,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final received = message.firstTag('received', xmlns: deliveryXmlns)!;
 | 
			
		||||
    for (final item in message.children) {
 | 
			
		||||
      if (!['origin-id', 'stanza-id', 'delay', 'store', 'received']
 | 
			
		||||
          .contains(item.tag)) {
 | 
			
		||||
        logger.info(
 | 
			
		||||
          "Won't handle stanza as delivery receipt because we found an '${item.tag}' element",
 | 
			
		||||
        );
 | 
			
		||||
    // for (final item in message.children) {
 | 
			
		||||
    //   if (!['origin-id', 'stanza-id', 'delay', 'store', 'received']
 | 
			
		||||
    //       .contains(item.tag)) {
 | 
			
		||||
    //     logger.info(
 | 
			
		||||
    //       "Won't handle stanza as delivery receipt because we found an '${item.tag}' element",
 | 
			
		||||
    //     );
 | 
			
		||||
 | 
			
		||||
        return state.copyWith(done: true);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    //     return state.copyWith(done: true);
 | 
			
		||||
    //   }
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    getAttributes().sendEvent(
 | 
			
		||||
      DeliveryReceiptReceivedEvent(
 | 
			
		||||
@ -81,6 +89,6 @@ class MessageDeliveryReceiptManager extends XmppManagerBase {
 | 
			
		||||
        id: received.attributes['id']! as String,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -70,7 +70,7 @@ class BlockingManager extends XmppManagerBase {
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<StanzaHandlerData> _unblockPush(
 | 
			
		||||
@ -92,7 +92,7 @@ class BlockingManager extends XmppManagerBase {
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<bool> block(List<String> items) async {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								packages/moxxmpp/lib/src/xeps/xep_0198/types.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								packages/moxxmpp/lib/src/xeps/xep_0198/types.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
			
		||||
class StreamManagementData {
 | 
			
		||||
  const StreamManagementData(this.exclude);
 | 
			
		||||
 | 
			
		||||
  /// Whether the stanza should be exluded from the StreamManagement's resend queue.
 | 
			
		||||
  final bool exclude;
 | 
			
		||||
}
 | 
			
		||||
@ -15,6 +15,7 @@ import 'package:moxxmpp/src/xeps/xep_0198/errors.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0198/negotiator.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0198/nonzas.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0198/state.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0198/types.dart';
 | 
			
		||||
import 'package:synchronized/synchronized.dart';
 | 
			
		||||
 | 
			
		||||
const xmlUintMax = 4294967296; // 2**32
 | 
			
		||||
@ -402,7 +403,9 @@ class StreamManagementManager extends XmppManagerBase {
 | 
			
		||||
    if (isStreamManagementEnabled()) {
 | 
			
		||||
      await _incrementC2S();
 | 
			
		||||
 | 
			
		||||
      if (state.excludeFromStreamManagement) return state;
 | 
			
		||||
      if (state.extensions.get<StreamManagementData>()?.exclude ?? false) {
 | 
			
		||||
        return state;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      _unackedStanzas[_state.c2s] = stanza;
 | 
			
		||||
      await _sendAckRequest();
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import 'package:meta/meta.dart';
 | 
			
		||||
import 'package:moxxmpp/src/jid.dart';
 | 
			
		||||
import 'package:moxxmpp/src/managers/base.dart';
 | 
			
		||||
import 'package:moxxmpp/src/managers/data.dart';
 | 
			
		||||
import 'package:moxxmpp/src/managers/handlers.dart';
 | 
			
		||||
@ -7,10 +8,14 @@ import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
 | 
			
		||||
@immutable
 | 
			
		||||
class DelayedDelivery {
 | 
			
		||||
  const DelayedDelivery(this.from, this.timestamp);
 | 
			
		||||
class DelayedDeliveryData {
 | 
			
		||||
  const DelayedDeliveryData(this.from, this.timestamp);
 | 
			
		||||
 | 
			
		||||
  /// The timestamp the message was originally sent.
 | 
			
		||||
  final DateTime timestamp;
 | 
			
		||||
  final String from;
 | 
			
		||||
 | 
			
		||||
  /// The JID that originally sent the message.
 | 
			
		||||
  final JID from;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DelayedDeliveryManager extends XmppManagerBase {
 | 
			
		||||
@ -23,6 +28,8 @@ class DelayedDeliveryManager extends XmppManagerBase {
 | 
			
		||||
  List<StanzaHandler> getIncomingStanzaHandlers() => [
 | 
			
		||||
        StanzaHandler(
 | 
			
		||||
          stanzaTag: 'message',
 | 
			
		||||
          tagName: 'delay',
 | 
			
		||||
          tagXmlns: delayedDeliveryXmlns,
 | 
			
		||||
          callback: _onIncomingMessage,
 | 
			
		||||
          priority: 200,
 | 
			
		||||
        ),
 | 
			
		||||
@ -32,14 +39,14 @@ class DelayedDeliveryManager extends XmppManagerBase {
 | 
			
		||||
    Stanza stanza,
 | 
			
		||||
    StanzaHandlerData state,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final delay = stanza.firstTag('delay', xmlns: delayedDeliveryXmlns);
 | 
			
		||||
    if (delay == null) return state;
 | 
			
		||||
    final delay = stanza.firstTag('delay', xmlns: delayedDeliveryXmlns)!;
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      delayedDelivery: DelayedDelivery(
 | 
			
		||||
        delay.attributes['from']! as String,
 | 
			
		||||
        DateTime.parse(delay.attributes['stamp']! as String),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        DelayedDeliveryData(
 | 
			
		||||
          JID.fromString(delay.attributes['from']! as String),
 | 
			
		||||
          DateTime.parse(delay.attributes['stamp']! as String),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,13 @@ import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0297.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0386.dart';
 | 
			
		||||
 | 
			
		||||
class CarbonsData {
 | 
			
		||||
  const CarbonsData(this.isCarbon);
 | 
			
		||||
 | 
			
		||||
  /// Indicates whether this message is a carbon.
 | 
			
		||||
  final bool isCarbon;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// This manager class implements support for XEP-0280.
 | 
			
		||||
class CarbonsManager extends XmppManagerBase {
 | 
			
		||||
  CarbonsManager() : super(carbonsManager);
 | 
			
		||||
@ -77,15 +84,14 @@ class CarbonsManager extends XmppManagerBase {
 | 
			
		||||
  ) async {
 | 
			
		||||
    final from = JID.fromString(message.attributes['from']! as String);
 | 
			
		||||
    final received = message.firstTag('received', xmlns: carbonsXmlns)!;
 | 
			
		||||
    if (!isCarbonValid(from)) return state.copyWith(done: true);
 | 
			
		||||
    if (!isCarbonValid(from)) return state..done = true;
 | 
			
		||||
 | 
			
		||||
    final forwarded = received.firstTag('forwarded', xmlns: forwardedXmlns)!;
 | 
			
		||||
    final carbon = unpackForwarded(forwarded);
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      isCarbon: true,
 | 
			
		||||
      stanza: carbon,
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(const CarbonsData(true))
 | 
			
		||||
      ..stanza = carbon;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<StanzaHandlerData> _onMessageSent(
 | 
			
		||||
@ -94,15 +100,14 @@ class CarbonsManager extends XmppManagerBase {
 | 
			
		||||
  ) async {
 | 
			
		||||
    final from = JID.fromString(message.attributes['from']! as String);
 | 
			
		||||
    final sent = message.firstTag('sent', xmlns: carbonsXmlns)!;
 | 
			
		||||
    if (!isCarbonValid(from)) return state.copyWith(done: true);
 | 
			
		||||
    if (!isCarbonValid(from)) return state..done = true;
 | 
			
		||||
 | 
			
		||||
    final forwarded = sent.firstTag('forwarded', xmlns: forwardedXmlns)!;
 | 
			
		||||
    final carbon = unpackForwarded(forwarded);
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      isCarbon: true,
 | 
			
		||||
      stanza: carbon,
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(const CarbonsData(true))
 | 
			
		||||
      ..stanza = carbon;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Send a request to the server, asking it to enable Message Carbons.
 | 
			
		||||
 | 
			
		||||
@ -6,14 +6,21 @@ import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
 | 
			
		||||
XMLNode makeLastMessageCorrectionEdit(String id) {
 | 
			
		||||
  return XMLNode.xmlns(
 | 
			
		||||
    tag: 'replace',
 | 
			
		||||
    xmlns: lmcXmlns,
 | 
			
		||||
    attributes: <String, String>{
 | 
			
		||||
      'id': id,
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
class LastMessageCorrectionData {
 | 
			
		||||
  const LastMessageCorrectionData(this.id);
 | 
			
		||||
 | 
			
		||||
  /// The id the LMC applies to.
 | 
			
		||||
  final String id;
 | 
			
		||||
 | 
			
		||||
  XMLNode toXML() {
 | 
			
		||||
    return XMLNode.xmlns(
 | 
			
		||||
      tag: 'replace',
 | 
			
		||||
      xmlns: lmcXmlns,
 | 
			
		||||
      attributes: {
 | 
			
		||||
        'id': id,
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class LastMessageCorrectionManager extends XmppManagerBase {
 | 
			
		||||
@ -42,8 +49,9 @@ class LastMessageCorrectionManager extends XmppManagerBase {
 | 
			
		||||
    StanzaHandlerData state,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final edit = stanza.firstTag('replace', xmlns: lmcXmlns)!;
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      lastMessageCorrectionSid: edit.attributes['id']! as String,
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        LastMessageCorrectionData(edit.attributes['id']! as String),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,13 @@ import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
 | 
			
		||||
class ChatMarkerData {
 | 
			
		||||
  const ChatMarkerData(this.isMarkable);
 | 
			
		||||
 | 
			
		||||
  /// Indicates whether the message can be replied to with a chat marker.
 | 
			
		||||
  final bool isMarkable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XMLNode makeChatMarkerMarkable() {
 | 
			
		||||
  return XMLNode.xmlns(
 | 
			
		||||
    tag: 'markable',
 | 
			
		||||
@ -54,7 +61,9 @@ class ChatMarkerManager extends XmppManagerBase {
 | 
			
		||||
    final marker = message.firstTagByXmlns(chatMarkersXmlns)!;
 | 
			
		||||
 | 
			
		||||
    // Handle the <markable /> explicitly
 | 
			
		||||
    if (marker.tag == 'markable') return state.copyWith(isMarkable: true);
 | 
			
		||||
    if (marker.tag == 'markable') {
 | 
			
		||||
      return state..extensions.set(const ChatMarkerData(true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!['received', 'displayed', 'acknowledged'].contains(marker.tag)) {
 | 
			
		||||
      logger.warning("Unknown message marker '${marker.tag}' found.");
 | 
			
		||||
@ -68,6 +77,6 @@ class ChatMarkerManager extends XmppManagerBase {
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(done: true);
 | 
			
		||||
    return state..done = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,27 +5,25 @@ enum MessageProcessingHint {
 | 
			
		||||
  noPermanentStore,
 | 
			
		||||
  noStore,
 | 
			
		||||
  noCopies,
 | 
			
		||||
  store,
 | 
			
		||||
}
 | 
			
		||||
  store;
 | 
			
		||||
 | 
			
		||||
MessageProcessingHint messageProcessingHintFromXml(XMLNode element) {
 | 
			
		||||
  switch (element.tag) {
 | 
			
		||||
    case 'no-permanent-store':
 | 
			
		||||
      return MessageProcessingHint.noPermanentStore;
 | 
			
		||||
    case 'no-store':
 | 
			
		||||
      return MessageProcessingHint.noStore;
 | 
			
		||||
    case 'no-copy':
 | 
			
		||||
      return MessageProcessingHint.noCopies;
 | 
			
		||||
    case 'store':
 | 
			
		||||
      return MessageProcessingHint.store;
 | 
			
		||||
  factory MessageProcessingHint.fromName(String name) {
 | 
			
		||||
    switch (name) {
 | 
			
		||||
      case 'no-permanent-store':
 | 
			
		||||
        return MessageProcessingHint.noPermanentStore;
 | 
			
		||||
      case 'no-store':
 | 
			
		||||
        return MessageProcessingHint.noStore;
 | 
			
		||||
      case 'no-copy':
 | 
			
		||||
        return MessageProcessingHint.noCopies;
 | 
			
		||||
      case 'store':
 | 
			
		||||
        return MessageProcessingHint.store;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(false, 'Invalid Message Processing Hint: $name');
 | 
			
		||||
    return MessageProcessingHint.noStore;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  assert(false, 'Invalid Message Processing Hint: ${element.tag}');
 | 
			
		||||
  return MessageProcessingHint.noStore;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension XmlExtension on MessageProcessingHint {
 | 
			
		||||
  XMLNode toXml() {
 | 
			
		||||
  XMLNode toXML() {
 | 
			
		||||
    String tag;
 | 
			
		||||
    switch (this) {
 | 
			
		||||
      case MessageProcessingHint.noPermanentStore:
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,28 @@ import 'package:moxxmpp/src/namespaces.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stanza.dart';
 | 
			
		||||
import 'package:moxxmpp/src/stringxml.dart';
 | 
			
		||||
 | 
			
		||||
class StableIdData {
 | 
			
		||||
  const StableIdData(this.originId, this.stanzaIds);
 | 
			
		||||
 | 
			
		||||
  /// <origin-id />
 | 
			
		||||
  final String? originId;
 | 
			
		||||
 | 
			
		||||
  /// Stanza ids
 | 
			
		||||
  final List<StanzaId>? stanzaIds;
 | 
			
		||||
 | 
			
		||||
  XMLNode toOriginIdElement() {
 | 
			
		||||
    assert(
 | 
			
		||||
      originId != null,
 | 
			
		||||
      'Can only build the XML element if originId != null',
 | 
			
		||||
    );
 | 
			
		||||
    return XMLNode.xmlns(
 | 
			
		||||
      tag: 'origin-id',
 | 
			
		||||
      xmlns: stableIdXmlns,
 | 
			
		||||
      attributes: {'id': originId!},
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Representation of a <stanza-id /> element.
 | 
			
		||||
class StanzaId {
 | 
			
		||||
  const StanzaId(
 | 
			
		||||
@ -32,14 +54,6 @@ class StanzaId {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XMLNode makeOriginIdElement(String id) {
 | 
			
		||||
  return XMLNode.xmlns(
 | 
			
		||||
    tag: 'origin-id',
 | 
			
		||||
    xmlns: stableIdXmlns,
 | 
			
		||||
    attributes: {'id': id},
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StableIdManager extends XmppManagerBase {
 | 
			
		||||
  StableIdManager() : super(stableIdManager);
 | 
			
		||||
 | 
			
		||||
@ -86,9 +100,12 @@ class StableIdManager extends XmppManagerBase {
 | 
			
		||||
          .toList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      originId: originId,
 | 
			
		||||
      stanzaIds: stanzaIds,
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        StableIdData(
 | 
			
		||||
          originId,
 | 
			
		||||
          stanzaIds,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -13,57 +13,57 @@ enum ExplicitEncryptionType {
 | 
			
		||||
  omemo,
 | 
			
		||||
  omemo1,
 | 
			
		||||
  omemo2,
 | 
			
		||||
  unknown,
 | 
			
		||||
}
 | 
			
		||||
  unknown;
 | 
			
		||||
 | 
			
		||||
String _explicitEncryptionTypeToString(ExplicitEncryptionType type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case ExplicitEncryptionType.otr:
 | 
			
		||||
      return emeOtr;
 | 
			
		||||
    case ExplicitEncryptionType.legacyOpenPGP:
 | 
			
		||||
      return emeLegacyOpenPGP;
 | 
			
		||||
    case ExplicitEncryptionType.openPGP:
 | 
			
		||||
      return emeOpenPGP;
 | 
			
		||||
    case ExplicitEncryptionType.omemo:
 | 
			
		||||
      return emeOmemo;
 | 
			
		||||
    case ExplicitEncryptionType.omemo1:
 | 
			
		||||
      return emeOmemo1;
 | 
			
		||||
    case ExplicitEncryptionType.omemo2:
 | 
			
		||||
      return emeOmemo2;
 | 
			
		||||
    case ExplicitEncryptionType.unknown:
 | 
			
		||||
      return '';
 | 
			
		||||
  factory ExplicitEncryptionType.fromNamespace(String namespace) {
 | 
			
		||||
    switch (namespace) {
 | 
			
		||||
      case emeOtr:
 | 
			
		||||
        return ExplicitEncryptionType.otr;
 | 
			
		||||
      case emeLegacyOpenPGP:
 | 
			
		||||
        return ExplicitEncryptionType.legacyOpenPGP;
 | 
			
		||||
      case emeOpenPGP:
 | 
			
		||||
        return ExplicitEncryptionType.openPGP;
 | 
			
		||||
      case emeOmemo:
 | 
			
		||||
        return ExplicitEncryptionType.omemo;
 | 
			
		||||
      case emeOmemo1:
 | 
			
		||||
        return ExplicitEncryptionType.omemo1;
 | 
			
		||||
      case emeOmemo2:
 | 
			
		||||
        return ExplicitEncryptionType.omemo2;
 | 
			
		||||
      default:
 | 
			
		||||
        return ExplicitEncryptionType.unknown;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ExplicitEncryptionType _explicitEncryptionTypeFromString(String str) {
 | 
			
		||||
  switch (str) {
 | 
			
		||||
    case emeOtr:
 | 
			
		||||
      return ExplicitEncryptionType.otr;
 | 
			
		||||
    case emeLegacyOpenPGP:
 | 
			
		||||
      return ExplicitEncryptionType.legacyOpenPGP;
 | 
			
		||||
    case emeOpenPGP:
 | 
			
		||||
      return ExplicitEncryptionType.openPGP;
 | 
			
		||||
    case emeOmemo:
 | 
			
		||||
      return ExplicitEncryptionType.omemo;
 | 
			
		||||
    case emeOmemo1:
 | 
			
		||||
      return ExplicitEncryptionType.omemo1;
 | 
			
		||||
    case emeOmemo2:
 | 
			
		||||
      return ExplicitEncryptionType.omemo2;
 | 
			
		||||
    default:
 | 
			
		||||
      return ExplicitEncryptionType.unknown;
 | 
			
		||||
  String toNamespace() {
 | 
			
		||||
    switch (this) {
 | 
			
		||||
      case ExplicitEncryptionType.otr:
 | 
			
		||||
        return emeOtr;
 | 
			
		||||
      case ExplicitEncryptionType.legacyOpenPGP:
 | 
			
		||||
        return emeLegacyOpenPGP;
 | 
			
		||||
      case ExplicitEncryptionType.openPGP:
 | 
			
		||||
        return emeOpenPGP;
 | 
			
		||||
      case ExplicitEncryptionType.omemo:
 | 
			
		||||
        return emeOmemo;
 | 
			
		||||
      case ExplicitEncryptionType.omemo1:
 | 
			
		||||
        return emeOmemo1;
 | 
			
		||||
      case ExplicitEncryptionType.omemo2:
 | 
			
		||||
        return emeOmemo2;
 | 
			
		||||
      case ExplicitEncryptionType.unknown:
 | 
			
		||||
        return '';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create an <encryption /> element with [type] indicating which type of encryption was
 | 
			
		||||
/// used.
 | 
			
		||||
XMLNode buildEmeElement(ExplicitEncryptionType type) {
 | 
			
		||||
  return XMLNode.xmlns(
 | 
			
		||||
    tag: 'encryption',
 | 
			
		||||
    xmlns: emeXmlns,
 | 
			
		||||
    attributes: <String, String>{
 | 
			
		||||
      'namespace': _explicitEncryptionTypeToString(type),
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
  /// Create an <encryption /> element with an xmlns indicating what type of encryption was
 | 
			
		||||
  /// used.
 | 
			
		||||
  XMLNode toXML() {
 | 
			
		||||
    return XMLNode.xmlns(
 | 
			
		||||
      tag: 'encryption',
 | 
			
		||||
      xmlns: emeXmlns,
 | 
			
		||||
      attributes: <String, String>{
 | 
			
		||||
        'namespace': toNamespace(),
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EmeManager extends XmppManagerBase {
 | 
			
		||||
@ -91,10 +91,11 @@ class EmeManager extends XmppManagerBase {
 | 
			
		||||
  ) async {
 | 
			
		||||
    final encryption = message.firstTag('encryption', xmlns: emeXmlns)!;
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      encryptionType: _explicitEncryptionTypeFromString(
 | 
			
		||||
        encryption.attributes['namespace']! as String,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        ExplicitEncryptionType.fromNamespace(
 | 
			
		||||
          encryption.attributes['namespace']! as String,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,21 @@
 | 
			
		||||
import 'package:omemo_dart/omemo_dart.dart';
 | 
			
		||||
 | 
			
		||||
/// A simple wrapper class for defining elements that should not be encrypted.
 | 
			
		||||
class DoNotEncrypt {
 | 
			
		||||
  const DoNotEncrypt(this.tag, this.xmlns);
 | 
			
		||||
 | 
			
		||||
  /// The tag of the element.
 | 
			
		||||
  final String tag;
 | 
			
		||||
 | 
			
		||||
  /// The xmlns attribute of the element.
 | 
			
		||||
  final String xmlns;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// An encryption error caused by OMEMO.
 | 
			
		||||
class OmemoEncryptionError {
 | 
			
		||||
  const OmemoEncryptionError(this.jids, this.devices);
 | 
			
		||||
 | 
			
		||||
  /// See omemo_dart's EncryptionResult for info on these fields.
 | 
			
		||||
  final Map<String, OmemoException> jids;
 | 
			
		||||
  final Map<RatchetMapKey, OmemoException> devices;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0060/errors.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0060/xep_0060.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0203.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0280.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0334.dart';
 | 
			
		||||
import 'package:moxxmpp/src/xeps/xep_0380.dart';
 | 
			
		||||
@ -276,7 +277,7 @@ abstract class BaseOmemoManager extends XmppManagerBase {
 | 
			
		||||
            // Add a storage hint in case this is a message
 | 
			
		||||
            // Taken from the example at
 | 
			
		||||
            // https://xmpp.org/extensions/xep-0384.html#message-structure-description.
 | 
			
		||||
            MessageProcessingHint.store.toXml(),
 | 
			
		||||
            MessageProcessingHint.store.toXML(),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        awaitable: false,
 | 
			
		||||
@ -363,19 +364,18 @@ abstract class BaseOmemoManager extends XmppManagerBase {
 | 
			
		||||
    logger.finest('Encryption done');
 | 
			
		||||
 | 
			
		||||
    if (!result.isSuccess(2)) {
 | 
			
		||||
      final other = Map<String, dynamic>.from(state.other);
 | 
			
		||||
      other['encryption_error_jids'] = result.jidEncryptionErrors;
 | 
			
		||||
      other['encryption_error_devices'] = result.deviceEncryptionErrors;
 | 
			
		||||
      return state.copyWith(
 | 
			
		||||
        other: other,
 | 
			
		||||
      return state
 | 
			
		||||
        ..cancel = true
 | 
			
		||||
        // If we have no device list for toJid, then the contact most likely does not
 | 
			
		||||
        // support OMEMO:2
 | 
			
		||||
        cancelReason: result.jidEncryptionErrors[toJid.toString()]
 | 
			
		||||
        ..cancelReason = result.jidEncryptionErrors[toJid.toString()]
 | 
			
		||||
                is NoKeyMaterialAvailableException
 | 
			
		||||
            ? OmemoNotSupportedForContactException()
 | 
			
		||||
            : UnknownOmemoError(),
 | 
			
		||||
        cancel: true,
 | 
			
		||||
      );
 | 
			
		||||
            : UnknownOmemoError()
 | 
			
		||||
        ..encryptionError = OmemoEncryptionError(
 | 
			
		||||
          result.jidEncryptionErrors,
 | 
			
		||||
          result.deviceEncryptionErrors,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final encrypted = _buildEncryptedElement(
 | 
			
		||||
@ -389,19 +389,16 @@ abstract class BaseOmemoManager extends XmppManagerBase {
 | 
			
		||||
    if (stanza.tag == 'message') {
 | 
			
		||||
      children
 | 
			
		||||
        // Add EME data
 | 
			
		||||
        ..add(buildEmeElement(ExplicitEncryptionType.omemo2))
 | 
			
		||||
        ..add(ExplicitEncryptionType.omemo2.toXML())
 | 
			
		||||
        // Add a storage hint in case this is a message
 | 
			
		||||
        // Taken from the example at
 | 
			
		||||
        // https://xmpp.org/extensions/xep-0384.html#message-structure-description.
 | 
			
		||||
        ..add(MessageProcessingHint.store.toXml());
 | 
			
		||||
        ..add(MessageProcessingHint.store.toXML());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      stanza: state.stanza.copyWith(
 | 
			
		||||
        children: children,
 | 
			
		||||
      ),
 | 
			
		||||
      encrypted: true,
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..stanza = state.stanza.copyWith(children: children)
 | 
			
		||||
      ..encrypted = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// This function is called whenever a message is to be encrypted. If it returns true,
 | 
			
		||||
@ -444,17 +441,19 @@ abstract class BaseOmemoManager extends XmppManagerBase {
 | 
			
		||||
      OmemoIncomingStanza(
 | 
			
		||||
        fromJid.toString(),
 | 
			
		||||
        sid,
 | 
			
		||||
        state.delayedDelivery?.timestamp.millisecondsSinceEpoch ??
 | 
			
		||||
        state.extensions
 | 
			
		||||
                .get<DelayedDeliveryData>()
 | 
			
		||||
                ?.timestamp
 | 
			
		||||
                .millisecondsSinceEpoch ??
 | 
			
		||||
            DateTime.now().millisecondsSinceEpoch,
 | 
			
		||||
        keys,
 | 
			
		||||
        payloadElement?.innerText(),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    final other = Map<String, dynamic>.from(state.other);
 | 
			
		||||
    var children = stanza.children;
 | 
			
		||||
    if (result.error != null) {
 | 
			
		||||
      other['encryption_error'] = result.error;
 | 
			
		||||
      state.encryptionError = result.error;
 | 
			
		||||
    } else {
 | 
			
		||||
      children = stanza.children
 | 
			
		||||
          .where(
 | 
			
		||||
@ -471,11 +470,9 @@ abstract class BaseOmemoManager extends XmppManagerBase {
 | 
			
		||||
        envelope = XMLNode.fromString(result.payload!);
 | 
			
		||||
      } on XmlParserException catch (_) {
 | 
			
		||||
        logger.warning('Failed to parse envelope payload: ${result.payload!}');
 | 
			
		||||
        other['encryption_error'] = InvalidEnvelopePayloadException();
 | 
			
		||||
        return state.copyWith(
 | 
			
		||||
          encrypted: true,
 | 
			
		||||
          other: other,
 | 
			
		||||
        );
 | 
			
		||||
        return state
 | 
			
		||||
          ..encrypted = true
 | 
			
		||||
          ..encryptionError = InvalidEnvelopePayloadException();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      final envelopeChildren = envelope.firstTag('content')?.children;
 | 
			
		||||
@ -489,13 +486,13 @@ abstract class BaseOmemoManager extends XmppManagerBase {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!checkAffixElements(envelope, stanza.from!, ourJid)) {
 | 
			
		||||
        other['encryption_error'] = InvalidAffixElementsException();
 | 
			
		||||
        state.encryptionError = InvalidAffixElementsException();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      encrypted: true,
 | 
			
		||||
      stanza: Stanza(
 | 
			
		||||
    return state
 | 
			
		||||
      ..encrypted = true
 | 
			
		||||
      ..stanza = Stanza(
 | 
			
		||||
        to: stanza.to,
 | 
			
		||||
        from: stanza.from,
 | 
			
		||||
        id: stanza.id,
 | 
			
		||||
@ -503,9 +500,7 @@ abstract class BaseOmemoManager extends XmppManagerBase {
 | 
			
		||||
        children: children,
 | 
			
		||||
        tag: stanza.tag,
 | 
			
		||||
        attributes: Map<String, String>.from(stanza.attributes),
 | 
			
		||||
      ),
 | 
			
		||||
      other: other,
 | 
			
		||||
    );
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Convenience function that attempts to retrieve the raw XML payload from the
 | 
			
		||||
 | 
			
		||||
@ -98,7 +98,7 @@ class SIMSManager extends XmppManagerBase {
 | 
			
		||||
    final references = message.findTags('reference', xmlns: referenceXmlns);
 | 
			
		||||
    for (final ref in references) {
 | 
			
		||||
      final sims = ref.firstTag('media-sharing', xmlns: simsXmlns);
 | 
			
		||||
      if (sims != null) return state.copyWith(sims: parseSIMSElement(sims));
 | 
			
		||||
      if (sims != null) return state..extensions.set(parseSIMSElement(sims));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state;
 | 
			
		||||
 | 
			
		||||
@ -47,11 +47,12 @@ class MessageRetractionManager extends XmppManagerBase {
 | 
			
		||||
    final isFallbackBody =
 | 
			
		||||
        message.firstTag('fallback', xmlns: fallbackIndicationXmlns) != null;
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      messageRetraction: MessageRetractionData(
 | 
			
		||||
        applyTo.attributes['id']! as String,
 | 
			
		||||
        isFallbackBody ? message.firstTag('body')?.innerText() : null,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        MessageRetractionData(
 | 
			
		||||
          applyTo.attributes['id']! as String,
 | 
			
		||||
          isFallbackBody ? message.firstTag('body')?.innerText() : null,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -55,14 +55,15 @@ class MessageReactionsManager extends XmppManagerBase {
 | 
			
		||||
  ) async {
 | 
			
		||||
    final reactionsElement =
 | 
			
		||||
        message.firstTag('reactions', xmlns: messageReactionsXmlns)!;
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      messageReactions: MessageReactions(
 | 
			
		||||
        reactionsElement.attributes['id']! as String,
 | 
			
		||||
        reactionsElement.children
 | 
			
		||||
            .where((c) => c.tag == 'reaction')
 | 
			
		||||
            .map((c) => c.innerText())
 | 
			
		||||
            .toList(),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        MessageReactions(
 | 
			
		||||
          reactionsElement.attributes['id']! as String,
 | 
			
		||||
          reactionsElement.children
 | 
			
		||||
              .where((c) => c.tag == 'reaction')
 | 
			
		||||
              .map((c) => c.innerText())
 | 
			
		||||
              .toList(),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -135,10 +135,9 @@ class SFSManager extends XmppManagerBase {
 | 
			
		||||
  ) async {
 | 
			
		||||
    final sfs = message.firstTag('file-sharing', xmlns: sfsXmlns)!;
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      sfs: StatelessFileSharingData.fromXML(
 | 
			
		||||
        sfs,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        StatelessFileSharingData.fromXML(sfs),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -227,6 +227,13 @@ class StickerPack {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StickersData {
 | 
			
		||||
  const StickersData(this.stickerPackId);
 | 
			
		||||
 | 
			
		||||
  /// The id of the sticker pack the referenced sticker is from.
 | 
			
		||||
  final String stickerPackId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StickersManager extends XmppManagerBase {
 | 
			
		||||
  StickersManager() : super(stickersManager);
 | 
			
		||||
 | 
			
		||||
@ -249,9 +256,10 @@ class StickersManager extends XmppManagerBase {
 | 
			
		||||
    StanzaHandlerData state,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final sticker = stanza.firstTag('sticker', xmlns: stickersXmlns)!;
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      stickerPackId: sticker.attributes['pack']! as String,
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        StickersData(sticker.attributes['pack']! as String),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Publishes the StickerPack [pack] to the PubSub node of [jid]. If specified, then
 | 
			
		||||
 | 
			
		||||
@ -103,13 +103,14 @@ class MessageRepliesManager extends XmppManagerBase {
 | 
			
		||||
      end = int.parse(body.attributes['end']! as String);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return state.copyWith(
 | 
			
		||||
      reply: ReplyData(
 | 
			
		||||
        id: id,
 | 
			
		||||
        to: to,
 | 
			
		||||
        start: start,
 | 
			
		||||
        end: end,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return state
 | 
			
		||||
      ..extensions.set(
 | 
			
		||||
        ReplyData(
 | 
			
		||||
          id: id,
 | 
			
		||||
          to: to,
 | 
			
		||||
          start: start,
 | 
			
		||||
          end: end,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import 'package:moxxmpp/moxxmpp.dart';
 | 
			
		||||
import 'package:moxxmpp/src/util/typed_map.dart';
 | 
			
		||||
import 'package:test/test.dart';
 | 
			
		||||
 | 
			
		||||
final stanza1 = Stanza.iq(
 | 
			
		||||
@ -16,8 +17,8 @@ void main() {
 | 
			
		||||
      callback: (stanza, _) async => StanzaHandlerData(
 | 
			
		||||
        true,
 | 
			
		||||
        false,
 | 
			
		||||
        null,
 | 
			
		||||
        stanza,
 | 
			
		||||
        TypedMap(),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -38,8 +39,8 @@ void main() {
 | 
			
		||||
      callback: (stanza, _) async => StanzaHandlerData(
 | 
			
		||||
        true,
 | 
			
		||||
        false,
 | 
			
		||||
        null,
 | 
			
		||||
        stanza,
 | 
			
		||||
        TypedMap(),
 | 
			
		||||
      ),
 | 
			
		||||
      tagXmlns: 'owo',
 | 
			
		||||
    );
 | 
			
		||||
@ -59,8 +60,8 @@ void main() {
 | 
			
		||||
        return StanzaHandlerData(
 | 
			
		||||
          true,
 | 
			
		||||
          false,
 | 
			
		||||
          null,
 | 
			
		||||
          stanza,
 | 
			
		||||
          TypedMap(),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
      stanzaTag: 'iq',
 | 
			
		||||
@ -77,8 +78,8 @@ void main() {
 | 
			
		||||
      StanzaHandlerData(
 | 
			
		||||
        false,
 | 
			
		||||
        false,
 | 
			
		||||
        null,
 | 
			
		||||
        stanza2,
 | 
			
		||||
        TypedMap(),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    expect(run, true);
 | 
			
		||||
@ -89,8 +90,8 @@ void main() {
 | 
			
		||||
      callback: (stanza, _) async => StanzaHandlerData(
 | 
			
		||||
        true,
 | 
			
		||||
        false,
 | 
			
		||||
        null,
 | 
			
		||||
        stanza,
 | 
			
		||||
        TypedMap(),
 | 
			
		||||
      ),
 | 
			
		||||
      tagName: 'tag',
 | 
			
		||||
    );
 | 
			
		||||
@ -107,8 +108,8 @@ void main() {
 | 
			
		||||
      callback: (stanza, _) async => StanzaHandlerData(
 | 
			
		||||
        true,
 | 
			
		||||
        false,
 | 
			
		||||
        null,
 | 
			
		||||
        stanza,
 | 
			
		||||
        TypedMap(),
 | 
			
		||||
      ),
 | 
			
		||||
      tagName: 'tag',
 | 
			
		||||
      stanzaTag: 'iq',
 | 
			
		||||
@ -127,8 +128,8 @@ void main() {
 | 
			
		||||
      callback: (stanza, _) async => StanzaHandlerData(
 | 
			
		||||
        true,
 | 
			
		||||
        false,
 | 
			
		||||
        null,
 | 
			
		||||
        stanza,
 | 
			
		||||
        TypedMap(),
 | 
			
		||||
      ),
 | 
			
		||||
      xmlns: componentAcceptXmlns,
 | 
			
		||||
    );
 | 
			
		||||
@ -147,8 +148,8 @@ void main() {
 | 
			
		||||
        callback: (stanza, _) async => StanzaHandlerData(
 | 
			
		||||
          true,
 | 
			
		||||
          false,
 | 
			
		||||
          null,
 | 
			
		||||
          stanza,
 | 
			
		||||
          TypedMap(),
 | 
			
		||||
        ),
 | 
			
		||||
        tagName: '1',
 | 
			
		||||
        priority: 100,
 | 
			
		||||
@ -157,8 +158,8 @@ void main() {
 | 
			
		||||
        callback: (stanza, _) async => StanzaHandlerData(
 | 
			
		||||
          true,
 | 
			
		||||
          false,
 | 
			
		||||
          null,
 | 
			
		||||
          stanza,
 | 
			
		||||
          TypedMap(),
 | 
			
		||||
        ),
 | 
			
		||||
        tagName: '2',
 | 
			
		||||
      ),
 | 
			
		||||
@ -166,8 +167,8 @@ void main() {
 | 
			
		||||
        callback: (stanza, _) async => StanzaHandlerData(
 | 
			
		||||
          true,
 | 
			
		||||
          false,
 | 
			
		||||
          null,
 | 
			
		||||
          stanza,
 | 
			
		||||
          TypedMap(),
 | 
			
		||||
        ),
 | 
			
		||||
        tagName: '3',
 | 
			
		||||
        priority: 50,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'package:moxxmpp/moxxmpp.dart';
 | 
			
		||||
import 'package:moxxmpp/src/util/typed_map.dart';
 | 
			
		||||
import 'package:test/test.dart';
 | 
			
		||||
import '../helpers/logging.dart';
 | 
			
		||||
import '../helpers/xmpp.dart';
 | 
			
		||||
@ -15,8 +16,8 @@ Future<void> runIncomingStanzaHandlers(
 | 
			
		||||
        StanzaHandlerData(
 | 
			
		||||
          false,
 | 
			
		||||
          false,
 | 
			
		||||
          null,
 | 
			
		||||
          stanza,
 | 
			
		||||
          TypedMap(),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
@ -34,8 +35,8 @@ Future<void> runOutgoingStanzaHandlers(
 | 
			
		||||
        StanzaHandlerData(
 | 
			
		||||
          false,
 | 
			
		||||
          false,
 | 
			
		||||
          null,
 | 
			
		||||
          stanza,
 | 
			
		||||
          TypedMap(),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'package:moxxmpp/moxxmpp.dart';
 | 
			
		||||
import 'package:moxxmpp/src/util/typed_map.dart';
 | 
			
		||||
import 'package:test/test.dart';
 | 
			
		||||
import 'helpers/logging.dart';
 | 
			
		||||
import 'helpers/xmpp.dart';
 | 
			
		||||
@ -78,8 +79,8 @@ Future<bool> testRosterManager(
 | 
			
		||||
        StanzaHandlerData(
 | 
			
		||||
          false,
 | 
			
		||||
          false,
 | 
			
		||||
          null,
 | 
			
		||||
          stanza,
 | 
			
		||||
          TypedMap(),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
@ -335,8 +336,8 @@ void main() {
 | 
			
		||||
            StanzaHandlerData(
 | 
			
		||||
              false,
 | 
			
		||||
              false,
 | 
			
		||||
              null,
 | 
			
		||||
              maliciousStanza,
 | 
			
		||||
              TypedMap(),
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user