Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libqxmpp for openSUSE:Factory checked in at 2025-03-31 11:39:54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libqxmpp (Old) and /work/SRC/openSUSE:Factory/.libqxmpp.new.2696 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libqxmpp" Mon Mar 31 11:39:54 2025 rev:37 rq:1265169 version:1.10.2 Changes: -------- --- /work/SRC/openSUSE:Factory/libqxmpp/libqxmpp.changes 2025-02-26 17:28:23.323951255 +0100 +++ /work/SRC/openSUSE:Factory/.libqxmpp.new.2696/libqxmpp.changes 2025-03-31 11:40:50.026327048 +0200 @@ -1,0 +2,7 @@ +Thu Mar 27 17:50:48 UTC 2025 - Michael Vetter <mvet...@suse.com> + +- Update to 1.10.2: + * RosterManager: Do not auto-accept Moved subscription requests to + comply with XEP #691 + +------------------------------------------------------------------- Old: ---- libqxmpp-1.10.1.tar.gz New: ---- libqxmpp-1.10.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libqxmpp.spec ++++++ --- /var/tmp/diff_new_pack.yJa9qE/_old 2025-03-31 11:40:51.054369647 +0200 +++ /var/tmp/diff_new_pack.yJa9qE/_new 2025-03-31 11:40:51.058369812 +0200 @@ -32,7 +32,7 @@ %endif %define sover 5 Name: libqxmpp%{?pkg_suffix} -Version: 1.10.1 +Version: 1.10.2 Release: 0 Summary: Qt XMPP Library License: LGPL-2.1-or-later @@ -117,6 +117,7 @@ # No need to build it twice + %package -n libqxmpp-doc Summary: Qxmpp library documentation Group: Documentation/HTML ++++++ libqxmpp-1.10.1.tar.gz -> libqxmpp-1.10.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qxmpp-1.10.1/CHANGELOG.md new/qxmpp-1.10.2/CHANGELOG.md --- old/qxmpp-1.10.1/CHANGELOG.md 2025-02-25 21:43:41.000000000 +0100 +++ new/qxmpp-1.10.2/CHANGELOG.md 2025-03-19 19:04:07.000000000 +0100 @@ -4,6 +4,11 @@ SPDX-License-Identifier: CC0-1.0 --> +QXmpp 1.10.2 (March 19, 2025) +----------------------------- + + - RosterManager: Do not auto-accept Moved subscription requests to comply with XEP (@melvo, #691) + QXmpp 1.10.1 (February 25, 2025) -------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qxmpp-1.10.1/CMakeLists.txt new/qxmpp-1.10.2/CMakeLists.txt --- old/qxmpp-1.10.1/CMakeLists.txt 2025-02-25 21:43:41.000000000 +0100 +++ new/qxmpp-1.10.2/CMakeLists.txt 2025-03-19 19:04:07.000000000 +0100 @@ -3,7 +3,7 @@ # SPDX-License-Identifier: CC0-1.0 cmake_minimum_required(VERSION 3.7) -project(qxmpp VERSION 1.10.1) +project(qxmpp VERSION 1.10.2) set(SO_VERSION 5) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qxmpp-1.10.1/doc/doap.xml new/qxmpp-1.10.2/doc/doap.xml --- old/qxmpp-1.10.1/doc/doap.xml 2025-02-25 21:43:41.000000000 +0100 +++ new/qxmpp-1.10.2/doc/doap.xml 2025-03-19 19:04:07.000000000 +0100 @@ -743,6 +743,13 @@ </implements> <release> <Version> + <revision>1.10.2</revision> + <created>2025-03-19</created> + <file-release rdf:resource='https://github.com/qxmpp-project/qxmpp/archive/refs/tags/v1.10.2.tar.gz'/> + </Version> + </release> + <release> + <Version> <revision>1.10.1</revision> <created>2025-02-25</created> <file-release rdf:resource='https://github.com/qxmpp-project/qxmpp/archive/refs/tags/v1.10.1.tar.gz'/> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qxmpp-1.10.1/src/client/QXmppMovedManager.cpp new/qxmpp-1.10.2/src/client/QXmppMovedManager.cpp --- old/qxmpp-1.10.1/src/client/QXmppMovedManager.cpp 2025-02-25 21:43:41.000000000 +0100 +++ new/qxmpp-1.10.2/src/client/QXmppMovedManager.cpp 2025-03-19 19:04:07.000000000 +0100 @@ -253,41 +253,38 @@ /// \endcond /// -/// Checks for moved elements in incoming subscription requests and verifies them. +/// Verifies an old JID in a received presence subscription request and removes it if it is invalid. /// /// This requires the QXmppRosterManager to be registered with the client. /// -/// \returns a task for the verification result if the subscription request contains a moved -/// element with an 'old-jid' that is already in the account's roster. +/// \param presence presence subscription request to be processed containing a non-empty old JID. /// -std::optional<QXmppTask<bool>> QXmppMovedManager::handleSubscriptionRequest(const QXmppPresence &presence) +/// \returns the processed presence subscription request +/// +QXmppTask<QXmppPresence> QXmppMovedManager::processSubscriptionRequest(QXmppPresence presence) { - // check for moved element - if (presence.oldJid().isEmpty()) { - return {}; - } + Q_ASSERT(!presence.oldJid().isEmpty()); - // find roster manager auto *rosterManager = client()->findExtension<QXmppRosterManager>(); Q_ASSERT(rosterManager); - // check subscription state of old-jid const auto entry = rosterManager->getRosterEntry(presence.oldJid()); switch (entry.subscriptionType()) { case QXmppRosterIq::Item::From: case QXmppRosterIq::Item::Both: - break; + return chain<QXmppPresence>(verifyStatement(presence.oldJid(), QXmppUtils::jidToBareJid(presence.from())), this, [this, presence = presence](Result &&result) mutable { + if (std::holds_alternative<QXmppError>(result)) { + warning(presence.from() + u" sent a presence subscription request with the invalid old JID "_s + presence.oldJid()); + presence.setOldJid({}); + } + + return presence; + }); default: - // The subscription state of the old JID needs to be either from or both, else ignore - // the moved element - return {}; + presence.setOldJid({}); + return makeReadyTask(std::move(presence)); } - - // return verification result - return chain<bool>(verifyStatement(presence.oldJid(), QXmppUtils::jidToBareJid(presence.from())), this, [this](Result &&result) mutable { - return std::holds_alternative<Success>(result); - }); } /// diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qxmpp-1.10.1/src/client/QXmppMovedManager.h new/qxmpp-1.10.2/src/client/QXmppMovedManager.h --- old/qxmpp-1.10.1/src/client/QXmppMovedManager.h 2025-02-25 21:43:41.000000000 +0100 +++ new/qxmpp-1.10.2/src/client/QXmppMovedManager.h 2025-03-19 19:04:07.000000000 +0100 @@ -41,7 +41,7 @@ /// \endcond private: - std::optional<QXmppTask<bool>> handleSubscriptionRequest(const QXmppPresence &presence); + QXmppTask<QXmppPresence> processSubscriptionRequest(QXmppPresence presence); void handleDiscoInfo(const QXmppDiscoveryIq &iq); Result movedJidsMatch(const QString &newBareJid, const QString &pepBareJid) const; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qxmpp-1.10.1/src/client/QXmppRosterManager.cpp new/qxmpp-1.10.2/src/client/QXmppRosterManager.cpp --- old/qxmpp-1.10.1/src/client/QXmppRosterManager.cpp 2025-02-25 21:43:41.000000000 +0100 +++ new/qxmpp-1.10.2/src/client/QXmppRosterManager.cpp 2025-03-19 19:04:07.000000000 +0100 @@ -74,11 +74,16 @@ /// The user can either accept the request by calling acceptSubscription() or refuse it /// by calling refuseSubscription(). /// +/// Since *QXmpp 1.10.2* only verified \xep{0283, Moved} old JIDs are passed in \a presence. If +/// verification fails or the given old JID is not valid, the attribute is cleared in the +/// QXmppPresence. See \ref rostermanager_moved "above" for more details. +/// /// \note If QXmppConfiguration::autoAcceptSubscriptions() is set to true or the subscription /// request is automatically accepted by the QXmppMovedManager, this signal will not be emitted. /// /// \param subscriberBareJid bare JID that wants to subscribe to the user's presence -/// \param presence presence stanza containing the reason / message (presence.statusText()) +/// \param presence presence stanza, e.g. containing the message (presence.statusText()), +/// \xep{0283, Moved} old JID or other information /// /// \since QXmpp 1.5 /// @@ -265,23 +270,7 @@ Q_EMIT presenceChanged(bareJid, resource); break; case QXmppPresence::Subscribe: { - // accept all incoming subscription requests if enabled - if (client()->configuration().autoAcceptSubscriptions()) { - handleSubscriptionRequest(bareJid, presence, true); - break; - } - - // check for XEP-0283: Moved subscription requests and verify them - if (auto *movedManager = client()->findExtension<QXmppMovedManager>()) { - if (auto verificationTask = movedManager->handleSubscriptionRequest(presence)) { - verificationTask->then(this, [this, presence, bareJid](bool valid) { - handleSubscriptionRequest(bareJid, presence, valid); - }); - break; - } - } - - handleSubscriptionRequest(bareJid, presence, false); + handleSubscriptionRequest(bareJid, presence); break; } default: @@ -289,18 +278,30 @@ } } -void QXmppRosterManager::handleSubscriptionRequest(const QString &bareJid, const QXmppPresence &presence, bool accept) +void QXmppRosterManager::handleSubscriptionRequest(const QString &bareJid, const QXmppPresence &presence) { - if (accept) { - // accept subscription request - acceptSubscription(bareJid); + auto notifyOnSubscriptionRequest = [this, bareJid](const QXmppPresence &presence) { + Q_EMIT subscriptionReceived(bareJid); + Q_EMIT subscriptionRequestReceived(bareJid, presence); + }; - // ask for reciprocal subscription + // Automatically accept all incoming subscription requests if enabled. + if (client()->configuration().autoAcceptSubscriptions()) { + acceptSubscription(bareJid); subscribe(bareJid); + return; + } + + // check for XEP-0283: Moved subscription requests and verify them + if (auto *movedManager = client()->findExtension<QXmppMovedManager>(); movedManager && !presence.oldJid().isEmpty()) { + movedManager->processSubscriptionRequest(presence).then(this, [this, notifyOnSubscriptionRequest](QXmppPresence &&presence) mutable { + notifyOnSubscriptionRequest(presence); + }); } else { - // let user decide whether to accept the subscription request - Q_EMIT subscriptionReceived(bareJid); - Q_EMIT subscriptionRequestReceived(bareJid, presence); + auto safePresence = presence; + safePresence.setOldJid({}); + + notifyOnSubscriptionRequest(safePresence); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qxmpp-1.10.1/src/client/QXmppRosterManager.h new/qxmpp-1.10.2/src/client/QXmppRosterManager.h --- old/qxmpp-1.10.1/src/client/QXmppRosterManager.h 2025-02-25 21:43:41.000000000 +0100 +++ new/qxmpp-1.10.2/src/client/QXmppRosterManager.h 2025-03-19 19:04:07.000000000 +0100 @@ -51,6 +51,33 @@ /// The \c presenceChanged() signal is emitted whenever the presence for a /// roster item changes. /// +/// \anchor rostermanager_moved +/// ## XEP-0283: Moved +/// +/// \xep{0283, Moved} provides a way to inform other users that an account has moved. This allows a +/// presence subscription request from a new account to be automatically linked to the old account. +/// However, this requires verification via a corresponding PubSub node on the old account. +/// +/// The roster manager automatically checks entries using the QXmppMovedManager. For this to work, +/// the moved manager must be added to the QXmppClient. If the moved manager is not found or the +/// specified old JID is incorrect, the entry in the presence subscription request will be removed. +/// +/// The received and verified presence subscription request is emitted via +/// subscriptionRequestReceived() and the old JID can be found in QXmppPresence::oldJid(). +/// It is safe to assume that this old JID is valid since QXmpp 1.10.2. +/// +/// Accepting moved subscription requests automatically is discouraged in +/// [section 4.3](https://xmpp.org/extensions/xep-0283.html#receive-notification-client) of the +/// XEP. +/// +/// ### Behaviour in previous versions +/// +/// Support in the roster manager and in QXmppPresence was initially added in 1.9.0. +/// +/// In QXmpp 1.9.0 until 1.9.4 and 1.10.0 until 1.10.1 subscription requests with a Moved element +/// are verified and automatically accepted without emitting subscriptionRequestReceived(). If +/// verification fails, the **invalid** old JID is passed in subscriptionRequestReceived()! +/// /// \ingroup Managers /// class QXMPP_EXPORT QXmppRosterManager : public QXmppClientExtension @@ -144,7 +171,7 @@ private: using RosterResult = std::variant<QXmppRosterIq, QXmppError>; - void handleSubscriptionRequest(const QString &bareJid, const QXmppPresence &presence, bool accept); + void handleSubscriptionRequest(const QString &bareJid, const QXmppPresence &presence); QXmppTask<RosterResult> requestRoster(); const std::unique_ptr<QXmppRosterManagerPrivate> d; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qxmpp-1.10.1/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp new/qxmpp-1.10.2/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp --- old/qxmpp-1.10.1/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp 2025-02-25 21:43:41.000000000 +0100 +++ new/qxmpp-1.10.2/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp 2025-03-19 19:04:07.000000000 +0100 @@ -5,6 +5,8 @@ #include "QXmppClient.h" #include "QXmppDiscoveryManager.h" +#include "QXmppMovedManager.h" +#include "QXmppPubSubManager.h" #include "QXmppRosterManager.h" #include "TestClient.h" @@ -18,7 +20,9 @@ Q_SLOT void testDiscoFeatures(); Q_SLOT void testRenameItem(); - Q_SLOT void subscriptionRequestReceived(); + Q_SLOT void testSubscriptionRequestReceived(); + Q_SLOT void testMovedSubscriptionRequestReceived_data(); + Q_SLOT void testMovedSubscriptionRequestReceived(); Q_SLOT void testAddItem(); Q_SLOT void testRemoveItem(); @@ -90,7 +94,7 @@ QVERIFY(requestSent); } -void tst_QXmppRosterManager::subscriptionRequestReceived() +void tst_QXmppRosterManager::testSubscriptionRequestReceived() { QXmppPresence presence; presence.setType(QXmppPresence::Subscribe); @@ -110,6 +114,112 @@ QVERIFY(subscriptionRequestReceived); } +void tst_QXmppRosterManager::testMovedSubscriptionRequestReceived_data() +{ + QTest::addColumn<bool>("movedManagerAdded"); + QTest::addColumn<QString>("oldJid"); + QTest::addColumn<QString>("oldJidResponse"); + QTest::addColumn<bool>("valid"); + + QTest::newRow("noMovedManagerNoJid") + << false + << QString() + << QString() + << false; + QTest::newRow("noMovedManagerJid") + << false + << u"o...@example.org"_s + << QString() + << false; + QTest::newRow("oldJidEmpty") + << true + << QString() + << QString() + << false; + QTest::newRow("oldJidNotInRoster") + << true + << u"old-inva...@example.org"_s + << QString() + << false; + QTest::newRow("oldJidRespondingWithError") + << true + << u"o...@example.org"_s + << u"<iq id='qxmpp1' from='o...@example.org' type='error'>" + u"<error type='cancel'>" + u"<not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" + u"</error>" + u"</iq>"_s + << false; + QTest::newRow("oldJidValid") + << true + << u"o...@example.org"_s + << u"<iq id='qxmpp1' from='o...@example.org' type='result'>" + "<pubsub xmlns='http://jabber.org/protocol/pubsub'>" + "<items node='urn:xmpp:moved:1'>" + "<item id='current'>" + "<moved xmlns='urn:xmpp:moved:1'>" + "<new-jid>n...@example.org</new-jid>" + "</moved>" + "</item>" + "</items>" + "</pubsub>" + "</iq>"_s + << true; +} + +void tst_QXmppRosterManager::testMovedSubscriptionRequestReceived() +{ + TestClient client; + client.configuration().setJid(u"al...@example.org"_s); + auto *rosterManager = client.addNewExtension<QXmppRosterManager>(&client); + + QFETCH(bool, movedManagerAdded); + QFETCH(QString, oldJid); + QFETCH(QString, oldJidResponse); + QFETCH(bool, valid); + + if (movedManagerAdded) { + client.addNewExtension<QXmppDiscoveryManager>(); + client.addNewExtension<QXmppPubSubManager>(); + client.addNewExtension<QXmppMovedManager>(); + + QXmppRosterIq::Item rosterItem; + rosterItem.setBareJid(u"o...@example.org"_s); + rosterItem.setSubscriptionType(QXmppRosterIq::Item::SubscriptionType::Both); + + QXmppRosterIq rosterIq; + rosterIq.setType(QXmppIq::Set); + rosterIq.setItems({ rosterItem }); + rosterManager->handleStanza(writePacketToDom(rosterIq)); + } + + QXmppPresence presence; + presence.setType(QXmppPresence::Subscribe); + presence.setFrom(u"n...@example.org/notebook"_s); + presence.setOldJid(oldJid); + + bool subscriptionRequestReceived = false; + client.resetIdCount(); + + connect(rosterManager, &QXmppRosterManager::subscriptionRequestReceived, this, [&](const QString &subscriberBareJid, const QXmppPresence &presence) { + subscriptionRequestReceived = true; + QCOMPARE(subscriberBareJid, u"n...@example.org"_s); + if (valid && movedManagerAdded) { + QCOMPARE(oldJid, presence.oldJid()); + } else { + QVERIFY(presence.oldJid().isEmpty()); + } + }); + + Q_EMIT client.presenceReceived(presence); + + if (!oldJidResponse.isEmpty()) { + client.inject(oldJidResponse); + } + + QVERIFY(subscriptionRequestReceived); +} + void tst_QXmppRosterManager::testAddItem() { TestClient test;