feat: Allow easier responding to incoming stanzas

Should fix #20.
This commit is contained in:
PapaTutuWawa 2023-01-23 12:47:30 +01:00
parent a01022c217
commit c9c45baabc
15 changed files with 269 additions and 247 deletions

View File

@ -459,7 +459,7 @@ class XmppConnection {
/// If addId is true, then an 'id' attribute will be added to the stanza if [stanza] has /// If addId is true, then an 'id' attribute will be added to the stanza if [stanza] has
/// none. /// none.
// TODO(Unknown): if addId = false, the function crashes. // TODO(Unknown): if addId = false, the function crashes.
Future<XMLNode> sendStanza(Stanza stanza, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool awaitable = true, bool encrypted = false }) async { Future<XMLNode> sendStanza(Stanza stanza, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool awaitable = true, bool encrypted = false, bool forceEncryption = false, }) async {
assert(implies(addId == false && stanza.id == null, !awaitable), 'Cannot await a stanza with no id'); assert(implies(addId == false && stanza.id == null, !awaitable), 'Cannot await a stanza with no id');
// Add extra data in case it was not set // Add extra data in case it was not set
@ -490,6 +490,7 @@ class XmppConnection {
null, null,
stanza_, stanza_,
encrypted: encrypted, encrypted: encrypted,
forceEncryption: forceEncryption,
), ),
); );
_log.fine('Done'); _log.fine('Done');
@ -737,7 +738,7 @@ class XmppConnection {
), ),
); );
if (!incomingHandlers.done) { if (!incomingHandlers.done) {
handleUnhandledStanza(this, incomingPreHandlers.stanza); await handleUnhandledStanza(this, incomingPreHandlers);
} }
} }

View File

@ -1,10 +1,28 @@
import 'package:moxxmpp/src/connection.dart'; import 'package:moxxmpp/src/connection.dart';
import 'package:moxxmpp/src/managers/data.dart';
import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stanza.dart';
bool handleUnhandledStanza(XmppConnection conn, Stanza stanza) { /// Bounce a stanza if it was not handled by any manager. [conn] is the connection object
if (stanza.type != 'error' && stanza.type != 'result') { /// to use for sending the stanza. [data] is the StanzaHandlerData of the unhandled
conn.sendStanza(stanza.errorReply('cancel', 'feature-not-implemented')); /// stanza.
} Future<void> handleUnhandledStanza(XmppConnection conn, StanzaHandlerData data) async {
if (data.stanza.type != 'error' && data.stanza.type != 'result') {
final stanza = data.stanza.copyWith(
to: data.stanza.from,
from: data.stanza.to,
type: 'error',
children: [
buildErrorElement(
'cancel',
'feature-not-implemented',
),
],
);
return true; await conn.sendStanza(
stanza,
awaitable: false,
forceEncryption: data.encrypted,
);
}
} }

View File

@ -23,7 +23,7 @@ class XmppManagerAttributes {
required this.getNegotiatorById, required this.getNegotiatorById,
}); });
/// Send a stanza whose response can be awaited. /// Send a stanza whose response can be awaited.
final Future<XMLNode> Function(Stanza stanza, { StanzaFromType addFrom, bool addId, bool awaitable, bool encrypted}) sendStanza; final Future<XMLNode> Function(Stanza stanza, { StanzaFromType addFrom, bool addId, bool awaitable, bool encrypted, bool forceEncryption}) sendStanza;
/// Send a nonza. /// Send a nonza.
final void Function(XMLNode) sendNonza; final void Function(XMLNode) sendNonza;

View File

@ -1,6 +1,7 @@
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/events.dart';
import 'package:moxxmpp/src/managers/attributes.dart'; import 'package:moxxmpp/src/managers/attributes.dart';
import 'package:moxxmpp/src/managers/data.dart';
import 'package:moxxmpp/src/managers/handlers.dart'; import 'package:moxxmpp/src/managers/handlers.dart';
import 'package:moxxmpp/src/stringxml.dart'; import 'package:moxxmpp/src/stringxml.dart';
@ -79,4 +80,25 @@ abstract class XmppManagerBase {
return handled; return handled;
} }
/// Sends a reply of the stanza in [data] with [type]. Replaces the original stanza's
/// children with [children].
///
/// Note that this function currently only accepts IQ stanzas.
Future<void> reply(StanzaHandlerData data, String type, List<XMLNode> children) async {
assert(data.stanza.tag == 'iq', 'Reply makes little sense for non-IQ stanzas');
final stanza = data.stanza.copyWith(
to: data.stanza.from,
from: data.stanza.to,
type: type,
children: children,
);
await getAttributes().sendStanza(
stanza,
awaitable: false,
forceEncryption: data.encrypted,
);
}
} }

View File

@ -50,6 +50,11 @@ class StanzaHandlerData with _$StanzaHandlerData {
String? funCancellation, String? funCancellation,
// Whether the stanza was received encrypted // Whether the stanza was received encrypted
@Default(false) bool 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 // The stated type of encryption used, if any was used
ExplicitEncryptionType? encryptionType, ExplicitEncryptionType? encryptionType,
// Delayed Delivery // Delayed Delivery

View File

@ -48,6 +48,11 @@ mixin _$StanzaHandlerData {
String? get funCancellation => String? get funCancellation =>
throw _privateConstructorUsedError; // Whether the stanza was received encrypted throw _privateConstructorUsedError; // Whether the stanza was received encrypted
bool get encrypted => bool get encrypted =>
throw _privateConstructorUsedError; // 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 get forceEncryption =>
throw _privateConstructorUsedError; // The stated type of encryption used, if any was used throw _privateConstructorUsedError; // The stated type of encryption used, if any was used
ExplicitEncryptionType? get encryptionType => ExplicitEncryptionType? get encryptionType =>
throw _privateConstructorUsedError; // Delayed Delivery throw _privateConstructorUsedError; // Delayed Delivery
@ -94,6 +99,7 @@ abstract class $StanzaHandlerDataCopyWith<$Res> {
String? funReplacement, String? funReplacement,
String? funCancellation, String? funCancellation,
bool encrypted, bool encrypted,
bool forceEncryption,
ExplicitEncryptionType? encryptionType, ExplicitEncryptionType? encryptionType,
DelayedDelivery? delayedDelivery, DelayedDelivery? delayedDelivery,
Map<String, dynamic> other, Map<String, dynamic> other,
@ -132,6 +138,7 @@ class _$StanzaHandlerDataCopyWithImpl<$Res>
Object? funReplacement = freezed, Object? funReplacement = freezed,
Object? funCancellation = freezed, Object? funCancellation = freezed,
Object? encrypted = freezed, Object? encrypted = freezed,
Object? forceEncryption = freezed,
Object? encryptionType = freezed, Object? encryptionType = freezed,
Object? delayedDelivery = freezed, Object? delayedDelivery = freezed,
Object? other = freezed, Object? other = freezed,
@ -213,6 +220,10 @@ class _$StanzaHandlerDataCopyWithImpl<$Res>
? _value.encrypted ? _value.encrypted
: encrypted // ignore: cast_nullable_to_non_nullable : encrypted // ignore: cast_nullable_to_non_nullable
as bool, as bool,
forceEncryption: forceEncryption == freezed
? _value.forceEncryption
: forceEncryption // ignore: cast_nullable_to_non_nullable
as bool,
encryptionType: encryptionType == freezed encryptionType: encryptionType == freezed
? _value.encryptionType ? _value.encryptionType
: encryptionType // ignore: cast_nullable_to_non_nullable : encryptionType // ignore: cast_nullable_to_non_nullable
@ -271,6 +282,7 @@ abstract class _$$_StanzaHandlerDataCopyWith<$Res>
String? funReplacement, String? funReplacement,
String? funCancellation, String? funCancellation,
bool encrypted, bool encrypted,
bool forceEncryption,
ExplicitEncryptionType? encryptionType, ExplicitEncryptionType? encryptionType,
DelayedDelivery? delayedDelivery, DelayedDelivery? delayedDelivery,
Map<String, dynamic> other, Map<String, dynamic> other,
@ -311,6 +323,7 @@ class __$$_StanzaHandlerDataCopyWithImpl<$Res>
Object? funReplacement = freezed, Object? funReplacement = freezed,
Object? funCancellation = freezed, Object? funCancellation = freezed,
Object? encrypted = freezed, Object? encrypted = freezed,
Object? forceEncryption = freezed,
Object? encryptionType = freezed, Object? encryptionType = freezed,
Object? delayedDelivery = freezed, Object? delayedDelivery = freezed,
Object? other = freezed, Object? other = freezed,
@ -392,6 +405,10 @@ class __$$_StanzaHandlerDataCopyWithImpl<$Res>
? _value.encrypted ? _value.encrypted
: encrypted // ignore: cast_nullable_to_non_nullable : encrypted // ignore: cast_nullable_to_non_nullable
as bool, as bool,
forceEncryption: forceEncryption == freezed
? _value.forceEncryption
: forceEncryption // ignore: cast_nullable_to_non_nullable
as bool,
encryptionType: encryptionType == freezed encryptionType: encryptionType == freezed
? _value.encryptionType ? _value.encryptionType
: encryptionType // ignore: cast_nullable_to_non_nullable : encryptionType // ignore: cast_nullable_to_non_nullable
@ -442,6 +459,7 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
this.funReplacement, this.funReplacement,
this.funCancellation, this.funCancellation,
this.encrypted = false, this.encrypted = false,
this.forceEncryption = false,
this.encryptionType, this.encryptionType,
this.delayedDelivery, this.delayedDelivery,
final Map<String, dynamic> other = const <String, dynamic>{}, final Map<String, dynamic> other = const <String, dynamic>{},
@ -506,6 +524,13 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
@override @override
@JsonKey() @JsonKey()
final bool encrypted; final 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.
@override
@JsonKey()
final bool forceEncryption;
// The stated type of encryption used, if any was used // The stated type of encryption used, if any was used
@override @override
final ExplicitEncryptionType? encryptionType; final ExplicitEncryptionType? encryptionType;
@ -540,7 +565,7 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
@override @override
String toString() { String toString() {
return 'StanzaHandlerData(done: $done, cancel: $cancel, cancelReason: $cancelReason, stanza: $stanza, retransmitted: $retransmitted, sims: $sims, sfs: $sfs, oob: $oob, stableId: $stableId, reply: $reply, chatState: $chatState, isCarbon: $isCarbon, deliveryReceiptRequested: $deliveryReceiptRequested, isMarkable: $isMarkable, fun: $fun, funReplacement: $funReplacement, funCancellation: $funCancellation, encrypted: $encrypted, encryptionType: $encryptionType, delayedDelivery: $delayedDelivery, other: $other, messageRetraction: $messageRetraction, lastMessageCorrectionSid: $lastMessageCorrectionSid, messageReactions: $messageReactions, stickerPackId: $stickerPackId)'; return 'StanzaHandlerData(done: $done, cancel: $cancel, cancelReason: $cancelReason, stanza: $stanza, retransmitted: $retransmitted, sims: $sims, sfs: $sfs, oob: $oob, stableId: $stableId, reply: $reply, chatState: $chatState, isCarbon: $isCarbon, deliveryReceiptRequested: $deliveryReceiptRequested, isMarkable: $isMarkable, fun: $fun, funReplacement: $funReplacement, funCancellation: $funCancellation, encrypted: $encrypted, forceEncryption: $forceEncryption, encryptionType: $encryptionType, delayedDelivery: $delayedDelivery, other: $other, messageRetraction: $messageRetraction, lastMessageCorrectionSid: $lastMessageCorrectionSid, messageReactions: $messageReactions, stickerPackId: $stickerPackId)';
} }
@override @override
@ -572,6 +597,8 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
const DeepCollectionEquality() const DeepCollectionEquality()
.equals(other.funCancellation, funCancellation) && .equals(other.funCancellation, funCancellation) &&
const DeepCollectionEquality().equals(other.encrypted, encrypted) && const DeepCollectionEquality().equals(other.encrypted, encrypted) &&
const DeepCollectionEquality()
.equals(other.forceEncryption, forceEncryption) &&
const DeepCollectionEquality() const DeepCollectionEquality()
.equals(other.encryptionType, encryptionType) && .equals(other.encryptionType, encryptionType) &&
const DeepCollectionEquality() const DeepCollectionEquality()
@ -608,6 +635,7 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
const DeepCollectionEquality().hash(funReplacement), const DeepCollectionEquality().hash(funReplacement),
const DeepCollectionEquality().hash(funCancellation), const DeepCollectionEquality().hash(funCancellation),
const DeepCollectionEquality().hash(encrypted), const DeepCollectionEquality().hash(encrypted),
const DeepCollectionEquality().hash(forceEncryption),
const DeepCollectionEquality().hash(encryptionType), const DeepCollectionEquality().hash(encryptionType),
const DeepCollectionEquality().hash(delayedDelivery), const DeepCollectionEquality().hash(delayedDelivery),
const DeepCollectionEquality().hash(_other), const DeepCollectionEquality().hash(_other),
@ -641,6 +669,7 @@ abstract class _StanzaHandlerData implements StanzaHandlerData {
final String? funReplacement, final String? funReplacement,
final String? funCancellation, final String? funCancellation,
final bool encrypted, final bool encrypted,
final bool forceEncryption,
final ExplicitEncryptionType? encryptionType, final ExplicitEncryptionType? encryptionType,
final DelayedDelivery? delayedDelivery, final DelayedDelivery? delayedDelivery,
final Map<String, dynamic> other, final Map<String, dynamic> other,
@ -690,6 +719,11 @@ abstract class _StanzaHandlerData implements StanzaHandlerData {
String? get funCancellation; String? get funCancellation;
@override // Whether the stanza was received encrypted @override // Whether the stanza was received encrypted
bool get encrypted; bool get encrypted;
@override // 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 get forceEncryption;
@override // The stated type of encryption used, if any was used @override // The stated type of encryption used, if any was used
ExplicitEncryptionType? get encryptionType; ExplicitEncryptionType? get encryptionType;
@override // Delayed Delivery @override // Delayed Delivery

View File

@ -161,7 +161,11 @@ class RosterManager extends XmppManagerBase {
), ),
); );
await attrs.sendStanza(stanza.reply()); await reply(
state,
'result',
[],
);
return state.copyWith(done: true); return state.copyWith(done: true);
} }

View File

@ -114,40 +114,27 @@ class Stanza extends XMLNode {
children: children ?? this.children, children: children ?? this.children,
); );
} }
}
Stanza reply({ List<XMLNode> children = const [] }) { /// Build an <error /> element with a child <[condition] type="[type]" />. If [text]
return copyWith( /// is not null, then the condition element will contain a <text /> element with [text]
from: attributes['to'] as String?, /// as the body.
to: attributes['from'] as String?, XMLNode buildErrorElement(String type, String condition, { String? text }) {
type: tag == 'iq' ? 'result' : attributes['type'] as String?, return XMLNode(
children: children,
);
}
Stanza errorReply(String type, String condition, { String? text }) {
return copyWith(
from: attributes['to'] as String?,
to: attributes['from'] as String?,
type: 'error',
children: [
XMLNode(
tag: 'error', tag: 'error',
attributes: <String, dynamic>{ 'type': type }, attributes: <String, dynamic>{ 'type': type },
children: [ children: [
XMLNode.xmlns( XMLNode.xmlns(
tag: condition, tag: condition,
xmlns: fullStanzaXmlns, xmlns: fullStanzaXmlns,
children: text != null ?[ children: text != null ? [
XMLNode.xmlns( XMLNode.xmlns(
tag: 'text', tag: 'text',
xmlns: fullStanzaXmlns, xmlns: fullStanzaXmlns,
text: text, text: text,
) )
] : [], ] : [],
) ),
],
)
], ],
); );
}
} }

View File

@ -20,7 +20,6 @@ import 'package:synchronized/synchronized.dart';
@immutable @immutable
class DiscoCacheKey { class DiscoCacheKey {
const DiscoCacheKey(this.jid, this.node); const DiscoCacheKey(this.jid, this.node);
final String jid; final String jid;
final String? node; final String? node;
@ -35,7 +34,6 @@ class DiscoCacheKey {
} }
class DiscoManager extends XmppManagerBase { class DiscoManager extends XmppManagerBase {
DiscoManager() DiscoManager()
: _features = List.empty(growable: true), : _features = List.empty(growable: true),
_capHashCache = {}, _capHashCache = {},
@ -47,15 +45,19 @@ class DiscoManager extends XmppManagerBase {
/// Our features /// Our features
final List<String> _features; final List<String> _features;
// Map full JID to Capability hashes /// Map full JID to Capability hashes
final Map<String, CapabilityHashInfo> _capHashCache; final Map<String, CapabilityHashInfo> _capHashCache;
// Map capability hash to the disco info
/// Map capability hash to the disco info
final Map<String, DiscoInfo> _capHashInfoCache; final Map<String, DiscoInfo> _capHashInfoCache;
// Map full JID to Disco Info
/// Map full JID to Disco Info
final Map<DiscoCacheKey, DiscoInfo> _discoInfoCache; final Map<DiscoCacheKey, DiscoInfo> _discoInfoCache;
// Mapping the full JID to a list of running requests
/// Mapping the full JID to a list of running requests
final Map<DiscoCacheKey, List<Completer<Result<DiscoError, DiscoInfo>>>> _runningInfoQueries; final Map<DiscoCacheKey, List<Completer<Result<DiscoError, DiscoInfo>>>> _runningInfoQueries;
// Cache lock
/// Cache lock
final Lock _cacheLock; final Lock _cacheLock;
@visibleForTesting @visibleForTesting
@ -154,22 +156,19 @@ class DiscoManager extends XmppManagerBase {
if (stanza.type != 'get') return state; if (stanza.type != 'get') return state;
final presence = getAttributes().getManagerById(presenceManager)! as PresenceManager; final presence = getAttributes().getManagerById(presenceManager)! as PresenceManager;
final query = stanza.firstTag('query')!; final query = stanza.firstTag('query', xmlns: discoInfoXmlns)!;
final node = query.attributes['node'] as String?; final node = query.attributes['node'] as String?;
final capHash = await presence.getCapabilityHash(); final capHash = await presence.getCapabilityHash();
final isCapabilityNode = node == '${presence.capabilityHashNode}#$capHash'; final isCapabilityNode = node == '${presence.capabilityHashNode}#$capHash';
if (!isCapabilityNode && node != null) { if (!isCapabilityNode && node != null) {
await getAttributes().sendStanza(Stanza.iq( await reply(
to: stanza.from, state,
from: stanza.to, 'error',
id: stanza.id, [
type: 'error',
children: [
XMLNode.xmlns( XMLNode.xmlns(
tag: 'query', tag: 'query',
// TODO(PapaTutuWawa): Why are we copying the xmlns? xmlns: discoInfoXmlns,
xmlns: query.attributes['xmlns']! as String,
attributes: <String, String>{ attributes: <String, String>{
'node': node 'node': node
}, },
@ -185,16 +184,17 @@ class DiscoManager extends XmppManagerBase {
xmlns: fullStanzaXmlns, xmlns: fullStanzaXmlns,
) )
], ],
) ),
], ],
) );
,);
return state.copyWith(done: true); return state.copyWith(done: true);
} }
await getAttributes().sendStanza(stanza.reply( await reply(
children: [ state,
'result',
[
XMLNode.xmlns( XMLNode.xmlns(
tag: 'query', tag: 'query',
xmlns: discoInfoXmlns, xmlns: discoInfoXmlns,
@ -214,7 +214,7 @@ class DiscoManager extends XmppManagerBase {
], ],
), ),
], ],
),); );
return state.copyWith(done: true); return state.copyWith(done: true);
} }
@ -222,20 +222,16 @@ class DiscoManager extends XmppManagerBase {
Future<StanzaHandlerData> _onDiscoItemsRequest(Stanza stanza, StanzaHandlerData state) async { Future<StanzaHandlerData> _onDiscoItemsRequest(Stanza stanza, StanzaHandlerData state) async {
if (stanza.type != 'get') return state; if (stanza.type != 'get') return state;
final query = stanza.firstTag('query')!; final query = stanza.firstTag('query', xmlns: discoItemsXmlns)!;
if (query.attributes['node'] != null) { if (query.attributes['node'] != null) {
// TODO(Unknown): Handle the node we specified for XEP-0115 // TODO(Unknown): Handle the node we specified for XEP-0115
await getAttributes().sendStanza( await reply(
Stanza.iq( state,
to: stanza.from, 'error',
from: stanza.to, [
id: stanza.id,
type: 'error',
children: [
XMLNode.xmlns( XMLNode.xmlns(
tag: 'query', tag: 'query',
// TODO(PapaTutuWawa): Why copy the xmlns? xmlns: discoItemsXmlns,
xmlns: query.attributes['xmlns']! as String,
attributes: <String, String>{ attributes: <String, String>{
'node': query.attributes['node']! as String, 'node': query.attributes['node']! as String,
}, },
@ -253,21 +249,22 @@ class DiscoManager extends XmppManagerBase {
], ],
), ),
], ],
),
); );
return state.copyWith(done: true); return state.copyWith(done: true);
} }
await getAttributes().sendStanza( await reply(
stanza.reply( state,
children: [ 'result',
[
XMLNode.xmlns( XMLNode.xmlns(
tag: 'query', tag: 'query',
xmlns: discoItemsXmlns, xmlns: discoItemsXmlns,
), ),
], ],
),
); );
return state.copyWith(done: true); return state.copyWith(done: true);
} }

View File

@ -318,11 +318,12 @@ abstract class BaseOmemoManager extends XmppManagerBase {
} }
final toJid = JID.fromString(stanza.to!).toBare(); final toJid = JID.fromString(stanza.to!).toBare();
if (!(await shouldEncryptStanza(toJid, stanza))) { final shouldEncryptResult = await shouldEncryptStanza(toJid, stanza);
logger.finest('shouldEncryptStanza returned false for message to $toJid. Not encrypting.'); if (!shouldEncryptResult && !state.forceEncryption) {
logger.finest('Not encrypting stanza for $toJid: Both shouldEncryptStanza and forceEncryption are false.');
return state; return state;
} else { } else {
logger.finest('shouldEncryptStanza returned true for message to $toJid.'); logger.finest('Encrypting stanza for $toJid: shouldEncryptResult=$shouldEncryptResult, forceEncryption=${state.forceEncryption}');
} }
final toEncrypt = List<XMLNode>.empty(growable: true); final toEncrypt = List<XMLNode>.empty(growable: true);

View File

@ -1,50 +0,0 @@
import 'package:moxxmpp/moxxmpp.dart';
import 'package:test/test.dart';
void main() {
test('Make sure reply does not copy the children', () {
final stanza = Stanza.iq(
to: 'hallo',
from: 'world',
id: 'abc123',
type: 'get',
children: [
XMLNode(tag: 'test-tag'),
XMLNode(tag: 'test-tag2')
],
);
final reply = stanza.reply();
expect(reply.children, []);
expect(reply.type, 'result');
expect(reply.from, stanza.to);
expect(reply.to, stanza.from);
expect(reply.id, stanza.id);
});
test('Make sure reply includes the new children', () {
final stanza = Stanza.iq(
to: 'hallo',
from: 'world',
id: 'abc123',
type: 'get',
children: [
XMLNode(tag: 'test-tag'),
XMLNode(tag: 'test-tag2')
],
);
final reply = stanza.reply(
children: [
XMLNode.xmlns(
tag: 'test',
xmlns: 'test',
)
],
);
expect(reply.children.length, 1);
expect(reply.firstTag('test') != null, true);
});
}

View File

@ -17,7 +17,7 @@ Future<void> runOutgoingStanzaHandlers(StreamManagementManager man, Stanza stanz
XmppManagerAttributes mkAttributes(void Function(Stanza) callback) { XmppManagerAttributes mkAttributes(void Function(Stanza) callback) {
return XmppManagerAttributes( return XmppManagerAttributes(
sendStanza: (stanza, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool awaitable = true, bool encrypted = false }) async { sendStanza: (stanza, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool awaitable = true, bool encrypted = false, bool forceEncryption = false, }) async {
callback(stanza); callback(stanza);
return Stanza.message(); return Stanza.message();

View File

@ -5,7 +5,7 @@ import '../helpers/xmpp.dart';
void main() { void main() {
test("Test if we're vulnerable against CVE-2020-26547 style vulnerabilities", () async { test("Test if we're vulnerable against CVE-2020-26547 style vulnerabilities", () async {
final attributes = XmppManagerAttributes( final attributes = XmppManagerAttributes(
sendStanza: (stanza, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false }) async { sendStanza: (stanza, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false, bool forceEncryption = false, }) async {
// ignore: avoid_print // ignore: avoid_print
print('==> ${stanza.toXml()}'); print('==> ${stanza.toXml()}');
return XMLNode(tag: 'iq', attributes: { 'type': 'result' }); return XMLNode(tag: 'iq', attributes: { 'type': 'result' });

View File

@ -32,8 +32,9 @@ void main() {
test('Test setting the CSI state when CSI is unsupported', () { test('Test setting the CSI state when CSI is unsupported', () {
var nonzaSent = false; var nonzaSent = false;
final csi = CSIManager(); final csi = CSIManager();
csi.register(XmppManagerAttributes( csi.register(
sendStanza: (_, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false }) async => XMLNode(tag: 'hallo'), XmppManagerAttributes(
sendStanza: (_, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false, bool forceEncryption = false, }) async => XMLNode(tag: 'hallo'),
sendEvent: (event) {}, sendEvent: (event) {},
sendNonza: (nonza) { sendNonza: (nonza) {
nonzaSent = true; nonzaSent = true;
@ -60,8 +61,9 @@ void main() {
}); });
test('Test setting the CSI state when CSI is supported', () { test('Test setting the CSI state when CSI is supported', () {
final csi = CSIManager(); final csi = CSIManager();
csi.register(XmppManagerAttributes( csi.register(
sendStanza: (_, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false }) async => XMLNode(tag: 'hallo'), XmppManagerAttributes(
sendStanza: (_, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false, bool forceEncryption = false, }) async => XMLNode(tag: 'hallo'),
sendEvent: (event) {}, sendEvent: (event) {},
sendNonza: (nonza) { sendNonza: (nonza) {
expect(nonza.attributes['xmlns'] == csiXmlns, true, reason: "Expected only nonzas with XMLNS '$csiXmlns'"); expect(nonza.attributes['xmlns'] == csiXmlns, true, reason: "Expected only nonzas with XMLNS '$csiXmlns'");
@ -78,7 +80,8 @@ void main() {
getFullJID: () => JID.fromString('some.user@example.server/aaaaa'), getFullJID: () => JID.fromString('some.user@example.server/aaaaa'),
getSocket: () => StubTCPSocket(play: []), getSocket: () => StubTCPSocket(play: []),
getConnection: () => XmppConnection(TestingReconnectionPolicy(), StubTCPSocket(play: [])), getConnection: () => XmppConnection(TestingReconnectionPolicy(), StubTCPSocket(play: [])),
),); ),
);
csi.setActive(); csi.setActive();
csi.setInactive(); csi.setInactive();

View File

@ -9,7 +9,7 @@ Future<bool> testRosterManager(String bareJid, String resource, String stanzaStr
var eventTriggered = false; var eventTriggered = false;
final roster = RosterManager(TestingRosterStateManager('', [])); final roster = RosterManager(TestingRosterStateManager('', []));
roster.register(XmppManagerAttributes( roster.register(XmppManagerAttributes(
sendStanza: (_, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false }) async => XMLNode(tag: 'hallo'), sendStanza: (_, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false, bool forceEncryption = false, }) async => XMLNode(tag: 'hallo'),
sendEvent: (event) { sendEvent: (event) {
eventTriggered = true; eventTriggered = true;
}, },
@ -310,7 +310,7 @@ void main() {
var eventTriggered = false; var eventTriggered = false;
final roster = RosterManager(TestingRosterStateManager('', [])); final roster = RosterManager(TestingRosterStateManager('', []));
roster.register(XmppManagerAttributes( roster.register(XmppManagerAttributes(
sendStanza: (_, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false }) async => XMLNode(tag: 'hallo'), sendStanza: (_, { StanzaFromType addFrom = StanzaFromType.full, bool addId = true, bool retransmitted = false, bool awaitable = true, bool encrypted = false, bool forceEncryption = false, }) async => XMLNode(tag: 'hallo'),
sendEvent: (event) { sendEvent: (event) {
eventTriggered = true; eventTriggered = true;
}, },