Skip to content

Commit

Permalink
Merge branch '1.7' into 1.8
Browse files Browse the repository at this point in the history
  • Loading branch information
lnjX committed Oct 29, 2024
2 parents ecf61ab + 19a5d61 commit 1c04f28
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 45 deletions.
87 changes: 43 additions & 44 deletions src/client/QXmppMamManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,58 +313,57 @@ QXmppTask<QXmppMamManager::RetrieveResult> QXmppMamManager::retrieveMessages(con
// initialize processed messages (we need random access because
// decryptMessage() may finish in random order)
state.processedMessages.resize(state.messages.size());

// check for encrypted messages (once)
auto messagesEncrypted = transform<std::vector<bool>>(state.messages, [&](const auto &m) {
return e2eeExt->isEncrypted(m.element);
});
auto encryptedCount = sum(messagesEncrypted);

// We can't do this on the fly (with ++ and --) in the for loop
// because some decryptMessage() jobs could finish instantly
state.runningDecryptionJobs = encryptedCount;

int size = state.messages.size();
for (auto i = 0; i < size; i++) {
if (!messagesEncrypted[i]) {
continue;
}

e2eeExt->decryptMessage(parseMamMessage(state.messages.at(i), Encrypted)).then(this, [this, i, queryId](auto result) {
auto itr = d->ongoingRequests.find(queryId.toStdString());
Q_ASSERT(itr != d->ongoingRequests.end());

auto &state = itr->second;

// store decrypted message, fallback to encrypted message
if (std::holds_alternative<QXmppMessage>(result)) {
state.processedMessages[i] = std::get<QXmppMessage>(std::move(result));
} else {
warning(u"Error decrypting message."_s);
state.processedMessages[i] = parseMamMessage(state.messages[i], Unencrypted);
}

// finish promise if this was the last job
state.runningDecryptionJobs = state.messages.size();

const auto size = state.messages.size();
for (qsizetype i = 0; i < size; i++) {
const auto &message = state.messages.at(i);

// decrypt message if needed
if (e2eeExt->isEncrypted(message.element)) {
e2eeExt->decryptMessage(parseMamMessage(state.messages.at(i), Encrypted)).then(this, [this, i, queryId](auto result) {
// find state (again)
auto itr = d->ongoingRequests.find(queryId.toStdString());
Q_ASSERT(itr != d->ongoingRequests.end());

auto &state = itr->second;

// store decrypted message, fallback to encrypted message
if (std::holds_alternative<QXmppMessage>(result)) {
state.processedMessages[i] = std::get<QXmppMessage>(std::move(result));
} else {
warning(u"Error decrypting message."_s);
state.processedMessages[i] = parseMamMessage(state.messages[i], Unencrypted);
}

// finish promise on last job
state.runningDecryptionJobs--;
if (state.runningDecryptionJobs == 0) {
state.finish();
d->ongoingRequests.erase(itr);
}
});
} else {
state.processedMessages[i] = parseMamMessage(state.messages.at(i), Unencrypted);

// finish promise on last job (may be needed if no messages are encrypted or
// decryption finishes instantly)
state.runningDecryptionJobs--;
if (state.runningDecryptionJobs == 0) {
state.finish();
d->ongoingRequests.erase(itr);
}
});
}
}
} else {
// for the case without decryption
state.processedMessages = transform<QVector<QXmppMessage>>(state.messages, [](const auto &m) {
return parseMamMessage(m, Unencrypted);
});

// finishing the promise is done after decryptMessage()
if (encryptedCount > 0) {
return;
}
state.finish();
d->ongoingRequests.erase(itr);
}

// for the case without decryption, finish here
state.processedMessages = transform<QVector<QXmppMessage>>(state.messages, [](const auto &m) {
return parseMamMessage(m, Unencrypted);
});
state.finish();
d->ongoingRequests.erase(itr);
});

return task;
Expand Down
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ add_simple_test(qxmppiceconnection)
add_simple_test(qxmppiq)
add_simple_test(qxmppjingledata)
add_simple_test(qxmppjinglemessageinitiationmanager)
add_simple_test(qxmppmammanager)
add_simple_test(qxmppmammanager TestClient.h)
add_simple_test(qxmppmixinvitation)
add_simple_test(qxmppmixitems)
add_simple_test(qxmppmixmanager TestClient.h)
Expand Down
153 changes: 153 additions & 0 deletions tests/qxmppmammanager/tst_qxmppmammanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,46 @@
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#include "QXmppE2eeExtension.h"
#include "QXmppFutureUtils_p.h"
#include "QXmppMamManager.h"
#include "QXmppMessage.h"

#include "TestClient.h"
#include "util.h"

#include <QObject>

using namespace QXmpp::Private;

class EncryptionExtension : public QXmppE2eeExtension
{
public:
QXmppTask<MessageEncryptResult> encryptMessage(QXmppMessage &&, const std::optional<QXmppSendStanzaParams> &) override
{
return makeReadyTask<MessageEncryptResult>(QXmppError { "it's only a test", QXmpp::SendError::EncryptionError });
}
QXmppTask<MessageDecryptResult> decryptMessage(QXmppMessage &&m) override
{
m.setBody(m.e2eeFallbackBody());
m.setE2eeFallbackBody({});
return makeReadyTask<MessageDecryptResult>(std::move(m));
}

QXmppTask<IqEncryptResult> encryptIq(QXmppIq &&, const std::optional<QXmppSendStanzaParams> &) override
{
return makeReadyTask<IqEncryptResult>(QXmppError { "it's only a test", QXmpp::SendError::EncryptionError });
}

QXmppTask<IqDecryptResult> decryptIq(const QDomElement &) override
{
return makeReadyTask<IqDecryptResult>(QXmppError { "it's only a test", QXmpp::SendError::EncryptionError });
}

bool isEncrypted(const QDomElement &e) override { return !e.firstChildElement("test-encrypted").isNull(); };
bool isEncrypted(const QXmppMessage &) override { return false; };
};

class QXmppMamTestHelper : public QObject
{
Q_OBJECT
Expand Down Expand Up @@ -40,6 +73,10 @@ class tst_QXmppMamManager : public QObject
Q_SLOT void testHandleResultIq_data();
Q_SLOT void testHandleResultIq();

// test for task-based API
Q_SLOT void retrieveMessagesUnencrypted();
Q_SLOT void retrieveMessagesEncrypted();

QXmppMamTestHelper m_helper;
QXmppMamManager m_manager;
};
Expand Down Expand Up @@ -206,6 +243,122 @@ void tst_QXmppMamManager::testHandleResultIq()
QCOMPARE(m_helper.m_signalTriggered, accept);
}

void tst_QXmppMamManager::retrieveMessagesUnencrypted()
{
TestClient test;
auto *mam = test.addNewExtension<QXmppMamManager>();
auto task = mam->retrieveMessages("mam.server.org");
test.expect("<iq id='qxmpp1' to='mam.server.org' type='set'>"
"<query xmlns='urn:xmpp:mam:2' queryid='qxmpp1'>"
"<x xmlns='jabber:x:data' type='submit'>"
"<field type='hidden' var='FORM_TYPE'><value>urn:xmpp:mam:2</value></field>"
"</x>"
"</query>"
"</iq>");
mam->handleStanza(xmlToDom("<message id='aeb213' to='[email protected]/chamber' from='mam.server.org'>"
"<result xmlns='urn:xmpp:mam:2' queryid='qxmpp1' id='28482-98726-73623'>"
"<forwarded xmlns='urn:xmpp:forward:0'>"
"<delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>"
"<message xmlns='jabber:client'"
" to='[email protected]/balcony'"
" from='[email protected]/orchard'"
" type='chat'>"
"<body>Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.</body>"
"</message>"
"</forwarded>"
"</result>"
"</message>"));
mam->handleStanza(xmlToDom("<message id='aeb214' to='[email protected]/chamber' from='mam.server.org'>"
"<result xmlns='urn:xmpp:mam:2' queryid='qxmpp1' id='5d398-28273-f7382'>"
"<forwarded xmlns='urn:xmpp:forward:0'>"
"<delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:09:32Z'/>"
"<message xmlns='jabber:client'"
" to='[email protected]/orchard'"
" from='[email protected]/balcony'"
" type='chat' id='8a54s'>"
"<body>What man art thou that thus bescreen'd in night so stumblest on my counsel?</body>"
"</message>"
"</forwarded>"
"</result>"
"</message>"));
test.inject("<iq type='result' id='qxmpp1'>"
"<fin xmlns='urn:xmpp:mam:2'>"
"<set xmlns='http://jabber.org/protocol/rsm'>"
"<first index='0'>28482-98726-73623</first>"
"<last>09af3-cc343-b409f</last>"
"</set>"
"</fin>"
"</iq>");

auto retrieved = expectFutureVariant<QXmppMamManager::RetrievedMessages>(task);
QCOMPARE(retrieved.messages.size(), 2);
QCOMPARE(retrieved.messages.at(0).body(), "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.");
QCOMPARE(retrieved.messages.at(1).body(), "What man art thou that thus bescreen'd in night so stumblest on my counsel?");
QCOMPARE(retrieved.result.resultSetReply().first(), "28482-98726-73623");
}

void tst_QXmppMamManager::retrieveMessagesEncrypted()
{
TestClient test;
// e2ee
auto e2ee = std::make_unique<EncryptionExtension>();
test.setEncryptionExtension(e2ee.get());
// mam manager
auto *mam = test.addNewExtension<QXmppMamManager>();

// start request
auto task = mam->retrieveMessages("mam.server.org");
test.expect("<iq id='qxmpp1' to='mam.server.org' type='set'>"
"<query xmlns='urn:xmpp:mam:2' queryid='qxmpp1'>"
"<x xmlns='jabber:x:data' type='submit'>"
"<field type='hidden' var='FORM_TYPE'><value>urn:xmpp:mam:2</value></field>"
"</x>"
"</query>"
"</iq>");
mam->handleStanza(xmlToDom("<message id='aeb213' to='[email protected]/chamber' from='mam.server.org'>"
"<result xmlns='urn:xmpp:mam:2' queryid='qxmpp1' id='28482-98726-73623'>"
"<forwarded xmlns='urn:xmpp:forward:0'>"
"<delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>"
"<message xmlns='jabber:client'"
" to='[email protected]/balcony'"
" from='[email protected]/orchard'"
" type='chat'>"
"<test-encrypted/>"
"<body>Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.</body>"
"</message>"
"</forwarded>"
"</result>"
"</message>"));
mam->handleStanza(xmlToDom("<message id='aeb214' to='[email protected]/chamber' from='mam.server.org'>"
"<result xmlns='urn:xmpp:mam:2' queryid='qxmpp1' id='5d398-28273-f7382'>"
"<forwarded xmlns='urn:xmpp:forward:0'>"
"<delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:09:32Z'/>"
"<message xmlns='jabber:client'"
" to='[email protected]/orchard'"
" from='[email protected]/balcony'"
" type='chat' id='8a54s'>"
"<body>What man art thou that thus bescreen'd in night so stumblest on my counsel?</body>"
"</message>"
"</forwarded>"
"</result>"
"</message>"));
test.inject("<iq type='result' id='qxmpp1'>"
"<fin xmlns='urn:xmpp:mam:2'>"
"<set xmlns='http://jabber.org/protocol/rsm'>"
"<first index='0'>28482-98726-73623</first>"
"<last>09af3-cc343-b409f</last>"
"</set>"
"</fin>"
"</iq>");

// check results
auto retrieved = expectFutureVariant<QXmppMamManager::RetrievedMessages>(task);
QCOMPARE(retrieved.messages.size(), 2);
QCOMPARE(retrieved.messages.at(0).body(), "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.");
QCOMPARE(retrieved.messages.at(1).body(), "What man art thou that thus bescreen'd in night so stumblest on my counsel?");
QCOMPARE(retrieved.result.resultSetReply().first(), "28482-98726-73623");
}

void QXmppMamTestHelper::archivedMessageReceived(const QString &queryId, const QXmppMessage &message)
{
m_signalTriggered = true;
Expand Down

0 comments on commit 1c04f28

Please sign in to comment.