Compare commits
No commits in common. "d7723615fe311a2fc8028946fd27c091848d788d" and "9223a7d4032deb54d144e3f49d19788db3218da2" have entirely different histories.
d7723615fe
...
9223a7d403
@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:moxlib/moxlib.dart';
|
|
||||||
import 'package:moxxmpp/src/buffer.dart';
|
import 'package:moxxmpp/src/buffer.dart';
|
||||||
import 'package:moxxmpp/src/errors.dart';
|
import 'package:moxxmpp/src/errors.dart';
|
||||||
import 'package:moxxmpp/src/events.dart';
|
import 'package:moxxmpp/src/events.dart';
|
||||||
@ -73,23 +72,6 @@ class XmppConnectionResult {
|
|||||||
final XmppError? error;
|
final XmppError? error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@immutable
|
|
||||||
class _StanzaAwaitableData {
|
|
||||||
const _StanzaAwaitableData(this.sentTo, this.id);
|
|
||||||
final String sentTo;
|
|
||||||
final String id;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => sentTo.hashCode ^ id.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator==(Object other) {
|
|
||||||
return other is _StanzaAwaitableData &&
|
|
||||||
other.sentTo == sentTo &&
|
|
||||||
other.id == id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class XmppConnection {
|
class XmppConnection {
|
||||||
/// [_socket] is for debugging purposes.
|
/// [_socket] is for debugging purposes.
|
||||||
/// [connectionPingDuration] is the duration after which a ping will be sent to keep
|
/// [connectionPingDuration] is the duration after which a ping will be sent to keep
|
||||||
@ -146,7 +128,7 @@ class XmppConnection {
|
|||||||
/// A policy on how to reconnect
|
/// A policy on how to reconnect
|
||||||
final ReconnectionPolicy _reconnectionPolicy;
|
final ReconnectionPolicy _reconnectionPolicy;
|
||||||
/// A list of stanzas we are tracking with its corresponding critical section
|
/// A list of stanzas we are tracking with its corresponding critical section
|
||||||
final Map<_StanzaAwaitableData, Completer<XMLNode>> _awaitingResponse;
|
final Map<String, Completer<XMLNode>> _awaitingResponse;
|
||||||
final Lock _awaitingResponseLock;
|
final Lock _awaitingResponseLock;
|
||||||
|
|
||||||
/// Helpers
|
/// Helpers
|
||||||
@ -446,10 +428,9 @@ class XmppConnection {
|
|||||||
/// 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 }) async {
|
||||||
assert(implies(addId == false && stanza.id == null, !awaitable), 'Cannot await a stanza with no id');
|
|
||||||
|
|
||||||
// Add extra data in case it was not set
|
|
||||||
var stanza_ = stanza;
|
var stanza_ = stanza;
|
||||||
|
|
||||||
|
// Add extra data in case it was not set
|
||||||
if (addId && (stanza_.id == null || stanza_.id == '')) {
|
if (addId && (stanza_.id == null || stanza_.id == '')) {
|
||||||
stanza_ = stanza.copyWith(id: generateId());
|
stanza_ = stanza.copyWith(id: generateId());
|
||||||
}
|
}
|
||||||
@ -467,6 +448,8 @@ class XmppConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final id = stanza_.id!;
|
||||||
|
|
||||||
_log.fine('Running pre stanza handlers..');
|
_log.fine('Running pre stanza handlers..');
|
||||||
final data = await _runOutgoingPreStanzaHandlers(
|
final data = await _runOutgoingPreStanzaHandlers(
|
||||||
stanza_,
|
stanza_,
|
||||||
@ -504,45 +487,42 @@ class XmppConnection {
|
|||||||
final stanzaString = data.stanza.toXml();
|
final stanzaString = data.stanza.toXml();
|
||||||
|
|
||||||
// ignore: cascade_invocations
|
// ignore: cascade_invocations
|
||||||
_log.fine('Attempting to acquire lock for ${data.stanza.id}...');
|
_log.fine('Attempting to acquire lock for $id...');
|
||||||
// TODO(PapaTutuWawa): Handle this much more graceful
|
// TODO(PapaTutuWawa): Handle this much more graceful
|
||||||
var future = Future.value(XMLNode(tag: 'not-used'));
|
var future = Future.value(XMLNode(tag: 'not-used'));
|
||||||
await _awaitingResponseLock.synchronized(() async {
|
await _awaitingResponseLock.synchronized(() async {
|
||||||
_log.fine('Lock acquired for ${data.stanza.id}');
|
_log.fine('Lock acquired for $id');
|
||||||
|
if (awaitable) {
|
||||||
|
_awaitingResponse[id] = Completer();
|
||||||
|
}
|
||||||
|
|
||||||
_StanzaAwaitableData? key;
|
// This uses the StreamManager to behave like a send queue
|
||||||
if (awaitable) {
|
if (await _canSendData()) {
|
||||||
key = _StanzaAwaitableData(data.stanza.to!, data.stanza.id!);
|
_socket.write(stanzaString);
|
||||||
_awaitingResponse[key] = Completer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This uses the StreamManager to behave like a send queue
|
// Try to ack every stanza
|
||||||
if (await _canSendData()) {
|
// NOTE: Here we have send an Ack request nonza. This is now done by StreamManagementManager when receiving the StanzaSentEvent
|
||||||
_socket.write(stanzaString);
|
} else {
|
||||||
|
_log.fine('_canSendData() returned false.');
|
||||||
|
}
|
||||||
|
|
||||||
// Try to ack every stanza
|
_log.fine('Running post stanza handlers..');
|
||||||
// NOTE: Here we have send an Ack request nonza. This is now done by StreamManagementManager when receiving the StanzaSentEvent
|
await _runOutgoingPostStanzaHandlers(
|
||||||
} else {
|
|
||||||
_log.fine('_canSendData() returned false.');
|
|
||||||
}
|
|
||||||
|
|
||||||
_log.fine('Running post stanza handlers..');
|
|
||||||
await _runOutgoingPostStanzaHandlers(
|
|
||||||
stanza_,
|
|
||||||
initial: StanzaHandlerData(
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
stanza_,
|
stanza_,
|
||||||
),
|
initial: StanzaHandlerData(
|
||||||
);
|
false,
|
||||||
_log.fine('Done');
|
false,
|
||||||
|
null,
|
||||||
|
stanza_,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_log.fine('Done');
|
||||||
|
|
||||||
if (awaitable) {
|
if (awaitable) {
|
||||||
future = _awaitingResponse[key]!.future;
|
future = _awaitingResponse[id]!.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
_log.fine('Releasing lock for ${data.stanza.id}');
|
_log.fine('Releasing lock for $id');
|
||||||
});
|
});
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
@ -711,14 +691,10 @@ class XmppConnection {
|
|||||||
final id = incomingPreHandlers.stanza.attributes['id'] as String?;
|
final id = incomingPreHandlers.stanza.attributes['id'] as String?;
|
||||||
var awaited = false;
|
var awaited = false;
|
||||||
await _awaitingResponseLock.synchronized(() async {
|
await _awaitingResponseLock.synchronized(() async {
|
||||||
if (id != null && incomingPreHandlers.stanza.from != null) {
|
if (id != null && _awaitingResponse.containsKey(id)) {
|
||||||
final key = _StanzaAwaitableData(incomingPreHandlers.stanza.from!, id);
|
_awaitingResponse[id]!.complete(incomingPreHandlers.stanza);
|
||||||
final comp = _awaitingResponse[key];
|
_awaitingResponse.remove(id);
|
||||||
if (comp != null) {
|
awaited = true;
|
||||||
comp.complete(incomingPreHandlers.stanza);
|
|
||||||
_awaitingResponse.remove(key);
|
|
||||||
awaited = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:moxlib/moxlib.dart';
|
|
||||||
import 'package:moxxmpp/src/events.dart';
|
import 'package:moxxmpp/src/events.dart';
|
||||||
import 'package:moxxmpp/src/jid.dart';
|
import 'package:moxxmpp/src/jid.dart';
|
||||||
import 'package:moxxmpp/src/managers/base.dart';
|
import 'package:moxxmpp/src/managers/base.dart';
|
||||||
@ -21,7 +20,6 @@ import 'package:moxxmpp/src/xeps/xep_0444.dart';
|
|||||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
|
import 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
|
import 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0448.dart';
|
import 'package:moxxmpp/src/xeps/xep_0448.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0461.dart';
|
|
||||||
|
|
||||||
/// Data used to build a message stanza.
|
/// Data used to build a message stanza.
|
||||||
///
|
///
|
||||||
@ -142,11 +140,6 @@ class MessageManager extends XmppManagerBase {
|
|||||||
/// element to this id. If originId is non-null, then it will create an "origin-id"
|
/// 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.
|
/// child in the message stanza and set its id to originId.
|
||||||
void sendMessage(MessageDetails details) {
|
void sendMessage(MessageDetails details) {
|
||||||
assert(
|
|
||||||
implies(details.quoteBody != null, details.quoteFrom != null && details.quoteId != null),
|
|
||||||
'When quoting a message, then quoteFrom and quoteId must also be non-null',
|
|
||||||
);
|
|
||||||
|
|
||||||
final stanza = Stanza.message(
|
final stanza = Stanza.message(
|
||||||
to: details.to,
|
to: details.to,
|
||||||
type: 'chat',
|
type: 'chat',
|
||||||
@ -155,11 +148,11 @@ class MessageManager extends XmppManagerBase {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (details.quoteBody != null) {
|
if (details.quoteBody != null) {
|
||||||
final quote = QuoteData.fromBodies(details.quoteBody!, details.body!);
|
final fallback = '> ${details.quoteBody!}';
|
||||||
|
|
||||||
stanza
|
stanza
|
||||||
..addChild(
|
..addChild(
|
||||||
XMLNode(tag: 'body', text: quote.body),
|
XMLNode(tag: 'body', text: '$fallback\n${details.body}'),
|
||||||
)
|
)
|
||||||
..addChild(
|
..addChild(
|
||||||
XMLNode.xmlns(
|
XMLNode.xmlns(
|
||||||
@ -183,7 +176,7 @@ class MessageManager extends XmppManagerBase {
|
|||||||
tag: 'body',
|
tag: 'body',
|
||||||
attributes: <String, String>{
|
attributes: <String, String>{
|
||||||
'start': '0',
|
'start': '0',
|
||||||
'end': '${quote.fallbackLength}',
|
'end': '${fallback.length}'
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:meta/meta.dart';
|
|
||||||
import 'package:moxxmpp/src/managers/base.dart';
|
import 'package:moxxmpp/src/managers/base.dart';
|
||||||
import 'package:moxxmpp/src/managers/data.dart';
|
import 'package:moxxmpp/src/managers/data.dart';
|
||||||
import 'package:moxxmpp/src/managers/handlers.dart';
|
import 'package:moxxmpp/src/managers/handlers.dart';
|
||||||
@ -6,7 +5,6 @@ import 'package:moxxmpp/src/managers/namespaces.dart';
|
|||||||
import 'package:moxxmpp/src/namespaces.dart';
|
import 'package:moxxmpp/src/namespaces.dart';
|
||||||
import 'package:moxxmpp/src/stanza.dart';
|
import 'package:moxxmpp/src/stanza.dart';
|
||||||
|
|
||||||
/// Data summarizing the XEP-0461 data.
|
|
||||||
class ReplyData {
|
class ReplyData {
|
||||||
const ReplyData({
|
const ReplyData({
|
||||||
required this.to,
|
required this.to,
|
||||||
@ -14,57 +12,12 @@ class ReplyData {
|
|||||||
this.start,
|
this.start,
|
||||||
this.end,
|
this.end,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The bare JID to whom the reply applies to
|
|
||||||
final String to;
|
final String to;
|
||||||
|
|
||||||
/// The stanza ID of the message that is replied to
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
/// The start of the fallback body (inclusive)
|
|
||||||
final int? start;
|
final int? start;
|
||||||
|
|
||||||
/// The end of the fallback body (exclusive)
|
|
||||||
final int? end;
|
final int? end;
|
||||||
|
|
||||||
/// Applies the metadata to the received body [body] in order to remove the fallback.
|
|
||||||
/// If either [ReplyData.start] or [ReplyData.end] are null, then body is returned as
|
|
||||||
/// is.
|
|
||||||
String removeFallback(String body) {
|
|
||||||
if (start == null || end == null) return body;
|
|
||||||
|
|
||||||
return body.replaceRange(start!, end, '');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal class describing how to build a message with a quote fallback body.
|
|
||||||
@visibleForTesting
|
|
||||||
class QuoteData {
|
|
||||||
const QuoteData(this.body, this.fallbackLength);
|
|
||||||
|
|
||||||
/// Takes the body of the message we want to quote [quoteBody] and the content of
|
|
||||||
/// the reply [body] and computes the fallback body and its length.
|
|
||||||
factory QuoteData.fromBodies(String quoteBody, String body) {
|
|
||||||
final fallback = quoteBody
|
|
||||||
.split('\n')
|
|
||||||
.map((line) => '> $line\n')
|
|
||||||
.join();
|
|
||||||
|
|
||||||
return QuoteData(
|
|
||||||
'$fallback$body',
|
|
||||||
fallback.length,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The new body with fallback data at the beginning
|
|
||||||
final String body;
|
|
||||||
|
|
||||||
/// The length of the fallback data
|
|
||||||
final int fallbackLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A manager implementing support for parsing XEP-0461 metadata. The
|
|
||||||
/// MessageRepliesManager itself does not modify the body of the message.
|
|
||||||
class MessageRepliesManager extends XmppManagerBase {
|
class MessageRepliesManager extends XmppManagerBase {
|
||||||
@override
|
@override
|
||||||
String getName() => 'MessageRepliesManager';
|
String getName() => 'MessageRepliesManager';
|
||||||
|
@ -348,7 +348,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
StanzaExpectation(
|
StanzaExpectation(
|
||||||
"<iq to='user@example.com' type='get' id='a' xmlns='jabber:client' />",
|
"<iq to='user@example.com' type='get' id='a' xmlns='jabber:client' />",
|
||||||
"<iq from='user@example.com' type='result' id='a' />",
|
"<iq to='user@example.com' type='result' id='a' />",
|
||||||
ignoreId: true,
|
ignoreId: true,
|
||||||
adjustId: true,
|
adjustId: true,
|
||||||
),
|
),
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import 'package:moxxmpp/moxxmpp.dart';
|
|
||||||
import 'package:test/test.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
test('Test building a singleline quote', () {
|
|
||||||
final quote = QuoteData.fromBodies('Hallo Welt', 'Hello Earth!');
|
|
||||||
|
|
||||||
expect(quote.body, '> Hallo Welt\nHello Earth!');
|
|
||||||
expect(quote.fallbackLength, 13);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Test building a multiline quote', () {
|
|
||||||
final quote = QuoteData.fromBodies('Hallo Welt\nHallo Erde', 'How are you?');
|
|
||||||
|
|
||||||
expect(quote.body, '> Hallo Welt\n> Hallo Erde\nHow are you?');
|
|
||||||
expect(quote.fallbackLength, 26);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Applying a singleline quote', () {
|
|
||||||
final body = '> Hallo Welt\nHello right back!';
|
|
||||||
final reply = ReplyData(
|
|
||||||
to: '',
|
|
||||||
id: '',
|
|
||||||
start: 0,
|
|
||||||
end: 13,
|
|
||||||
);
|
|
||||||
|
|
||||||
final bodyWithoutFallback = reply.removeFallback(body);
|
|
||||||
expect(bodyWithoutFallback, 'Hello right back!');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Applying a multiline quote', () {
|
|
||||||
final body = "> Hallo Welt\n> How are you?\nI'm fine.\nThank you!";
|
|
||||||
final reply = ReplyData(
|
|
||||||
to: '',
|
|
||||||
id: '',
|
|
||||||
start: 0,
|
|
||||||
end: 28,
|
|
||||||
);
|
|
||||||
|
|
||||||
final bodyWithoutFallback = reply.removeFallback(body);
|
|
||||||
expect(bodyWithoutFallback, "I'm fine.\nThank you!");
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user