feat(all): Restrict extensions to StanzaHandlerExtension

This commit is contained in:
PapaTutuWawa 2023-06-06 21:19:42 +02:00
parent cf3287ccf4
commit 60279a84e0
25 changed files with 176 additions and 94 deletions

View File

@ -533,7 +533,7 @@ class XmppConnection {
// Run post-send handlers
_log.fine('Running post stanza handlers..');
final extensions = TypedMap()
final extensions = TypedMap<StanzaHandlerExtension>()
..set(StreamManagementData(details.excludeFromStreamManagement));
await _runOutgoingPostStanzaHandlers(
newStanza,

View File

@ -8,6 +8,7 @@ import 'package:moxxmpp/src/util/typed_map.dart';
import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
import 'package:moxxmpp/src/xeps/xep_0060/xep_0060.dart';
import 'package:moxxmpp/src/xeps/xep_0084.dart';
import 'package:moxxmpp/src/xeps/xep_0333.dart';
abstract class XmppEvent {}
@ -90,7 +91,7 @@ class MessageEvent extends XmppEvent {
final StanzaError? error;
/// Data added by other handlers.
final TypedMap extensions;
final TypedMap<StanzaHandlerExtension> extensions;
}
/// Triggered when a client responds to our delivery receipt request
@ -101,13 +102,19 @@ class DeliveryReceiptReceivedEvent extends XmppEvent {
}
class ChatMarkerEvent extends XmppEvent {
ChatMarkerEvent({
required this.type,
required this.from,
required this.id,
});
ChatMarkerEvent(
this.from,
this.type,
this.id,
);
/// The entity that sent the chat marker.
final JID from;
final String type;
/// The type of chat marker that was sent.
final ChatMarker type;
/// The id of the message that the marker applies to.
final String id;
}

View File

@ -1,6 +1,8 @@
import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
abstract class StanzaHandlerExtension {}
class StanzaHandlerData {
StanzaHandlerData(
this.done,
@ -41,5 +43,5 @@ class StanzaHandlerData {
bool forceEncryption;
/// Additional data from other managers.
final TypedMap extensions;
final TypedMap<StanzaHandlerExtension> extensions;
}

View File

@ -16,7 +16,7 @@ import 'package:moxxmpp/src/xeps/xep_0461.dart';
/// A callback that is called whenever a message is sent using
/// [MessageManager.sendMessage]. The input the typed map that is passed to
/// sendMessage.
typedef MessageSendingCallback = List<XMLNode> Function(TypedMap);
typedef MessageSendingCallback = List<XMLNode> Function(TypedMap<StanzaHandlerExtension>);
/// The raw content of the <body /> element.
class MessageBodyData {
@ -85,7 +85,7 @@ class MessageManager extends XmppManagerBase {
/// Send an unawaitable message to [to]. [extensions] is a typed map that contains
/// data for building the message.
Future<void> sendMessage(JID to, TypedMap extensions) async {
Future<void> sendMessage(JID to, TypedMap<StanzaHandlerExtension> extensions) async {
await getAttributes().sendStanza(
StanzaDetails(
Stanza.message(
@ -102,7 +102,7 @@ class MessageManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
if (extensions.get<ReplyData>() != null) {
return [];
}

View File

@ -1,10 +1,10 @@
/// A map, similar to Map, but always uses the type of the value as the key.
class TypedMap {
class TypedMap<B> {
/// The internal mapping of type -> data
final Map<Object, Object> _data = {};
final Map<Object, B> _data = {};
/// Associate the type of [value] with [value] in the map.
void set<T extends Object>(T value) {
void set<T extends B>(T value) {
_data[T] = value;
}

View File

@ -13,7 +13,7 @@ import 'package:moxxmpp/src/xeps/xep_0446.dart';
const fileUploadNotificationXmlns = 'proto:urn:xmpp:fun:0';
/// Indicates a file upload notification.
class FileUploadNotificationData {
class FileUploadNotificationData implements StanzaHandlerExtension {
const FileUploadNotificationData(this.metadata);
/// The file metadata indicated in the upload notification.
@ -31,7 +31,7 @@ class FileUploadNotificationData {
}
/// Indicates that a file upload has been cancelled.
class FileUploadNotificationCancellationData {
class FileUploadNotificationCancellationData implements StanzaHandlerExtension {
const FileUploadNotificationCancellationData(this.id);
/// The id of the upload notifiaction that is cancelled.
@ -49,7 +49,7 @@ class FileUploadNotificationCancellationData {
}
/// Indicates that a file upload has been completed.
class FileUploadNotificationReplacementData {
class FileUploadNotificationReplacementData implements StanzaHandlerExtension {
const FileUploadNotificationReplacementData(this.id);
/// The id of the upload notifiaction that is replaced.
@ -141,7 +141,7 @@ class FileUploadNotificationManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final fun = extensions.get<FileUploadNotificationData>();
if (fun != null) {
return [fun.toXML()];

View File

@ -9,7 +9,7 @@ import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
/// A data class representing the jabber:x:oob tag.
class OOBData {
class OOBData implements StanzaHandlerExtension {
const OOBData(this.url, this.desc);
/// The communicated URL of the OOB data
@ -68,7 +68,7 @@ class OOBManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<OOBData>();
return data != null
? [

View File

@ -8,14 +8,14 @@ import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
enum ChatState {
enum ChatState implements StanzaHandlerExtension {
active,
composing,
paused,
inactive,
gone;
factory ChatState.fromString(String state) {
factory ChatState.fromName(String state) {
switch (state) {
case 'active':
return ChatState.active;
@ -27,9 +27,9 @@ enum ChatState {
return ChatState.inactive;
case 'gone':
return ChatState.gone;
default:
return ChatState.gone;
}
throw Exception('Invalid chat state $state');
}
@override
@ -81,7 +81,13 @@ class ChatStateManager extends XmppManagerBase {
StanzaHandlerData state,
) async {
final element = state.stanza.firstTagByXmlns(chatStateXmlns)!;
state.extensions.set(ChatState.fromString(element.tag));
try {
state.extensions.set(ChatState.fromName(element.tag));
} catch (_) {
logger.finest('Ignoring invalid chat state ${element.tag}');
}
return state;
}
@ -106,7 +112,7 @@ class ChatStateManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<ChatState>();
return data != null
? [

View File

@ -10,7 +10,7 @@ import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
class MessageDeliveryReceiptData {
class MessageDeliveryReceiptData implements StanzaHandlerExtension {
const MessageDeliveryReceiptData(this.receiptRequested);
/// Indicates whether a delivery receipt is requested or not.
@ -28,7 +28,7 @@ class MessageDeliveryReceiptData {
}
}
class MessageDeliveryReceivedData {
class MessageDeliveryReceivedData implements StanzaHandlerExtension {
const MessageDeliveryReceivedData(this.id);
/// The stanza id of the message we received.
@ -104,7 +104,7 @@ class MessageDeliveryReceiptManager extends XmppManagerBase {
return state..done = true;
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<MessageDeliveryReceivedData>();
return data != null
? [

View File

@ -1,4 +1,6 @@
class StreamManagementData {
import 'package:moxxmpp/src/managers/data.dart';
class StreamManagementData implements StanzaHandlerExtension {
const StreamManagementData(this.exclude);
/// Whether the stanza should be exluded from the StreamManagement's resend queue.

View File

@ -8,7 +8,7 @@ import 'package:moxxmpp/src/namespaces.dart';
import 'package:moxxmpp/src/stanza.dart';
@immutable
class DelayedDeliveryData {
class DelayedDeliveryData implements StanzaHandlerExtension {
const DelayedDeliveryData(this.from, this.timestamp);
/// The timestamp the message was originally sent.

View File

@ -14,7 +14,7 @@ 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 {
class CarbonsData implements StanzaHandlerExtension {
const CarbonsData(this.isCarbon);
/// Indicates whether this message is a carbon.

View File

@ -8,7 +8,7 @@ import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
class LastMessageCorrectionData {
class LastMessageCorrectionData implements StanzaHandlerExtension {
const LastMessageCorrectionData(this.id);
/// The id the LMC applies to.
@ -57,7 +57,7 @@ class LastMessageCorrectionManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<LastMessageCorrectionData>();
return data != null
? [

View File

@ -10,30 +10,77 @@ import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
class ChatMarkerData {
const ChatMarkerData(this.isMarkable);
enum ChatMarker {
received,
displayed,
acknowledged;
factory ChatMarker.fromName(String name) {
switch (name) {
case 'received': return ChatMarker.received;
case 'displayed': return ChatMarker.displayed;
case 'acknowledged': return ChatMarker.acknowledged;
}
throw Exception('Invalid chat marker $name');
}
XMLNode toXML() {
String tag;
switch (this) {
case ChatMarker.received:
tag = 'received';
break;
case ChatMarker.displayed:
tag = 'displayed';
break;
case ChatMarker.acknowledged:
tag = 'acknowledged';
break;
}
return XMLNode.xmlns(
tag: tag,
xmlns: chatMarkersXmlns,
);
}
}
class MarkableData implements StanzaHandlerExtension {
const MarkableData(this.isMarkable);
/// Indicates whether the message can be replied to with a chat marker.
final bool isMarkable;
XMLNode toXML() {
assert(isMarkable, '');
return XMLNode.xmlns(
tag: 'markable',
xmlns: chatMarkersXmlns,
);
}
}
XMLNode makeChatMarkerMarkable() {
return XMLNode.xmlns(
tag: 'markable',
xmlns: chatMarkersXmlns,
);
}
class ChatMarkerData implements StanzaHandlerExtension {
const ChatMarkerData(this.marker, this.id);
XMLNode makeChatMarker(String tag, String id) {
assert(
['received', 'displayed', 'acknowledged'].contains(tag),
'Invalid chat marker',
);
return XMLNode.xmlns(
tag: tag,
xmlns: chatMarkersXmlns,
attributes: {'id': id},
);
/// The actual chat state
final ChatMarker marker;
/// The ID the chat marker applies to
final String id;
XMLNode toXML() {
final tag = marker.toXML();
return XMLNode.xmlns(
tag: tag.tag,
xmlns: chatMarkersXmlns,
attributes: {
'id': id,
},
);
}
}
class ChatMarkerManager extends XmppManagerBase {
@ -60,31 +107,41 @@ class ChatMarkerManager extends XmppManagerBase {
Stanza message,
StanzaHandlerData state,
) async {
final marker = message.firstTagByXmlns(chatMarkersXmlns)!;
final element = message.firstTagByXmlns(chatMarkersXmlns)!;
// Handle the <markable /> explicitly
if (marker.tag == 'markable') {
return state..extensions.set(const ChatMarkerData(true));
if (element.tag == 'markable') {
return state..extensions.set(const MarkableData(true));
}
if (!['received', 'displayed', 'acknowledged'].contains(marker.tag)) {
logger.warning("Unknown message marker '${marker.tag}' found.");
} else {
try {
getAttributes().sendEvent(
ChatMarkerEvent(
from: JID.fromString(message.from!),
type: marker.tag,
id: marker.attributes['id']! as String,
JID.fromString(message.from!),
ChatMarker.fromName(element.tag),
element.attributes['id']! as String,
),
);
} catch (_) {
logger.warning("Unknown message marker '${element.tag}' found.");
}
return state..done = true;
}
// TODO: Implement
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
return [];
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final children = List<XMLNode>.empty(growable: true);
final marker = extensions.get<ChatMarkerData>();
if (marker != null) {
children.add(marker.toXML());
}
final markable = extensions.get<MarkableData>();
if (markable != null) {
children.add(markable.toXML());
}
return children;
}
@override
@ -92,8 +149,6 @@ class ChatMarkerManager extends XmppManagerBase {
await super.postRegisterCallback();
// Register the sending callback
getAttributes()
.getManagerById<MessageManager>(messageManager)
?.registerMessageSendingCallback(_messageSendingCallback);
getAttributes().getManagerById<MessageManager>(messageManager)?.registerMessageSendingCallback(_messageSendingCallback);
}
}

View File

@ -54,6 +54,13 @@ enum MessageProcessingHint {
}
}
class MessageProcessingHintData implements StanzaHandlerExtension {
const MessageProcessingHintData(this.hints);
/// The attached message processing hints.
final List<MessageProcessingHint> hints;
}
class MessageProcessingHintManager extends XmppManagerBase {
MessageProcessingHintManager() : super(messageProcessingHintManager);
@ -76,18 +83,19 @@ class MessageProcessingHintManager extends XmppManagerBase {
Stanza stanza,
StanzaHandlerData state,
) async {
// TODO(Unknown): Do we need to consider multiple hints?
final element = stanza.findTagsByXmlns(messageProcessingHintsXmlns).first;
return state..extensions.set(MessageProcessingHint.fromName(element.tag));
final elements = stanza.findTagsByXmlns(messageProcessingHintsXmlns);
return state
..extensions.set(
MessageProcessingHintData(
elements.map((element) => MessageProcessingHint.fromName(element.tag)).toList(),
),
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
// TODO(Unknown): Do we need to consider multiple hints?
final data = extensions.get<MessageProcessingHint>();
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<MessageProcessingHintData>();
return data != null
? [
data.toXML(),
]
? data.hints.map((hint) => hint.toXML()).toList()
: [];
}

View File

@ -34,7 +34,7 @@ class StanzaId {
}
}
class StableIdData {
class StableIdData implements StanzaHandlerExtension {
const StableIdData(this.originId, this.stanzaIds);
/// <origin-id />
@ -123,7 +123,7 @@ class StableIdManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<StableIdData>();
return data != null ? data.toXML() : [];
}

View File

@ -6,7 +6,7 @@ import 'package:moxxmpp/src/namespaces.dart';
import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
enum ExplicitEncryptionType {
enum ExplicitEncryptionType implements StanzaHandlerExtension {
otr,
legacyOpenPGP,
openPGP,

View File

@ -7,7 +7,7 @@ import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/xeps/staging/extensible_file_thumbnails.dart';
class StatelessMediaSharingData {
class StatelessMediaSharingData implements StanzaHandlerExtension {
const StatelessMediaSharingData({
required this.mediaType,
required this.size,

View File

@ -8,7 +8,7 @@ import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
class MessageRetractionData {
class MessageRetractionData implements StanzaHandlerExtension {
MessageRetractionData(this.id, this.fallback);
/// A potential fallback message to set the body to when retracting.
@ -63,7 +63,7 @@ class MessageRetractionManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<MessageRetractionData>();
return data != null
? [

View File

@ -8,7 +8,7 @@ import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
class MessageReactions {
class MessageReactions implements StanzaHandlerExtension {
const MessageReactions(this.messageId, this.emojis);
final String messageId;
final List<String> emojis;
@ -69,7 +69,7 @@ class MessageReactionsManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<MessageReactions>();
return data != null
? [

View File

@ -73,7 +73,7 @@ List<StatelessFileSharingSource> processStatelessFileSharingSources(
return sources;
}
class StatelessFileSharingData {
class StatelessFileSharingData implements StanzaHandlerExtension {
const StatelessFileSharingData(
this.metadata,
this.sources, {
@ -140,7 +140,7 @@ class SFSManager extends XmppManagerBase {
@override
Future<bool> isSupported() async => true;
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<StatelessFileSharingData>();
if (data == null) {
return [];

View File

@ -229,7 +229,7 @@ class StickerPack {
}
}
class StickersData {
class StickersData implements StanzaHandlerExtension {
const StickersData(this.stickerPackId, this.sticker);
/// The id of the sticker pack the referenced sticker is from.
@ -270,7 +270,7 @@ class StickersManager extends XmppManagerBase {
);
}
List<XMLNode> _messageSendingCallback(TypedMap extensions) {
List<XMLNode> _messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<StickersData>();
return data != null
? [

View File

@ -11,7 +11,7 @@ import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/util/typed_map.dart';
/// A reply to a message.
class ReplyData {
class ReplyData implements StanzaHandlerExtension {
const ReplyData(
this.id, {
this.body,
@ -103,7 +103,7 @@ class MessageRepliesManager extends XmppManagerBase {
Future<bool> isSupported() async => true;
@visibleForTesting
List<XMLNode> messageSendingCallback(TypedMap extensions) {
List<XMLNode> messageSendingCallback(TypedMap<StanzaHandlerExtension> extensions) {
final data = extensions.get<ReplyData>();
return data != null
? [

View File

@ -1,12 +1,14 @@
import 'package:moxxmpp/src/util/typed_map.dart';
import 'package:test/test.dart';
class TestType1 {
abstract class BaseType {}
class TestType1 implements BaseType {
const TestType1(this.i);
final int i;
}
class TestType2 {
class TestType2 implements BaseType {
const TestType2(this.j);
final bool j;
}
@ -14,7 +16,7 @@ class TestType2 {
void main() {
test('Test storing data in the type map', () {
// Set
final map = TypedMap()
final map = TypedMap<BaseType>()
..set(const TestType1(1))
..set(const TestType2(false));