diff --git a/packages/moxxmpp/lib/src/connection.dart b/packages/moxxmpp/lib/src/connection.dart index b752114..6888713 100644 --- a/packages/moxxmpp/lib/src/connection.dart +++ b/packages/moxxmpp/lib/src/connection.dart @@ -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) { diff --git a/packages/moxxmpp/lib/src/managers/data.dart b/packages/moxxmpp/lib/src/managers/data.dart index 5245808..3109fca 100644 --- a/packages/moxxmpp/lib/src/managers/data.dart +++ b/packages/moxxmpp/lib/src/managers/data.dart @@ -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 '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 elements, if available. - List? 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({}) Map 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; } diff --git a/packages/moxxmpp/lib/src/message.dart b/packages/moxxmpp/lib/src/message.dart index ae3ea31..92d0eed 100644 --- a/packages/moxxmpp/lib/src/message.dart +++ b/packages/moxxmpp/lib/src/message.dart @@ -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.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()?.originId, + stanzaIds: state.extensions.get()?.stanzaIds, + isCarbon: state.extensions.get()?.isCarbon ?? false, + deliveryReceiptRequested: state.extensions + .get() + ?.receiptRequested ?? + false, + isMarkable: state.extensions.get()?.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(), + sfs: state.extensions.get(), + sims: state.extensions.get(), + reply: state.extensions.get(), + chatState: state.extensions.get(), + fun: state.extensions.get()?.metadata, + funReplacement: + state.extensions.get()?.id, + funCancellation: + state.extensions.get()?.id, encrypted: state.encrypted, - messageRetraction: state.messageRetraction, - messageCorrectionId: state.lastMessageCorrectionSid, - messageReactions: state.messageReactions, + messageRetraction: state.extensions.get(), + messageCorrectionId: state.extensions.get()?.id, + messageReactions: state.extensions.get(), messageProcessingHints: hints.isEmpty ? null : hints, - stickerPackId: state.stickerPackId, - other: state.other, + stickerPackId: state.extensions.get()?.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 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, diff --git a/packages/moxxmpp/lib/src/presence.dart b/packages/moxxmpp/lib/src/presence.dart index c9931ad..41e1013 100644 --- a/packages/moxxmpp/lib/src/presence.dart +++ b/packages/moxxmpp/lib/src/presence.dart @@ -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; diff --git a/packages/moxxmpp/lib/src/roster/roster.dart b/packages/moxxmpp/lib/src/roster/roster.dart index 6c08d34..c6d7758 100644 --- a/packages/moxxmpp/lib/src/roster/roster.dart +++ b/packages/moxxmpp/lib/src/roster/roster.dart @@ -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 diff --git a/packages/moxxmpp/lib/src/xeps/staging/file_upload_notification.dart b/packages/moxxmpp/lib/src/xeps/staging/file_upload_notification.dart index 7c1caad..51d60ff 100644 --- a/packages/moxxmpp/lib/src/xeps/staging/file_upload_notification.dart +++ b/packages/moxxmpp/lib/src/xeps/staging/file_upload_notification.dart @@ -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 _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 _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, + ), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart b/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart index 263cdce..903304e 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0030/xep_0030.dart @@ -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 _onDiscoItemsRequest( @@ -223,7 +223,7 @@ class DiscoManager extends XmppManagerBase { ], ); - return state.copyWith(done: true); + return state..done = true; } return state; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0054.dart b/packages/moxxmpp/lib/src/xeps/xep_0054.dart index 48031ed..19d0717 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0054.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0054.dart @@ -76,7 +76,7 @@ class VCardManager extends XmppManagerBase { } } - return state.copyWith(done: true); + return state..done = true; } VCardPhoto? _parseVCardPhoto(XMLNode? node) { diff --git a/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart b/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart index 99f7a49..c840c37 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0060/xep_0060.dart @@ -114,7 +114,7 @@ class PubSubManager extends XmppManagerBase { ), ); - return state.copyWith(done: true); + return state..done = true; } Future _getNodeItemCount(JID jid, String node) async { diff --git a/packages/moxxmpp/lib/src/xeps/xep_0066.dart b/packages/moxxmpp/lib/src/xeps/xep_0066.dart index 61bb4cc..a008be3 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0066.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0066.dart @@ -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.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(), + ), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0085.dart b/packages/moxxmpp/lib/src/xeps/xep_0085.dart index ab28ab1..f0d25f7 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0085.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0085.dart @@ -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 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, ), ); } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0184.dart b/packages/moxxmpp/lib/src/xeps/xep_0184.dart index 5a562d2..c0d5e46 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0184.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0184.dart @@ -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 _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; } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0191.dart b/packages/moxxmpp/lib/src/xeps/xep_0191.dart index c443d0d..dcb3395 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0191.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0191.dart @@ -70,7 +70,7 @@ class BlockingManager extends XmppManagerBase { ), ); - return state.copyWith(done: true); + return state..done = true; } Future _unblockPush( @@ -92,7 +92,7 @@ class BlockingManager extends XmppManagerBase { ); } - return state.copyWith(done: true); + return state..done = true; } Future block(List items) async { diff --git a/packages/moxxmpp/lib/src/xeps/xep_0198/types.dart b/packages/moxxmpp/lib/src/xeps/xep_0198/types.dart new file mode 100644 index 0000000..9e26e4c --- /dev/null +++ b/packages/moxxmpp/lib/src/xeps/xep_0198/types.dart @@ -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; +} diff --git a/packages/moxxmpp/lib/src/xeps/xep_0198/xep_0198.dart b/packages/moxxmpp/lib/src/xeps/xep_0198/xep_0198.dart index 9f35880..9ed647f 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0198/xep_0198.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0198/xep_0198.dart @@ -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()?.exclude ?? false) { + return state; + } _unackedStanzas[_state.c2s] = stanza; await _sendAckRequest(); diff --git a/packages/moxxmpp/lib/src/xeps/xep_0203.dart b/packages/moxxmpp/lib/src/xeps/xep_0203.dart index cc33598..a0e7cad 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0203.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0203.dart @@ -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 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), + ), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0280.dart b/packages/moxxmpp/lib/src/xeps/xep_0280.dart index d24e8ca..e84f596 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0280.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0280.dart @@ -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 _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. diff --git a/packages/moxxmpp/lib/src/xeps/xep_0308.dart b/packages/moxxmpp/lib/src/xeps/xep_0308.dart index 2cd9aa7..1cc57b8 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0308.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0308.dart @@ -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: { - '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), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0333.dart b/packages/moxxmpp/lib/src/xeps/xep_0333.dart index 76ae8e3..5858d20 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0333.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0333.dart @@ -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 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; } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0334.dart b/packages/moxxmpp/lib/src/xeps/xep_0334.dart index 57b768e..e4bf1b0 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0334.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0334.dart @@ -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: diff --git a/packages/moxxmpp/lib/src/xeps/xep_0359.dart b/packages/moxxmpp/lib/src/xeps/xep_0359.dart index 649edbe..a1cdfb9 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0359.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0359.dart @@ -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); + + /// + final String? originId; + + /// Stanza ids + final List? 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 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, + ), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0380.dart b/packages/moxxmpp/lib/src/xeps/xep_0380.dart index 9022596..f576bbe 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0380.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0380.dart @@ -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 element with [type] indicating which type of encryption was -/// used. -XMLNode buildEmeElement(ExplicitEncryptionType type) { - return XMLNode.xmlns( - tag: 'encryption', - xmlns: emeXmlns, - attributes: { - 'namespace': _explicitEncryptionTypeToString(type), - }, - ); + /// Create an element with an xmlns indicating what type of encryption was + /// used. + XMLNode toXML() { + return XMLNode.xmlns( + tag: 'encryption', + xmlns: emeXmlns, + attributes: { + '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, + ), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart b/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart index 0b31d23..038598c 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0384/types.dart @@ -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 jids; + final Map devices; +} diff --git a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart index 63b7397..bdf618d 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0384/xep_0384.dart @@ -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.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() + ?.timestamp + .millisecondsSinceEpoch ?? DateTime.now().millisecondsSinceEpoch, keys, payloadElement?.innerText(), ), ); - final other = Map.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.from(stanza.attributes), - ), - other: other, - ); + ); } /// Convenience function that attempts to retrieve the raw XML payload from the diff --git a/packages/moxxmpp/lib/src/xeps/xep_0385.dart b/packages/moxxmpp/lib/src/xeps/xep_0385.dart index fdc6215..bbf66d0 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0385.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0385.dart @@ -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; diff --git a/packages/moxxmpp/lib/src/xeps/xep_0424.dart b/packages/moxxmpp/lib/src/xeps/xep_0424.dart index a9cfc6f..ca5bc62 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0424.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0424.dart @@ -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, + ), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0444.dart b/packages/moxxmpp/lib/src/xeps/xep_0444.dart index 15448ea..0e8f9ac 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0444.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0444.dart @@ -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(), + ), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0447.dart b/packages/moxxmpp/lib/src/xeps/xep_0447.dart index 81ee7d8..f9c8cff 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0447.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0447.dart @@ -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), + ); } } diff --git a/packages/moxxmpp/lib/src/xeps/xep_0449.dart b/packages/moxxmpp/lib/src/xeps/xep_0449.dart index fb95432..1f95556 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0449.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0449.dart @@ -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 diff --git a/packages/moxxmpp/lib/src/xeps/xep_0461.dart b/packages/moxxmpp/lib/src/xeps/xep_0461.dart index 11ee5bf..0415d6f 100644 --- a/packages/moxxmpp/lib/src/xeps/xep_0461.dart +++ b/packages/moxxmpp/lib/src/xeps/xep_0461.dart @@ -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, + ), + ); } } diff --git a/packages/moxxmpp/test/stanzahandler_test.dart b/packages/moxxmpp/test/stanzahandler_test.dart index 3c3bae8..1ff4142 100644 --- a/packages/moxxmpp/test/stanzahandler_test.dart +++ b/packages/moxxmpp/test/stanzahandler_test.dart @@ -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, diff --git a/packages/moxxmpp/test/xeps/xep_0198_test.dart b/packages/moxxmpp/test/xeps/xep_0198_test.dart index 1ba8b78..07c118a 100644 --- a/packages/moxxmpp/test/xeps/xep_0198_test.dart +++ b/packages/moxxmpp/test/xeps/xep_0198_test.dart @@ -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 runIncomingStanzaHandlers( StanzaHandlerData( false, false, - null, stanza, + TypedMap(), ), ); } @@ -34,8 +35,8 @@ Future runOutgoingStanzaHandlers( StanzaHandlerData( false, false, - null, stanza, + TypedMap(), ), ); } diff --git a/packages/moxxmpp/test/xmpp_test.dart b/packages/moxxmpp/test/xmpp_test.dart index 50675f4..21d80d0 100644 --- a/packages/moxxmpp/test/xmpp_test.dart +++ b/packages/moxxmpp/test/xmpp_test.dart @@ -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 testRosterManager( StanzaHandlerData( false, false, - null, stanza, + TypedMap(), ), ); } @@ -335,8 +336,8 @@ void main() { StanzaHandlerData( false, false, - null, maliciousStanza, + TypedMap(), ), ); }