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

View File

@ -47,28 +47,57 @@ class StanzaDetails {
final TypedMap<StanzaHandlerExtension>? postSendExtensions; final TypedMap<StanzaHandlerExtension>? postSendExtensions;
} }
/// A simple description of the <error /> element that may be inside a stanza /// A general error type for errors.
class StanzaError { abstract class StanzaError {
StanzaError(this.type, this.error); static StanzaError? fromXMLNode(XMLNode node) {
String type; final error = node.firstTag('error');
String 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) { static StanzaError? fromStanza(Stanza stanza) {
final error = stanza.firstTag('error'); return fromXMLNode(stanza);
if (error == null) return null;
final stanzaError = error.firstTagByXmlns(fullStanzaXmlns);
if (stanzaError == null) return null;
return StanzaError(
error.attributes['type']! as String,
stanzaError.tag,
);
} }
} }
/// 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 { class Stanza extends XMLNode {
// ignore: use_super_parameters // ignore: use_super_parameters
Stanza({ 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 {} class UnknownDiscoError extends DiscoError {}
/// The received disco response is invalid in some shape or form.
class InvalidResponseDiscoError extends DiscoError {} class InvalidResponseDiscoError extends DiscoError {}
class ErrorResponseDiscoError extends DiscoError {}

View File

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