feat(xep): Also potentially return "generic" stanza errors

This commit is contained in:
PapaTutuWawa 2023-06-29 21:12:30 +02:00
parent 77a1acb0e7
commit a928c5c877
4 changed files with 75 additions and 41 deletions

View File

@ -17,6 +17,7 @@
- **BREAKING**: Removed `PresenceReceivedEvent`. Use a manager registering handlers with priority greater than `[PresenceManager.presenceHandlerPriority]` instead.
- **BREAKING**: `ChatState.toString()` is now `ChatState.toName()`
- **BREAKING**: Overriding `BaseOmemoManager` is no longer required. `OmemoManager` now takes callback methods instead.
- Removed `ErrorResponseDiscoError` from the possible XEP-0030 errors.
## 0.3.1

View File

@ -47,28 +47,57 @@ class StanzaDetails {
final TypedMap<StanzaHandlerExtension>? postSendExtensions;
}
/// A simple description of the <error /> element that may be inside a stanza
class StanzaError {
StanzaError(this.type, this.error);
String type;
String error;
/// A general error type for errors.
abstract class StanzaError {
static StanzaError? fromXMLNode(XMLNode node) {
final error = node.firstTag('error');
if (error == null) {
return null;
}
final specificError = error.firstTagByXmlns(fullStanzaXmlns);
if (specificError == null) {
return UnknownStanzaError();
}
switch (specificError.tag) {
case RemoteServerNotFoundError.tag:
return RemoteServerNotFoundError();
case RemoteServerTimeoutError.tag:
return RemoteServerTimeoutError();
case ServiceUnavailableError.tag:
return ServiceUnavailableError();
}
return UnknownStanzaError();
}
/// Returns a StanzaError if [stanza] contains a <error /> element. If not, returns
/// null.
static StanzaError? fromStanza(Stanza stanza) {
final error = stanza.firstTag('error');
if (error == null) return null;
final stanzaError = error.firstTagByXmlns(fullStanzaXmlns);
if (stanzaError == null) return null;
return StanzaError(
error.attributes['type']! as String,
stanzaError.tag,
);
return fromXMLNode(stanza);
}
}
/// Recipient does not provide a given service.
/// https://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions-service-unavailable
class ServiceUnavailableError extends StanzaError {
static const tag = 'service-unavailable';
}
/// Could not connect to the remote server.
/// https://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions-remote-server-not-found
class RemoteServerNotFoundError extends StanzaError {
static const tag = 'remote-server-not-found';
}
/// The connection to the remote server timed out.
/// https://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions-remote-server-timeout
class RemoteServerTimeoutError extends StanzaError {
static const tag = 'remote-server-timeout';
}
/// An unknown error.
class UnknownStanzaError extends StanzaError {}
class Stanza extends XMLNode {
// ignore: use_super_parameters
Stanza({

View File

@ -1,7 +1,10 @@
abstract class DiscoError {}
import 'package:moxxmpp/src/stanza.dart';
/// Base type for disco-related errors.
abstract class DiscoError extends StanzaError {}
/// An unspecified error that is not covered by another [DiscoError].
class UnknownDiscoError extends DiscoError {}
/// The received disco response is invalid in some shape or form.
class InvalidResponseDiscoError extends DiscoError {}
class ErrorResponseDiscoError extends DiscoError {}

View File

@ -44,11 +44,11 @@ class DiscoManager extends XmppManagerBase {
final Map<DiscoCacheKey, DiscoInfo> _discoInfoCache = {};
/// The tracker for tracking disco#info queries that are in flight.
final WaitForTracker<DiscoCacheKey, Result<DiscoError, DiscoInfo>>
final WaitForTracker<DiscoCacheKey, Result<StanzaError, DiscoInfo>>
_discoInfoTracker = WaitForTracker();
/// The tracker for tracking disco#info queries that are in flight.
final WaitForTracker<DiscoCacheKey, Result<DiscoError, List<DiscoItem>>>
final WaitForTracker<DiscoCacheKey, Result<StanzaError, List<DiscoItem>>>
_discoItemsTracker = WaitForTracker();
/// Cache lock
@ -67,7 +67,7 @@ class DiscoManager extends XmppManagerBase {
List<String> get features => _features;
@visibleForTesting
WaitForTracker<DiscoCacheKey, Result<DiscoError, DiscoInfo>>
WaitForTracker<DiscoCacheKey, Result<StanzaError, DiscoInfo>>
get infoTracker => _discoInfoTracker;
@override
@ -231,7 +231,7 @@ class DiscoManager extends XmppManagerBase {
Future<void> _exitDiscoInfoCriticalSection(
DiscoCacheKey key,
Result<DiscoError, DiscoInfo> result,
Result<StanzaError, DiscoInfo> result,
bool shouldCache,
) async {
await _cacheLock.synchronized(() async {
@ -252,7 +252,7 @@ class DiscoManager extends XmppManagerBase {
///
/// [shouldCache] indicates whether the successful result of the disco#info query
/// should be cached (true) or not(false).
Future<Result<DiscoError, DiscoInfo>> discoInfoQuery(
Future<Result<StanzaError, DiscoInfo>> discoInfoQuery(
JID entity, {
String? node,
bool shouldEncrypt = false,
@ -263,7 +263,7 @@ class DiscoManager extends XmppManagerBase {
final ecm = getAttributes()
.getManagerById<EntityCapabilitiesManager>(entityCapabilitiesManager);
final ffuture = await _cacheLock
.synchronized<Future<Future<Result<DiscoError, DiscoInfo>>?>?>(
.synchronized<Future<Future<Result<StanzaError, DiscoInfo>>?>?>(
() async {
// Check if we already know what the JID supports
if (_discoInfoCache.containsKey(cacheKey)) {
@ -297,16 +297,18 @@ class DiscoManager extends XmppManagerBase {
shouldEncrypt: shouldEncrypt,
),
))!;
final query = stanza.firstTag('query');
if (query == null) {
final result = Result<DiscoError, DiscoInfo>(InvalidResponseDiscoError());
// Error handling
if (stanza.attributes['type'] == 'error') {
final result =
Result<StanzaError, DiscoInfo>(StanzaError.fromXMLNode(stanza));
await _exitDiscoInfoCriticalSection(cacheKey, result, shouldCache);
return result;
}
if (stanza.attributes['type'] == 'error') {
//final error = stanza.firstTag('error');
final result = Result<DiscoError, DiscoInfo>(ErrorResponseDiscoError());
final query = stanza.firstTag('query');
if (query == null) {
final result = Result<DiscoError, DiscoInfo>(InvalidResponseDiscoError());
await _exitDiscoInfoCriticalSection(cacheKey, result, shouldCache);
return result;
}
@ -322,7 +324,7 @@ class DiscoManager extends XmppManagerBase {
}
/// Sends a disco items query to the (full) jid [entity], optionally with node=[node].
Future<Result<DiscoError, List<DiscoItem>>> discoItemsQuery(
Future<Result<StanzaError, List<DiscoItem>>> discoItemsQuery(
JID entity, {
String? node,
bool shouldEncrypt = false,
@ -340,19 +342,18 @@ class DiscoManager extends XmppManagerBase {
),
))!;
final query = stanza.firstTag('query');
if (query == null) {
// Error handling
if (stanza.attributes['type'] == 'error') {
final result =
Result<DiscoError, List<DiscoItem>>(InvalidResponseDiscoError());
Result<StanzaError, List<DiscoItem>>(StanzaError.fromXMLNode(stanza));
await _discoItemsTracker.resolve(key, result);
return result;
}
if (stanza.attributes['type'] == 'error') {
//final error = stanza.firstTag('error');
//print("Disco Items error: " + error.toXml());
final query = stanza.firstTag('query');
if (query == null) {
final result =
Result<DiscoError, List<DiscoItem>>(ErrorResponseDiscoError());
Result<DiscoError, List<DiscoItem>>(InvalidResponseDiscoError());
await _discoItemsTracker.resolve(key, result);
return result;
}
@ -419,7 +420,7 @@ class DiscoManager extends XmppManagerBase {
/// [entity] supports the disco feature [feature]. If not, returns false.
Future<bool> supportsFeature(JID entity, String feature) async {
final info = await discoInfoQuery(entity);
if (info.isType<DiscoError>()) return false;
if (info.isType<StanzaError>()) return false;
return info.get<DiscoInfo>().features.contains(feature);
}