diff --git a/iris/src/xmpp/xmpp-im/client.cpp b/iris/src/xmpp/xmpp-im/client.cpp index 8ab2ed29c..f42c991af 100644 --- a/iris/src/xmpp/xmpp-im/client.cpp +++ b/iris/src/xmpp/xmpp-im/client.cpp @@ -286,7 +286,7 @@ void Client::setCapsOptimizationAllowed(bool allowed) { d->capsOptimization = al bool Client::capsOptimizationAllowed() const { - if (d->capsOptimization && d->active && d->serverInfoManager->features().hasCapsOptimize()) { + if (d->capsOptimization && d->active && d->serverInfoManager->server_features().hasCapsOptimize()) { auto it = d->resourceList.find(d->resource); return it != d->resourceList.end() && it->status().isAvailable(); } diff --git a/iris/src/xmpp/xmpp-im/xmpp_externalservicediscovery.cpp b/iris/src/xmpp/xmpp-im/xmpp_externalservicediscovery.cpp index 96f09e573..5bd33c048 100644 --- a/iris/src/xmpp/xmpp-im/xmpp_externalservicediscovery.cpp +++ b/iris/src/xmpp/xmpp-im/xmpp_externalservicediscovery.cpp @@ -250,7 +250,7 @@ ExternalServiceDiscovery::ExternalServiceDiscovery(Client *client) : client_(cli bool ExternalServiceDiscovery::isSupported() const { - return client_->serverInfoManager()->features().test("urn:xmpp:extdisco:2"); + return client_->serverInfoManager()->server_features().test("urn:xmpp:extdisco:2"); } void ExternalServiceDiscovery::services(QObject *ctx, ServicesCallback &&callback, std::chrono::minutes minTtl, diff --git a/iris/src/xmpp/xmpp-im/xmpp_features.cpp b/iris/src/xmpp/xmpp-im/xmpp_features.cpp index 8aa1f5bed..6c80a72df 100644 --- a/iris/src/xmpp/xmpp-im/xmpp_features.cpp +++ b/iris/src/xmpp/xmpp-im/xmpp_features.cpp @@ -214,6 +214,12 @@ bool Features::hasCapsOptimize() const { return test(QStringList() << QLatin1Str #define NS_DIRECT_MUC_INVITE "jabber:x:conference" bool Features::hasDirectMucInvite() const { return test(QStringList() << QLatin1String(NS_DIRECT_MUC_INVITE)); } +#define FID_AVATAR_TO_VCARD_CONVERSION "urn:xmpp:pep-vcard-conversion:0" +bool Features::hasAvatarConversion() const +{ + return test(QStringList() << QLatin1String(FID_AVATAR_TO_VCARD_CONVERSION)); +} + // custom Psi actions #define FID_ADD "psi:add" diff --git a/iris/src/xmpp/xmpp-im/xmpp_features.h b/iris/src/xmpp/xmpp-im/xmpp_features.h index 6b7f38850..dc26d064d 100644 --- a/iris/src/xmpp/xmpp-im/xmpp_features.h +++ b/iris/src/xmpp/xmpp-im/xmpp_features.h @@ -59,6 +59,7 @@ class Features { bool hasCaps() const; bool hasCapsOptimize() const; bool hasDirectMucInvite() const; + bool hasAvatarConversion() const; [[deprecated]] inline bool canRegister() const { return hasRegister(); } [[deprecated]] inline bool canSearch() const { return hasSearch(); } diff --git a/iris/src/xmpp/xmpp-im/xmpp_serverinfomanager.cpp b/iris/src/xmpp/xmpp-im/xmpp_serverinfomanager.cpp index d40ab4fca..88ab72cc7 100644 --- a/iris/src/xmpp/xmpp-im/xmpp_serverinfomanager.cpp +++ b/iris/src/xmpp/xmpp-im/xmpp_serverinfomanager.cpp @@ -266,12 +266,12 @@ void ServerInfoManager::server_disco_finished() { JT_DiscoInfo *jt = static_cast(sender()); if (jt->success()) { - _features = jt->item().features(); + _server_features = jt->item().features(); - if (_features.hasMulticast()) + if (_server_features.hasMulticast()) _multicastService = _client->jid().domain(); - _canMessageCarbons = _features.hasMessageCarbons(); + _canMessageCarbons = _server_features.hasMessageCarbons(); auto servInfo = jt->item().findExtension(XData::Data_Result, QLatin1String("http://jabber.org/network/serverinfo")); @@ -297,6 +297,7 @@ void ServerInfoManager::account_disco_finished() if (i.category == "pubsub" && i.type == "pep") _hasPEP = true; } + _account_features = jt->item().features(); emit featuresChanged(); } diff --git a/iris/src/xmpp/xmpp-im/xmpp_serverinfomanager.h b/iris/src/xmpp/xmpp-im/xmpp_serverinfomanager.h index 3c816e8e6..ccfa2a02b 100644 --- a/iris/src/xmpp/xmpp-im/xmpp_serverinfomanager.h +++ b/iris/src/xmpp/xmpp-im/xmpp_serverinfomanager.h @@ -80,7 +80,8 @@ class ServerInfoManager : public QObject { const QString &multicastService() const; bool hasPEP() const; - inline const Features &features() const { return _features; } + inline const Features &server_features() const { return _server_features; } + inline const Features &account_features() const { return _account_features; } bool canMessageCarbons() const; inline const QMap &extraServerInfo() const { return _extraServerInfo; } @@ -126,7 +127,8 @@ private slots: private: XMPP::Client *_client = nullptr; CapsSpec _caps; - Features _features; + Features _server_features; + Features _account_features; QString _multicastService; QMap _extraServerInfo; // XEP-0128, XEP-0157 diff --git a/psi.doap b/psi.doap index 0953e9128..2db07992a 100644 --- a/psi.doap +++ b/psi.doap @@ -284,7 +284,9 @@ - complete + partial + 1.1.4 + Loading avatars from URLs is not supported at the moment. @@ -740,6 +742,15 @@ + + + + + complete + 1.0.0 + + + diff --git a/src/avatars.cpp b/src/avatars.cpp index 63146e2cb..3a55a55fd 100644 --- a/src/avatars.cpp +++ b/src/avatars.cpp @@ -40,6 +40,7 @@ #include "pixmaputil.h" #include "psiaccount.h" #include "vcardfactory.h" +#include "xmpp/xmpp-im/xmpp_serverinfomanager.h" #include #include @@ -1064,7 +1065,15 @@ void AvatarFactory::publish_success(const QString &n, const PubSubItem &item) info_el.setAttribute("type", image2type(d->selfAvatarData_)); meta_el.appendChild(info_el); account()->pepManager()->publish(PEP_AVATAR_METADATA_NS, PubSubItem(d->selfAvatarHash_, meta_el)); + if (account()->client()->serverInfoManager()->account_features().hasAvatarConversion()) { + VCardFactory::instance()->setPhoto(account()->jid(), d->selfAvatarData_, {}); + } d->selfAvatarData_.clear(); // we don't need it anymore + } else if (n == PEP_AVATAR_METADATA_NS) { + bool removed = item.payload().firstChildElement("metadata").firstChildElement("info").isNull(); + if (account()->client()->serverInfoManager()->account_features().hasAvatarConversion() && removed) { + VCardFactory::instance()->deletePhoto(account()->jid(), {}); + } } } diff --git a/src/psiaccount.cpp b/src/psiaccount.cpp index a6373eadf..7d258e344 100644 --- a/src/psiaccount.cpp +++ b/src/psiaccount.cpp @@ -2374,7 +2374,7 @@ void PsiAccount::serverFeaturesChanged() j->go(true); } - if (d->client->serverInfoManager()->features().hasVCard() && !d->vcardChecked) { + if (d->client->serverInfoManager()->server_features().hasVCard() && !d->vcardChecked) { // Get the vcard const VCard vcard = VCardFactory::instance()->vcard(d->jid); if (PsiOptions::instance()->getOption("options.vcard.query-own-vcard-on-login").toBool() || vcard.isEmpty() diff --git a/src/vcardfactory.cpp b/src/vcardfactory.cpp index e3ea9ca57..3f91d2212 100644 --- a/src/vcardfactory.cpp +++ b/src/vcardfactory.cpp @@ -235,6 +235,40 @@ VCardRequest *VCardFactory::getVCard(PsiAccount *account, const Jid &jid, Flags return queuedLoader_->enqueue(account, jid, flags, QueuedLoader::HighPriority); } +void VCardFactory::setPhoto(const Jid &j, const QByteArray &photo, Flags flags) +{ + VCard vc; + Jid sj; + if (flags & MucUser) { + sj = j; + vc = mucVcard(j); + } else { + sj = j.withResource({}); + vc = vcard(sj); + } + if (vc && vc.photo() != photo) { + vc.setPhoto({}); + saveVCard(sj, vc, flags); + } +} + +void VCardFactory::deletePhoto(const Jid &j, Flags flags) +{ + VCard vc; + Jid sj; + if (flags & MucUser) { + sj = j; + vc = mucVcard(j); + } else { + sj = j.withResource({}); + vc = vcard(sj); + } + if (vc && !vc.photo().isEmpty()) { + vc.setPhoto({}); + saveVCard(sj, vc, flags); + } +} + void VCardFactory::ensureVCardPhotoUpdated(PsiAccount *acc, const Jid &jid, Flags flags, const QByteArray &newPhotoHash) { VCard vc; diff --git a/src/vcardfactory.h b/src/vcardfactory.h index 51d96ccd2..0a4a73e86 100644 --- a/src/vcardfactory.h +++ b/src/vcardfactory.h @@ -58,6 +58,9 @@ class VCardFactory : public QObject { JT_VCard *setVCard(const PsiAccount *account, const VCard &v, const Jid &targetJid, VCardFactory::Flags flags); VCardRequest *getVCard(PsiAccount *account, const Jid &, VCardFactory::Flags flags = {}); + void setPhoto(const Jid &j, const QByteArray &photo, Flags flags); + void deletePhoto(const Jid &j, Flags flags); + // 1. check if it's needed to do a request, // 2. enqueue request if necessary (no vcard, or if hash doesn't match) // 3. vcardChanged() will be sent as usually when vcard is updated diff --git a/version b/version index dec709184..94ff173ae 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.5.1947 (2024-06-06, a7848ee9) +1.5.1949 (2024-06-06, 6b5ba1c6)