diff options
| author | Linus Jahn <lnj@kaidan.im> | 2023-03-14 22:26:50 +0100 |
|---|---|---|
| committer | Linus Jahn <lnj@kaidan.im> | 2023-03-14 22:39:14 +0100 |
| commit | 054b35de3ea9251ca713209ea73b2b814fb6c0bc (patch) | |
| tree | dcfa448dd8cb71aaf50f27fb4a05d90dddb0b4ed | |
| parent | 154ac6b989aeee66eb2d3b87802faf009b90a92b (diff) | |
MamManager: Only parse ScePublic when decrypting messages
Message that are end-to-end-encrypted need to be parsed in ScePublic
mode and should then be decrypted.
In case the decryption fails, the messages are parsed in SceAll (the
normal mode).
| -rw-r--r-- | src/client/QXmppMamManager.cpp | 70 |
1 files changed, 49 insertions, 21 deletions
diff --git a/src/client/QXmppMamManager.cpp b/src/client/QXmppMamManager.cpp index d7f63eaf..fe0735c7 100644 --- a/src/client/QXmppMamManager.cpp +++ b/src/client/QXmppMamManager.cpp @@ -9,15 +9,16 @@ #include "QXmppConstants_p.h" #include "QXmppDataForm.h" #include "QXmppE2eeExtension.h" -#include "QXmppFutureUtils_p.h" #include "QXmppMamIq.h" #include "QXmppMessage.h" +#include "QXmppPromise.h" #include "QXmppUtils.h" #include <unordered_map> #include <QDomElement> +using namespace QXmpp; using namespace QXmpp::Private; template<typename T, typename Converter> @@ -36,7 +37,26 @@ auto sum(const T &c) return std::accumulate(c.begin(), c.end(), 0); } -std::optional<std::tuple<QXmppMessage, QString>> parseMamMessageResult(const QDomElement &messageEl) +struct MamMessage +{ + QDomElement element; + std::optional<QDateTime> delay; +}; + +enum EncryptedType { Unencrypted, + Encrypted }; + +QXmppMessage parseMamMessage(const MamMessage &mamMessage, EncryptedType encrypted) +{ + QXmppMessage m; + m.parse(mamMessage.element, encrypted == Encrypted ? ScePublic : SceAll); + if (mamMessage.delay) { + m.setStamp(*mamMessage.delay); + } + return m; +} + +std::optional<std::tuple<MamMessage, QString>> parseMamMessageResult(const QDomElement &messageEl) { auto resultElement = messageEl.firstChildElement("result"); if (resultElement.isNull() || resultElement.namespaceURI() != ns_mam) { @@ -55,31 +75,32 @@ std::optional<std::tuple<QXmppMessage, QString>> parseMamMessageResult(const QDo return {}; } - QXmppMessage message; - message.parse(messageElement); - - auto delayElement = forwardedElement.firstChildElement("delay"); - if (!delayElement.isNull() && delayElement.namespaceURI() == ns_delayed_delivery) { - const auto stamp = delayElement.attribute("stamp"); - message.setStamp(QXmppUtils::datetimeFromString(stamp)); - } + auto parseDelay = [](const auto &forwardedEl) -> std::optional<QDateTime> { + auto delayEl = forwardedEl.firstChildElement("delay"); + if (!delayEl.isNull() && delayEl.namespaceURI() == ns_delayed_delivery) { + return QXmppUtils::datetimeFromString(delayEl.attribute("stamp")); + } + return {}; + }; - return { { message, queryId } }; + return { { MamMessage { messageElement, parseDelay(forwardedElement) }, queryId } }; } struct RetrieveRequestState { QXmppPromise<QXmppMamManager::RetrieveResult> promise; QXmppMamResultIq iq; - QVector<QXmppMessage> messages; + QVector<MamMessage> messages; + QVector<QXmppMessage> processedMessages; uint runningDecryptionJobs = 0; void finish() { + Q_ASSERT(messages.count() == processedMessages.count()); promise.finish( QXmppMamManager::RetrievedMessages { std::move(iq), - std::move(messages) }); + std::move(processedMessages) }); } }; @@ -144,7 +165,7 @@ bool QXmppMamManager::handleStanza(const QDomElement &element) itr->second.messages.append(std::move(message)); } else { // signal-based API - Q_EMIT archivedMessageReceived(queryId, message); + Q_EMIT archivedMessageReceived(queryId, parseMamMessage(message, Unencrypted)); } return true; } @@ -304,11 +325,13 @@ QXmppTask<QXmppMamManager::RetrieveResult> QXmppMamManager::retrieveMessages(con // decrypt encrypted messages if (auto *e2eeExt = client()->encryptionExtension()) { - auto &messages = state.messages; + // 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(state.messages, [&](const auto &m) { - return e2eeExt->isEncrypted(m); + return e2eeExt->isEncrypted(m.element); }); auto encryptedCount = sum(messagesEncrypted); @@ -316,25 +339,26 @@ QXmppTask<QXmppMamManager::RetrieveResult> QXmppMamManager::retrieveMessages(con // because some decryptMessage() jobs could finish instantly state.runningDecryptionJobs = encryptedCount; - for (auto i = 0; i < messages.size(); i++) { + for (auto i = 0; i < state.messages.size(); i++) { if (!messagesEncrypted[i]) { continue; } - auto message = messages.at(i); - state.runningDecryptionJobs++; - e2eeExt->decryptMessage(std::move(message)).then(this, [this, i, queryId](auto result) { + 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.messages[i] = std::get<QXmppMessage>(std::move(result)); + state.processedMessages[i] = std::get<QXmppMessage>(std::move(result)); } else { warning(QStringLiteral("Error decrypting message.")); + state.processedMessages[i] = parseMamMessage(state.messages[i], Unencrypted); } + // finish promise if this was the last job state.runningDecryptionJobs--; if (state.runningDecryptionJobs == 0) { state.finish(); @@ -349,6 +373,10 @@ QXmppTask<QXmppMamManager::RetrieveResult> QXmppMamManager::retrieveMessages(con } } + // for the case without decryption, finish here + state.processedMessages = transform(state.messages, [](const auto &m) { + return parseMamMessage(m, Unencrypted); + }); state.finish(); d->ongoingRequests.erase(itr); }); |
