fix: Fix VCard and User Avatar queries being encrypted

This commit is contained in:
PapaTutuWawa 2022-12-25 13:05:16 +01:00
parent 298a8342b8
commit 09696c1c4d
4 changed files with 59 additions and 44 deletions

View File

@ -1,5 +1,4 @@
import 'dart:async';
import 'package:moxxmpp/src/connection.dart';
import 'package:moxxmpp/src/events.dart';
import 'package:moxxmpp/src/jid.dart';
@ -11,7 +10,6 @@ import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
class XmppManagerAttributes {
XmppManagerAttributes({
required this.sendStanza,
required this.sendNonza,

View File

@ -289,7 +289,7 @@ class DiscoManager extends XmppManagerBase {
}
/// Sends a disco info query to the (full) jid [entity], optionally with node=[node].
Future<Result<DiscoError, DiscoInfo>> discoInfoQuery(String entity, { String? node}) async {
Future<Result<DiscoError, DiscoInfo>> discoInfoQuery(String entity, { String? node, bool shouldEncrypt = true }) async {
final cacheKey = DiscoCacheKey(entity, node);
DiscoInfo? info;
Completer<Result<DiscoError, DiscoInfo>>? completer;
@ -316,6 +316,7 @@ class DiscoManager extends XmppManagerBase {
final stanza = await getAttributes().sendStanza(
buildDiscoInfoQueryStanza(entity, node),
encrypted: !shouldEncrypt,
);
final query = stanza.firstTag('query');
if (query == null) {
@ -359,9 +360,12 @@ class DiscoManager extends XmppManagerBase {
}
/// Sends a disco items query to the (full) jid [entity], optionally with node=[node].
Future<Result<DiscoError, List<DiscoItem>>> discoItemsQuery(String entity, { String? node }) async {
Future<Result<DiscoError, List<DiscoItem>>> discoItemsQuery(String entity, { String? node, bool shouldEncrypt = true }) async {
final stanza = await getAttributes()
.sendStanza(buildDiscoItemsQueryStanza(entity, node: node)) as Stanza;
.sendStanza(
buildDiscoItemsQueryStanza(entity, node: node),
encrypted: !shouldEncrypt,
) as Stanza;
final query = stanza.firstTag('query');
if (query == null) return Result(InvalidResponseDiscoError());

View File

@ -7,15 +7,20 @@ import 'package:moxxmpp/src/managers/namespaces.dart';
import 'package:moxxmpp/src/namespaces.dart';
import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/types/result.dart';
abstract class VCardError {}
class UnknownVCardError extends VCardError {}
class InvalidVCardError extends VCardError {}
class VCardPhoto {
const VCardPhoto({ this.binval });
final String? binval;
}
class VCard {
const VCard({ this.nickname, this.url, this.photo });
final String? nickname;
final String? url;
@ -23,7 +28,6 @@ class VCard {
}
class VCardManager extends XmppManagerBase {
VCardManager() : _lastHash = {}, super();
final Map<String, String> _lastHash;
@ -59,12 +63,18 @@ class VCardManager extends XmppManagerBase {
final lastHash = _lastHash[from];
if (lastHash != hash) {
_lastHash[from] = hash;
final vcard = await requestVCard(from);
final vcardResult = await requestVCard(from);
if (vcard != null) {
final binval = vcard.photo?.binval;
if (vcardResult.isType<VCard>()) {
final binval = vcardResult.get<VCard>().photo?.binval;
if (binval != null) {
getAttributes().sendEvent(AvatarUpdatedEvent(jid: from, base64: binval, hash: hash));
getAttributes().sendEvent(
AvatarUpdatedEvent(
jid: from,
base64: binval,
hash: hash,
),
);
} else {
logger.warning('No avatar data found');
}
@ -95,7 +105,7 @@ class VCardManager extends XmppManagerBase {
);
}
Future<VCard?> requestVCard(String jid) async {
Future<Result<VCardError, VCard>> requestVCard(String jid) async {
final result = await getAttributes().sendStanza(
Stanza.iq(
to: jid,
@ -107,12 +117,13 @@ class VCardManager extends XmppManagerBase {
)
],
),
encrypted: true,
);
if (result.attributes['type'] != 'result') return null;
if (result.attributes['type'] != 'result') return Result(UnknownVCardError());
final vcard = result.firstTag('vCard', xmlns: vCardTempXmlns);
if (vcard == null) return null;
if (vcard == null) return Result(UnknownVCardError());
return _parseVCard(vcard);
return Result(_parseVCard(vcard));
}
}

View File

@ -3,21 +3,24 @@ import 'package:moxxmpp/src/managers/base.dart';
import 'package:moxxmpp/src/managers/namespaces.dart';
import 'package:moxxmpp/src/namespaces.dart';
import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/types/result.dart';
import 'package:moxxmpp/src/xeps/xep_0030/errors.dart';
import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
import 'package:moxxmpp/src/xeps/xep_0060/errors.dart';
import 'package:moxxmpp/src/xeps/xep_0060/xep_0060.dart';
class UserAvatar {
abstract class AvatarError {}
class UnknownAvatarError extends AvatarError {}
class UserAvatar {
const UserAvatar({ required this.base64, required this.hash });
final String base64;
final String hash;
}
class UserAvatarMetadata {
const UserAvatarMetadata(
this.id,
this.length,
@ -65,27 +68,27 @@ class UserAvatarManager extends XmppManagerBase {
/// Requests the avatar from [jid]. Returns the avatar data if the request was
/// successful. Null otherwise
// TODO(Unknown): Migrate to Resultsv2
Future<UserAvatar?> getUserAvatar(String jid) async {
Future<Result<AvatarError, UserAvatar>> getUserAvatar(String jid) async {
final pubsub = _getPubSubManager();
final resultsRaw = await pubsub.getItems(jid, userAvatarDataXmlns);
if (resultsRaw.isType<PubSubError>()) return null;
if (resultsRaw.isType<PubSubError>()) return Result(UnknownAvatarError());
final results = resultsRaw.get<List<PubSubItem>>();
if (results.isEmpty) return null;
if (results.isEmpty) return Result(UnknownAvatarError());
final item = results[0];
return UserAvatar(
return Result(
UserAvatar(
base64: item.payload.innerText(),
hash: item.id,
),
);
}
/// Publish the avatar data, [base64], on the pubsub node using [hash] as
/// the item id. [hash] must be the SHA-1 hash of the image data, while
/// [base64] must be the base64-encoded version of the image data.
// TODO(Unknown): Migrate to Resultsv2
Future<bool> publishUserAvatar(String base64, String hash, bool public) async {
Future<Result<AvatarError, bool>> publishUserAvatar(String base64, String hash, bool public) async {
final pubsub = _getPubSubManager();
final result = await pubsub.publish(
getAttributes().getFullJID().toBare().toString(),
@ -101,14 +104,15 @@ class UserAvatarManager extends XmppManagerBase {
),
);
return !result.isType<PubSubError>();
if (result.isType<PubSubError>()) return Result(UnknownAvatarError());
return const Result(true);
}
/// Publish avatar metadata [metadata] to the User Avatar's metadata node. If [public]
/// is true, then the node will be set to an 'open' access model. If [public] is false,
/// then the node will be set to an 'roster' access model.
// TODO(Unknown): Migrate to Resultsv2
Future<bool> publishUserAvatarMetadata(UserAvatarMetadata metadata, bool public) async {
Future<Result<AvatarError, bool>> publishUserAvatarMetadata(UserAvatarMetadata metadata, bool public) async {
final pubsub = _getPubSubManager();
final result = await pubsub.publish(
getAttributes().getFullJID().toBare().toString(),
@ -135,39 +139,37 @@ class UserAvatarManager extends XmppManagerBase {
),
);
return result.isType<PubSubError>();
if (result.isType<PubSubError>()) return Result(UnknownAvatarError());
return const Result(true);
}
/// Subscribe the data and metadata node of [jid].
// TODO(Unknown): Migrate to Resultsv2
Future<bool> subscribe(String jid) async {
Future<Result<AvatarError, bool>> subscribe(String jid) async {
await _getPubSubManager().subscribe(jid, userAvatarDataXmlns);
await _getPubSubManager().subscribe(jid, userAvatarMetadataXmlns);
return true;
return const Result(true);
}
/// Unsubscribe the data and metadata node of [jid].
// TODO(Unknown): Migrate to Resultsv2
Future<bool> unsubscribe(String jid) async {
Future<Result<AvatarError, bool>> unsubscribe(String jid) async {
await _getPubSubManager().unsubscribe(jid, userAvatarDataXmlns);
await _getPubSubManager().subscribe(jid, userAvatarMetadataXmlns);
return true;
return const Result(true);
}
/// Returns the PubSub Id of an avatar after doing a disco#items query.
/// Note that this assumes that there is only one (1) item published on
/// the node.
// TODO(Unknown): Migrate to Resultsv2
Future<String?> getAvatarId(String jid) async {
Future<Result<AvatarError, String>> getAvatarId(String jid) async {
final disco = getAttributes().getManagerById(discoManager)! as DiscoManager;
final response = await disco.discoItemsQuery(jid, node: userAvatarDataXmlns);
if (response.isType<DiscoError>()) return null;
final response = await disco.discoItemsQuery(jid, node: userAvatarDataXmlns, shouldEncrypt: false);
if (response.isType<DiscoError>()) return Result(UnknownAvatarError());
final items = response.get<List<DiscoItem>>();
if (items.isEmpty) return null;
if (items.isEmpty) return Result(UnknownAvatarError());
return items.first.name;
return Result(items.first.name);
}
}