diff options
| author | Linus Jahn <lnj@kaidan.im> | 2022-12-28 21:58:25 +0100 |
|---|---|---|
| committer | Linus Jahn <lnj@kaidan.im> | 2022-12-28 22:56:35 +0100 |
| commit | 838deb445b615aa06829164deb926ad68a42ae30 (patch) | |
| tree | 267549c891617d9b871ef4a21a77b365c02de7a5 | |
| parent | 3dd4267a5842d5b956dd79633fa2c8b81fb80d53 (diff) | |
| download | qxmpp-838deb445b615aa06829164deb926ad68a42ae30.tar.gz | |
Stream: Add IQ response sender check
Verifies that the sender of the response is correct, so no evil entity
can inject responses.
Fixes #510.
| -rw-r--r-- | src/base/QXmppStream.cpp | 42 | ||||
| -rw-r--r-- | src/base/QXmppStream.h | 2 | ||||
| -rw-r--r-- | src/client/QXmppClient.cpp | 11 | ||||
| -rw-r--r-- | src/client/QXmppOutgoingClient.cpp | 10 | ||||
| -rw-r--r-- | src/client/QXmppOutgoingClient.h | 1 | ||||
| -rw-r--r-- | tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp | 51 | ||||
| -rw-r--r-- | tests/qxmpprostermanager/tst_qxmpprostermanager.cpp | 18 |
7 files changed, 88 insertions, 47 deletions
diff --git a/src/base/QXmppStream.cpp b/src/base/QXmppStream.cpp index 6b5d9589..c650b8ac 100644 --- a/src/base/QXmppStream.cpp +++ b/src/base/QXmppStream.cpp @@ -33,7 +33,11 @@ using namespace QXmpp::Private; static bool randomSeeded = false; #endif -using IqState = QFutureInterface<QXmppStream::IqResult>; +struct IqState +{ + QFutureInterface<QXmppStream::IqResult> interface; + QString jid; +}; class QXmppStreamPrivate { @@ -219,7 +223,7 @@ QFuture<QXmppStream::IqResult> QXmppStream::sendIq(QXmppIq &&iq) iq.setId(QXmppUtils::generateStanzaUuid()); } - return sendIq(QXmppPacket(iq), iq.id()); + return sendIq(QXmppPacket(iq), iq.id(), iq.to()); } /// @@ -229,7 +233,7 @@ QFuture<QXmppStream::IqResult> QXmppStream::sendIq(QXmppIq &&iq) /// /// \since QXmpp 1.5 /// -QFuture<QXmppStream::IqResult> QXmppStream::sendIq(QXmppPacket &&packet, const QString &id) +QFuture<QXmppStream::IqResult> QXmppStream::sendIq(QXmppPacket &&packet, const QString &id, const QString &to) { using namespace QXmpp; @@ -239,6 +243,12 @@ QFuture<QXmppStream::IqResult> QXmppStream::sendIq(QXmppPacket &&packet, const Q SendError::Disconnected }); } + if (to.isEmpty()) { + return makeReadyFuture<IqResult>(SendError { + QStringLiteral("The 'to' address must be set so the stream can match the response."), + SendError::Disconnected }); + } + auto sendFuture = send(std::move(packet)); if (sendFuture.isFinished()) { if (std::holds_alternative<SendError>(sendFuture.result())) { @@ -249,8 +259,8 @@ QFuture<QXmppStream::IqResult> QXmppStream::sendIq(QXmppPacket &&packet, const Q awaitLast(sendFuture, this, [this, id](SendResult result) { if (std::holds_alternative<SendError>(result)) { if (auto itr = d->runningIqs.find(id); itr != d->runningIqs.end()) { - itr.value().reportResult(std::get<SendError>(result)); - itr.value().reportFinished(); + itr.value().interface.reportResult(std::get<SendError>(result)); + itr.value().interface.reportFinished(); d->runningIqs.erase(itr); } @@ -258,9 +268,13 @@ QFuture<QXmppStream::IqResult> QXmppStream::sendIq(QXmppPacket &&packet, const Q }); } - IqState interface(IqState::Started); - d->runningIqs.insert(id, interface); - return interface.future(); + IqState state { + QFutureInterface<IqResult>(QFutureInterfaceBase::Started), + to, + }; + auto future = state.interface.future(); + d->runningIqs.insert(id, std::move(state)); + return future; } /// @@ -271,10 +285,10 @@ QFuture<QXmppStream::IqResult> QXmppStream::sendIq(QXmppPacket &&packet, const Q void QXmppStream::cancelOngoingIqs() { for (auto &state : d->runningIqs) { - state.reportResult(QXmpp::SendError { + state.interface.reportResult(QXmpp::SendError { QStringLiteral("IQ has been cancelled."), QXmpp::SendError::Disconnected }); - state.reportFinished(); + state.interface.reportFinished(); } d->runningIqs.clear(); } @@ -475,9 +489,13 @@ bool QXmppStream::handleIqResponse(const QDomElement &stanza) if (auto itr = d->runningIqs.find(stanza.attribute(QStringLiteral("id"))); itr != d->runningIqs.end()) { + if (stanza.attribute("from") != itr.value().jid) { + warning(QStringLiteral("Received IQ response to one of our requests from wrong sender. Ignoring.")); + return false; + } - itr.value().reportResult(stanza); - itr.value().reportFinished(); + itr.value().interface.reportResult(stanza); + itr.value().interface.reportFinished(); d->runningIqs.erase(itr); return true; diff --git a/src/base/QXmppStream.h b/src/base/QXmppStream.h index aba73785..f8c4e548 100644 --- a/src/base/QXmppStream.h +++ b/src/base/QXmppStream.h @@ -46,7 +46,7 @@ public: using IqResult = std::variant<QDomElement, QXmpp::SendError>; QFuture<IqResult> sendIq(QXmppIq &&); - QFuture<IqResult> sendIq(QXmppPacket &&, const QString &id); + QFuture<IqResult> sendIq(QXmppPacket &&, const QString &id, const QString &to); void cancelOngoingIqs(); bool hasIqId(const QString &id) const; diff --git a/src/client/QXmppClient.cpp b/src/client/QXmppClient.cpp index 174c4219..a6d55fa8 100644 --- a/src/client/QXmppClient.cpp +++ b/src/client/QXmppClient.cpp @@ -517,12 +517,12 @@ QFuture<QXmppClient::IqResult> QXmppClient::sendIq(QXmppIq &&iq, const std::opti /// QFuture<QXmppClient::IqResult> QXmppClient::sendSensitiveIq(QXmppIq &&iq, const std::optional<QXmppSendStanzaParams> ¶ms) { - const auto sendEncrypted = [this](QFuture<IqEncryptResult> &&future, const QString &id) { + const auto sendEncrypted = [this](QFuture<IqEncryptResult> &&future, const QString &id, const QString &to) { QFutureInterface<IqResult> interface(QFutureInterfaceBase::Started); - await(future, this, [this, interface, id](IqEncryptResult result) mutable { + await(future, this, [this, interface, id, to](IqEncryptResult result) mutable { if (const auto *xml = std::get_if<QByteArray>(&result)) { // encrypted successfully - auto future = d->stream->sendIq(QXmppPacket(*xml, true), id); + auto future = d->stream->QXmppStream::sendIq(QXmppPacket(*xml, true), id, to); await(future, this, [this, interface](QXmppStream::IqResult result) mutable { if (const auto encryptedDom = std::get_if<QDomElement>(&result)) { if (!isIqResponse(*encryptedDom)) { @@ -571,10 +571,13 @@ QFuture<QXmppClient::IqResult> QXmppClient::sendSensitiveIq(QXmppIq &&iq, const if (iq.id().isEmpty() || d->stream->hasIqId(iq.id())) { iq.setId(QXmppUtils::generateStanzaUuid()); } + if (iq.to().isEmpty()) { + iq.setTo(d->stream->configuration().domain()); + } if (d->encryptionExtension) { const auto id = iq.id(); - return sendEncrypted(d->encryptionExtension->encryptIq(std::move(iq), params), id); + return sendEncrypted(d->encryptionExtension->encryptIq(std::move(iq), params), id, iq.to()); } return d->stream->sendIq(std::move(iq)); } diff --git a/src/client/QXmppOutgoingClient.cpp b/src/client/QXmppOutgoingClient.cpp index 7d1ad6a3..9a8e51b8 100644 --- a/src/client/QXmppOutgoingClient.cpp +++ b/src/client/QXmppOutgoingClient.cpp @@ -19,6 +19,7 @@ #include <QCryptographicHash> #include <QDnsLookup> +#include <QFuture> #include <QNetworkProxy> #include <QSslConfiguration> #include <QSslSocket> @@ -316,6 +317,15 @@ bool QXmppOutgoingClient::isStreamResumed() const return d->streamResumed; } +QFuture<QXmppStream::IqResult> QXmppOutgoingClient::sendIq(QXmppIq &&iq) +{ + // always set a to address (the QXmppStream needs this for matching) + if (iq.to().isEmpty()) { + iq.setTo(d->config.domain()); + } + return QXmppStream::sendIq(std::move(iq)); +} + void QXmppOutgoingClient::_q_socketDisconnected() { debug("Socket disconnected"); diff --git a/src/client/QXmppOutgoingClient.h b/src/client/QXmppOutgoingClient.h index 57e3477a..6c72d076 100644 --- a/src/client/QXmppOutgoingClient.h +++ b/src/client/QXmppOutgoingClient.h @@ -38,6 +38,7 @@ public: bool isClientStateIndicationEnabled() const; bool isStreamManagementEnabled() const; bool isStreamResumed() const; + QFuture<IqResult> sendIq(QXmppIq &&); /// Returns the used socket QSslSocket *socket() const { return QXmppStream::socket(); }; diff --git a/tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp b/tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp index cd3df755..f6322779 100644 --- a/tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp +++ b/tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp @@ -222,9 +222,10 @@ void tst_QXmppPubSubManager::testRequestFeatures() void tst_QXmppPubSubManager::testRequestPepFeatures() { auto [test, psManager] = Client(); + test.configuration().setJid("juliet@capulet.lit"); auto future = psManager->requestOwnPepFeatures(); - test.expect(QStringLiteral("<iq id='qxmpp1' type='get'>" + test.expect(QStringLiteral("<iq id='qxmpp1' to='juliet@capulet.lit' type='get'>" "<query xmlns='http://jabber.org/protocol/disco#info'/>" "</iq>")); test.inject(QStringLiteral("<iq type='result' from='juliet@capulet.lit' to='juliet@capulet.lit/balcony' id='qxmpp1'>" @@ -257,9 +258,10 @@ void tst_QXmppPubSubManager::testFetchNodes() void tst_QXmppPubSubManager::testFetchPepNodes() { auto [test, psManager] = Client(); + test.configuration().setJid("juliet@capulet.lit"); auto future = psManager->requestOwnPepNodes(); - test.expect(QStringLiteral("<iq id='qxmpp1' type='get'>" + test.expect(QStringLiteral("<iq id='qxmpp1' to='juliet@capulet.lit' type='get'>" "<query xmlns='http://jabber.org/protocol/disco#items'/>" "</iq>")); test.inject(QStringLiteral("<iq id='qxmpp1' from='juliet@capulet.lit' to='juliet@capulet.lit/balcony' type='result'>" @@ -306,7 +308,7 @@ void tst_QXmppPubSubManager::testCreateNodes() } test.expect(QStringLiteral("<iq id='qxmpp1' to='%1' type='set'><pubsub xmlns='http://jabber.org/protocol/pubsub'><create node='%2'/></pubsub></iq>").arg(jid, node)); - test.inject<QString>("<iq id='qxmpp1' type='result'/>"); + test.inject(QStringLiteral("<iq id='qxmpp1' from='%1' type='result'/>").arg(jid)); expectFutureVariant<QXmpp::Success>(future); } @@ -327,7 +329,7 @@ void tst_QXmppPubSubManager::testCreateNodeWithConfig() "</configure>" "</pubsub>" "</iq>"); - test.inject<QString>("<iq id='qxmpp1' type='result'/>"); + test.inject<QString>("<iq id='qxmpp1' from='pubsub.qxmpp.org' type='result'/>"); expectFutureVariant<QXmpp::Success>(future); } @@ -407,7 +409,7 @@ void tst_QXmppPubSubManager::testDeleteNodes() // FIXME: pubsub#owner here, but not for <create/>? test.expect(QStringLiteral("<iq id='qxmpp1' to='%1' type='set'><pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><delete node='%2'/></pubsub></iq>").arg(jid, node)); - test.inject<QString>("<iq id='qxmpp1' type='result'/>"); + test.inject(QStringLiteral("<iq id='qxmpp1' from='%1' type='result'/>").arg(jid)); expectFutureVariant<QXmpp::Success>(future); } @@ -509,7 +511,7 @@ void tst_QXmppPubSubManager::testPublishItems() </iq>)") .arg(jid, node, itemIdsXml)); } else { - test.inject(QStringLiteral("<iq id='qxmpp1' type='result'/>")); + test.inject(QStringLiteral("<iq id='qxmpp1' from='%1' type='result'/>").arg(jid)); } }; @@ -631,9 +633,10 @@ void tst_QXmppPubSubManager::testRetractItem() void tst_QXmppPubSubManager::testRetractCurrentPepItem() { auto [test, psManager] = Client(); + test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->retractOwnPepItem(QStringLiteral("princely_musings"), PSManager::Current); - test.expect(QStringLiteral("<iq id='qxmpp1' type='set'>" + test.expect(QStringLiteral("<iq id='qxmpp1' to='juliet@capulet.lit' type='set'>" "<pubsub xmlns='http://jabber.org/protocol/pubsub'>" "<retract node='princely_musings'>" "<item id='current'/>" @@ -694,9 +697,10 @@ void tst_QXmppPubSubManager::testRequestItemIds() void tst_QXmppPubSubManager::testRequestPepItemIds() { auto [test, psManager] = Client(); + test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->requestOwnPepItemIds(QStringLiteral("princely_musings")); - test.expect(QStringLiteral("<iq id='qxmpp1' type='get'>" + test.expect(QStringLiteral("<iq id='qxmpp1' to='juliet@capulet.lit' type='get'>" "<query xmlns='http://jabber.org/protocol/disco#items' node='princely_musings'/>" "</iq>")); test.inject(QStringLiteral("<iq id='qxmpp1' from='juliet@capulet.lit' to='juliet@capulet.lit/balcony' type='result'>" @@ -867,9 +871,10 @@ void tst_QXmppPubSubManager::testRequestItems() void tst_QXmppPubSubManager::testRequestCurrentPepItem() { auto [test, psManager] = Client(); + test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->requestOwnPepItem(QStringLiteral("princely_musings"), PSManager::Current); - test.expect(QStringLiteral("<iq id='qxmpp1' type='get'>" + test.expect(QStringLiteral("<iq id='qxmpp1' to='juliet@capulet.lit' type='get'>" "<pubsub xmlns='http://jabber.org/protocol/pubsub'>" "<items node='princely_musings'>" "<item id='current'/>" @@ -889,9 +894,10 @@ void tst_QXmppPubSubManager::testRequestCurrentPepItem() void tst_QXmppPubSubManager::testRequestPepItem() { auto [test, psManager] = Client(); + test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->requestOwnPepItem(QStringLiteral("princely_musings"), QStringLiteral("ae890ac52d0df67ed7cfdf51b644e901")); - test.expect(QStringLiteral("<iq id='qxmpp1' type='get'>" + test.expect(QStringLiteral("<iq id='qxmpp1' to='juliet@capulet.lit' type='get'>" "<pubsub xmlns='http://jabber.org/protocol/pubsub'>" "<items node='princely_musings'>" "<item id='ae890ac52d0df67ed7cfdf51b644e901'/>" @@ -911,9 +917,10 @@ void tst_QXmppPubSubManager::testRequestPepItem() void tst_QXmppPubSubManager::testRequestPepItems() { auto [test, psManager] = Client(); + test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto future = psManager->requestOwnPepItems(QStringLiteral("princely_musings")); - test.expect(QStringLiteral("<iq id='qxmpp1' type='get'>" + test.expect(QStringLiteral("<iq id='qxmpp1' to='juliet@capulet.lit' type='get'>" "<pubsub xmlns='http://jabber.org/protocol/pubsub'>" "<items node='princely_musings'/>" "</pubsub></iq>")); @@ -954,7 +961,7 @@ void tst_QXmppPubSubManager::testRequestNodeAffiliations() "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>" "<affiliations node='news'/>" "</pubsub></iq>"); - test.inject(QStringLiteral("<iq id='qxmpp1' type='result' from='pubsub.shakespeare.lit'>" + test.inject(QStringLiteral("<iq id='qxmpp1' type='result' from='pubsub.qxmpp.org'>" "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>" "<affiliations node='news'>" "<affiliation jid='hamlet@denmark.lit' affiliation='owner'/>" @@ -1018,7 +1025,7 @@ void tst_QXmppPubSubManager::testRequestOptions() auto *psManager = test.addNewExtension<PSManager>(); auto testOpts = [&](QFuture<PSManager::OptionsResult> &&future) { - test.expect("<iq id='qxmpp1' to='pubsub.qxmpp.org' type='get'><pubsub xmlns='http://jabber.org/protocol/pubsub'>" + test.expect("<iq id='qxmpp1' to='pubsub.shakespeare.lit' type='get'><pubsub xmlns='http://jabber.org/protocol/pubsub'>" "<options jid='me@qxmpp.org' node='node1'/>" "</pubsub></iq>"); test.inject<QString>("<iq id='qxmpp1' from='pubsub.shakespeare.lit' type='result'>" @@ -1047,17 +1054,17 @@ void tst_QXmppPubSubManager::testRequestOptions() QCOMPARE(form.unknownFields().size(), 0); }; - testOpts(psManager->requestSubscribeOptions("pubsub.qxmpp.org", "node1", "me@qxmpp.org")); + testOpts(psManager->requestSubscribeOptions("pubsub.shakespeare.lit", "node1", "me@qxmpp.org")); test.configuration().setJid("me@qxmpp.org"); - testOpts(psManager->requestSubscribeOptions("pubsub.qxmpp.org", "node1")); + testOpts(psManager->requestSubscribeOptions("pubsub.shakespeare.lit", "node1")); } void tst_QXmppPubSubManager::testRequestOptionsError() { auto [test, psManager] = Client(); - auto future = psManager->requestSubscribeOptions("pubsub.qxmpp.org", "node1", "me@qxmpp.org"); - test.expect("<iq id='qxmpp1' to='pubsub.qxmpp.org' type='get'><pubsub xmlns='http://jabber.org/protocol/pubsub'>" + auto future = psManager->requestSubscribeOptions("pubsub.shakespeare.lit", "node1", "me@qxmpp.org"); + test.expect("<iq id='qxmpp1' to='pubsub.shakespeare.lit' type='get'><pubsub xmlns='http://jabber.org/protocol/pubsub'>" "<options jid='me@qxmpp.org' node='node1'/>" "</pubsub></iq>"); test.inject<QString>("<iq id='qxmpp1' from='pubsub.shakespeare.lit' type='result'>" @@ -1105,7 +1112,7 @@ void tst_QXmppPubSubManager::testSetOptions() "<field type=\"boolean\" var='pubsub#include_body'><value>0</value></field>" "<field type=\"list-multi\" var='pubsub#show-values'><value>away</value><value>chat</value><value>online</value></field>" "</x></options></pubsub></iq>"); - test.inject<QString>("<iq id='qxmpp1' type='result'/>"); + test.inject<QString>("<iq id='qxmpp1' from='pubsub.shakespeare.lit' type='result'/>"); expectFutureVariant<QXmpp::Success>(future); } @@ -1120,7 +1127,7 @@ void tst_QXmppPubSubManager::testRequestNodeConfig() "<configure node='tune'/>" "</pubsub>" "</iq>"); - test.inject<QString>("<iq id='qxmpp1' type='result'>" + test.inject<QString>("<iq id='qxmpp1' from='pubsub.qxmpp.org' type='result'>" "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>" "<configure node='princely_musings'>" "<x xmlns='jabber:x:data' type='form'>" @@ -1167,8 +1174,8 @@ void tst_QXmppPubSubManager::testConfigureNode() config.setPayloadType("http://www.w3.org/2005/Atom"); config.setBodyXslt("http://jabxslt.jabberstudio.org/atom_body.xslt"); - auto future = psManager->configureNode("pubsub.qxmpp.org", "princely_musings", config); - test.expect("<iq id='qxmpp1' to='pubsub.qxmpp.org' type='set'>" + auto future = psManager->configureNode("pubsub.shakespeare.lit", "princely_musings", config); + test.expect("<iq id='qxmpp1' to='pubsub.shakespeare.lit' type='set'>" "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>" "<configure node='princely_musings'>" "<x xmlns='jabber:x:data' type='submit'>" @@ -1213,7 +1220,7 @@ void tst_QXmppPubSubManager::testCancelConfig() "</configure>" "</pubsub>" "</iq>"); - test.inject<QString>("<iq id='qxmpp1' type='result'/>"); + test.inject<QString>("<iq id='qxmpp1' from='pubsub.qxmpp.org' type='result'/>"); expectFutureVariant<QXmpp::Success>(future); } diff --git a/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp b/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp index 11c7fb3d..fe9d6181 100644 --- a/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp +++ b/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp @@ -113,17 +113,18 @@ void tst_QXmppRosterManager::subscriptionRequestReceived() void tst_QXmppRosterManager::testAddItem() { TestClient test; + test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto *rosterManager = test.addNewExtension<QXmppRosterManager>(&test); auto future = rosterManager->addRosterItem("contact@example.org"); - test.expect("<iq id='qxmpp1' type='set'><query xmlns='jabber:iq:roster'><item jid='contact@example.org'/></query></iq>"); - test.inject<QString>("<iq id='qxmpp1' type='result'/>"); + test.expect("<iq id='qxmpp1' to='capulet.lit' type='set'><query xmlns='jabber:iq:roster'><item jid='contact@example.org'/></query></iq>"); + test.inject<QString>("<iq id='qxmpp1' from='capulet.lit' type='result'/>"); expectFutureVariant<QXmpp::Success>(future); future = rosterManager->addRosterItem("contact@example.org"); - test.expect("<iq id='qxmpp1' type='set'><query xmlns='jabber:iq:roster'><item jid='contact@example.org'/></query></iq>"); + test.expect("<iq id='qxmpp1' to='capulet.lit' type='set'><query xmlns='jabber:iq:roster'><item jid='contact@example.org'/></query></iq>"); test.inject<QString>(R"( -<iq id='qxmpp1' type='error'> +<iq id='qxmpp1' from='capulet.lit' type='error'> <error type='modify'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>This is not allowed</text> @@ -137,17 +138,18 @@ void tst_QXmppRosterManager::testAddItem() void tst_QXmppRosterManager::testRemoveItem() { TestClient test; + test.configuration().setJid(QStringLiteral("juliet@capulet.lit")); auto *rosterManager = test.addNewExtension<QXmppRosterManager>(&test); auto future = rosterManager->removeRosterItem("contact@example.org"); - test.expect("<iq id='qxmpp1' type='set'><query xmlns='jabber:iq:roster'><item jid='contact@example.org' subscription='remove'/></query></iq>"); - test.inject<QString>("<iq id='qxmpp1' type='result'/>"); + test.expect("<iq id='qxmpp1' to='capulet.lit' type='set'><query xmlns='jabber:iq:roster'><item jid='contact@example.org' subscription='remove'/></query></iq>"); + test.inject<QString>("<iq id='qxmpp1' from='capulet.lit' type='result'/>"); expectFutureVariant<QXmpp::Success>(future); future = rosterManager->removeRosterItem("contact@example.org"); - test.expect("<iq id='qxmpp1' type='set'><query xmlns='jabber:iq:roster'><item jid='contact@example.org' subscription='remove'/></query></iq>"); + test.expect("<iq id='qxmpp1' to='capulet.lit' type='set'><query xmlns='jabber:iq:roster'><item jid='contact@example.org' subscription='remove'/></query></iq>"); test.inject<QString>(R"( -<iq id='qxmpp1' type='error'> +<iq id='qxmpp1' from='capulet.lit' type='error'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Not found</text> |
