mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Show encryption errors in qml and add request keys button
This commit is contained in:
parent
9f742fe23d
commit
72bbad7485
14 changed files with 220 additions and 165 deletions
|
@ -541,32 +541,33 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
|
||||
src/AvatarProvider.h
|
||||
src/BlurhashProvider.h
|
||||
src/Cache_p.h
|
||||
src/CacheCryptoStructs.h
|
||||
src/Cache_p.h
|
||||
src/CallDevices.h
|
||||
src/CallManager.h
|
||||
src/ChatPage.h
|
||||
src/Clipboard.h
|
||||
src/CombinedImagePackModel.h
|
||||
src/CompletionProxyModel.h
|
||||
src/DeviceVerificationFlow.h
|
||||
src/ImagePackListModel.h
|
||||
src/InviteesModel.h
|
||||
src/LoginPage.h
|
||||
src/MainWindow.h
|
||||
src/MemberList.h
|
||||
src/MxcImageProvider.h
|
||||
src/ReadReceiptsModel.h
|
||||
src/Olm.h
|
||||
src/RegisterPage.h
|
||||
src/RoomsModel.h
|
||||
src/SSOHandler.h
|
||||
src/CombinedImagePackModel.h
|
||||
src/SingleImagePackModel.h
|
||||
src/ImagePackListModel.h
|
||||
src/TrayIcon.h
|
||||
src/UserSettingsPage.h
|
||||
src/UsersModel.h
|
||||
src/RoomsModel.h
|
||||
src/WebRTCSession.h
|
||||
src/WelcomePage.h
|
||||
)
|
||||
src/ReadReceiptsModel.h
|
||||
)
|
||||
|
||||
#
|
||||
# Bundle translations.
|
||||
|
|
|
@ -349,6 +349,7 @@ ScrollView {
|
|||
required property string callType
|
||||
required property var reactions
|
||||
required property int trustlevel
|
||||
required property int encryptionError
|
||||
required property var timestamp
|
||||
required property int status
|
||||
required property int index
|
||||
|
@ -456,6 +457,7 @@ ScrollView {
|
|||
callType: wrapper.callType
|
||||
reactions: wrapper.reactions
|
||||
trustlevel: wrapper.trustlevel
|
||||
encryptionError: wrapper.encryptionError
|
||||
timestamp: wrapper.timestamp
|
||||
status: wrapper.status
|
||||
relatedEventCacheBuster: wrapper.relatedEventCacheBuster
|
||||
|
|
|
@ -38,6 +38,7 @@ Item {
|
|||
required property string callType
|
||||
required property var reactions
|
||||
required property int trustlevel
|
||||
required property int encryptionError
|
||||
required property var timestamp
|
||||
required property int status
|
||||
required property int relatedEventCacheBuster
|
||||
|
@ -110,6 +111,7 @@ Item {
|
|||
roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? ""
|
||||
roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? ""
|
||||
callType: r.relatedEventCacheBuster, fromModel(Room.CallType) ?? ""
|
||||
encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? ""
|
||||
relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0
|
||||
}
|
||||
|
||||
|
@ -136,6 +138,7 @@ Item {
|
|||
roomTopic: r.roomTopic
|
||||
roomName: r.roomName
|
||||
callType: r.callType
|
||||
encryptionError: r.encryptionError
|
||||
relatedEventCacheBuster: r.relatedEventCacheBuster
|
||||
isReply: false
|
||||
}
|
||||
|
|
48
resources/qml/delegates/Encrypted.qml
Normal file
48
resources/qml/delegates/Encrypted.qml
Normal file
|
@ -0,0 +1,48 @@
|
|||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
import im.nheko 1.0
|
||||
|
||||
ColumnLayout {
|
||||
id: r
|
||||
|
||||
required property int encryptionError
|
||||
required property string eventId
|
||||
|
||||
width: parent ? parent.width : undefined
|
||||
|
||||
MatrixText {
|
||||
text: {
|
||||
switch (encryptionError) {
|
||||
case Olm.MissingSession:
|
||||
return qsTr("There is no key to unlock this message. We requested the key automatically, but you can try requesting it again if you are impatient.");
|
||||
case Olm.MissingSessionIndex:
|
||||
return qsTr("This message couldn't be decrypted, because we only have a key for newer messages. You can try requesting access to this message.");
|
||||
case Olm.DbError:
|
||||
return qsTr("There was an internal error reading the decryption key from the database.");
|
||||
case Olm.DecryptionFailed:
|
||||
return qsTr("There was an error decrypting this message.");
|
||||
case Olm.ParsingFailed:
|
||||
return qsTr("The message couldn't be parsed.");
|
||||
case Olm.ReplayAttack:
|
||||
return qsTr("The encryption key was reused! Someone is possibly trying to insert false messages into this chat!");
|
||||
default:
|
||||
return qsTr("Unknown decryption error");
|
||||
}
|
||||
}
|
||||
color: Nheko.colors.buttonText
|
||||
width: r ? r.width : undefined
|
||||
}
|
||||
|
||||
Button {
|
||||
palette: Nheko.colors
|
||||
visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
||||
text: qsTr("Request key")
|
||||
onClicked: room.requestKeyForEvent(eventId)
|
||||
}
|
||||
|
||||
}
|
|
@ -29,6 +29,7 @@ Item {
|
|||
required property string roomTopic
|
||||
required property string roomName
|
||||
required property string callType
|
||||
required property int encryptionError
|
||||
required property int relatedEventCacheBuster
|
||||
|
||||
height: chooser.childrenRect.height
|
||||
|
@ -189,6 +190,16 @@ Item {
|
|||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Encrypted
|
||||
|
||||
Encrypted {
|
||||
encryptionError: d.encryptionError
|
||||
eventId: d.eventId
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MtxEvent.Name
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ Item {
|
|||
property string roomTopic
|
||||
property string roomName
|
||||
property string callType
|
||||
property int encryptionError
|
||||
property int relatedEventCacheBuster
|
||||
|
||||
width: parent.width
|
||||
|
@ -97,6 +98,7 @@ Item {
|
|||
roomName: r.roomName
|
||||
callType: r.callType
|
||||
relatedEventCacheBuster: r.relatedEventCacheBuster
|
||||
encryptionError: r.encryptionError
|
||||
enabled: false
|
||||
width: parent.width
|
||||
isReply: true
|
||||
|
|
|
@ -143,14 +143,15 @@
|
|||
<file>qml/emoji/StickerPicker.qml</file>
|
||||
<file>qml/UserProfile.qml</file>
|
||||
<file>qml/delegates/MessageDelegate.qml</file>
|
||||
<file>qml/delegates/TextMessage.qml</file>
|
||||
<file>qml/delegates/NoticeMessage.qml</file>
|
||||
<file>qml/delegates/ImageMessage.qml</file>
|
||||
<file>qml/delegates/PlayableMediaMessage.qml</file>
|
||||
<file>qml/delegates/Encrypted.qml</file>
|
||||
<file>qml/delegates/FileMessage.qml</file>
|
||||
<file>qml/delegates/ImageMessage.qml</file>
|
||||
<file>qml/delegates/NoticeMessage.qml</file>
|
||||
<file>qml/delegates/Pill.qml</file>
|
||||
<file>qml/delegates/Placeholder.qml</file>
|
||||
<file>qml/delegates/PlayableMediaMessage.qml</file>
|
||||
<file>qml/delegates/Reply.qml</file>
|
||||
<file>qml/delegates/TextMessage.qml</file>
|
||||
<file>qml/device-verification/Waiting.qml</file>
|
||||
<file>qml/device-verification/DeviceVerification.qml</file>
|
||||
<file>qml/device-verification/DigitVerification.qml</file>
|
||||
|
|
|
@ -1069,7 +1069,7 @@ decryptEvent(const MegolmSessionIndex &index,
|
|||
mtx::events::collections::TimelineEvent te;
|
||||
mtx::events::collections::from_json(body, te);
|
||||
|
||||
return {std::nullopt, std::nullopt, std::move(te.data)};
|
||||
return {DecryptionErrorCode::NoError, std::nullopt, std::move(te.data)};
|
||||
} catch (std::exception &e) {
|
||||
return {DecryptionErrorCode::ParsingFailed, e.what(), std::nullopt};
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
constexpr auto OLM_ALGO = "m.olm.v1.curve25519-aes-sha2";
|
||||
|
||||
namespace olm {
|
||||
Q_NAMESPACE
|
||||
|
||||
enum class DecryptionErrorCode
|
||||
enum DecryptionErrorCode
|
||||
{
|
||||
NoError,
|
||||
MissingSession, // Session was not found, retrieve from backup or request from other devices
|
||||
// and try again
|
||||
MissingSessionIndex, // Session was found, but it does not reach back enough to this index,
|
||||
|
@ -25,14 +27,13 @@ enum class DecryptionErrorCode
|
|||
DecryptionFailed, // libolm error
|
||||
ParsingFailed, // Failed to parse the actual event
|
||||
ReplayAttack, // Megolm index reused
|
||||
UnknownFingerprint, // Unknown device Fingerprint
|
||||
};
|
||||
Q_ENUM_NS(DecryptionErrorCode)
|
||||
|
||||
struct DecryptionResult
|
||||
{
|
||||
std::optional<DecryptionErrorCode> error;
|
||||
DecryptionErrorCode error;
|
||||
std::optional<std::string> error_message;
|
||||
|
||||
std::optional<mtx::events::collections::TimelineEvents> event;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
|
||||
Q_DECLARE_METATYPE(Reaction)
|
||||
|
||||
QCache<EventStore::IdIndex, mtx::events::collections::TimelineEvents> EventStore::decryptedEvents_{
|
||||
1000};
|
||||
QCache<EventStore::IdIndex, olm::DecryptionResult> EventStore::decryptedEvents_{1000};
|
||||
QCache<EventStore::IdIndex, mtx::events::collections::TimelineEvents> EventStore::events_by_id_{
|
||||
1000};
|
||||
QCache<EventStore::Index, mtx::events::collections::TimelineEvents> EventStore::events_{1000};
|
||||
|
@ -144,12 +143,16 @@ EventStore::EventStore(std::string room_id, QObject *)
|
|||
mtx::events::msg::Encrypted>) {
|
||||
auto event =
|
||||
decryptEvent({room_id_, e.event_id}, e);
|
||||
if (auto dec =
|
||||
std::get_if<mtx::events::RoomEvent<
|
||||
mtx::events::msg::
|
||||
KeyVerificationRequest>>(event)) {
|
||||
emit updateFlowEventId(
|
||||
event_id.event_id.to_string());
|
||||
if (event->event) {
|
||||
if (auto dec = std::get_if<
|
||||
mtx::events::RoomEvent<
|
||||
mtx::events::msg::
|
||||
KeyVerificationRequest>>(
|
||||
&event->event.value())) {
|
||||
emit updateFlowEventId(
|
||||
event_id.event_id
|
||||
.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -393,12 +396,12 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
|
|||
if (auto encrypted =
|
||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
|
||||
&event)) {
|
||||
mtx::events::collections::TimelineEvents *d_event =
|
||||
decryptEvent({room_id_, encrypted->event_id}, *encrypted);
|
||||
if (std::visit(
|
||||
auto d_event = decryptEvent({room_id_, encrypted->event_id}, *encrypted);
|
||||
if (d_event->event &&
|
||||
std::visit(
|
||||
[](auto e) { return (e.sender != utils::localUser().toStdString()); },
|
||||
*d_event)) {
|
||||
handle_room_verification(*d_event);
|
||||
*d_event->event)) {
|
||||
handle_room_verification(*d_event->event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -599,11 +602,15 @@ EventStore::get(int idx, bool decrypt)
|
|||
events_.insert(index, event_ptr);
|
||||
}
|
||||
|
||||
if (decrypt)
|
||||
if (decrypt) {
|
||||
if (auto encrypted =
|
||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
|
||||
event_ptr))
|
||||
return decryptEvent({room_id_, encrypted->event_id}, *encrypted);
|
||||
event_ptr)) {
|
||||
auto decrypted = decryptEvent({room_id_, encrypted->event_id}, *encrypted);
|
||||
if (decrypted->event)
|
||||
return &*decrypted->event;
|
||||
}
|
||||
}
|
||||
|
||||
return event_ptr;
|
||||
}
|
||||
|
@ -629,7 +636,7 @@ EventStore::indexToId(int idx) const
|
|||
return cache::client()->getTimelineEventId(room_id_, toInternalIdx(idx));
|
||||
}
|
||||
|
||||
mtx::events::collections::TimelineEvents *
|
||||
olm::DecryptionResult *
|
||||
EventStore::decryptEvent(const IdIndex &idx,
|
||||
const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e)
|
||||
{
|
||||
|
@ -641,57 +648,24 @@ EventStore::decryptEvent(const IdIndex &idx,
|
|||
index.session_id = e.content.session_id;
|
||||
index.sender_key = e.content.sender_key;
|
||||
|
||||
auto asCacheEntry = [&idx](mtx::events::collections::TimelineEvents &&event) {
|
||||
auto event_ptr = new mtx::events::collections::TimelineEvents(std::move(event));
|
||||
auto asCacheEntry = [&idx](olm::DecryptionResult &&event) {
|
||||
auto event_ptr = new olm::DecryptionResult(std::move(event));
|
||||
decryptedEvents_.insert(idx, event_ptr);
|
||||
return event_ptr;
|
||||
};
|
||||
|
||||
auto decryptionResult = olm::decryptEvent(index, e);
|
||||
|
||||
mtx::events::RoomEvent<mtx::events::msg::Notice> dummy;
|
||||
dummy.origin_server_ts = e.origin_server_ts;
|
||||
dummy.event_id = e.event_id;
|
||||
dummy.sender = e.sender;
|
||||
|
||||
if (decryptionResult.error) {
|
||||
switch (*decryptionResult.error) {
|
||||
switch (decryptionResult.error) {
|
||||
case olm::DecryptionErrorCode::MissingSession:
|
||||
case olm::DecryptionErrorCode::MissingSessionIndex: {
|
||||
if (decryptionResult.error == olm::DecryptionErrorCode::MissingSession)
|
||||
dummy.content.body =
|
||||
tr("-- Encrypted Event (No keys found for decryption) --",
|
||||
"Placeholder, when the message was not decrypted yet or can't "
|
||||
"be "
|
||||
"decrypted.")
|
||||
.toStdString();
|
||||
else
|
||||
dummy.content.body =
|
||||
tr("-- Encrypted Event (Key not valid for this index) --",
|
||||
"Placeholder, when the message can't be decrypted with this "
|
||||
"key since it is not valid for this index ")
|
||||
.toStdString();
|
||||
nhlog::crypto()->info("Could not find inbound megolm session ({}, {}, {})",
|
||||
index.room_id,
|
||||
index.session_id,
|
||||
e.sender);
|
||||
// we may not want to request keys during initial sync and such
|
||||
if (suppressKeyRequests)
|
||||
break;
|
||||
// TODO: Check if this actually works and look in key backup
|
||||
auto copy = e;
|
||||
copy.room_id = room_id_;
|
||||
if (pending_key_requests.count(e.content.session_id)) {
|
||||
pending_key_requests.at(e.content.session_id)
|
||||
.events.push_back(copy);
|
||||
} else {
|
||||
PendingKeyRequests request;
|
||||
request.request_id =
|
||||
"key_request." + http::client()->generate_txn_id();
|
||||
request.events.push_back(copy);
|
||||
olm::send_key_request_for(copy, request.request_id);
|
||||
pending_key_requests[e.content.session_id] = request;
|
||||
}
|
||||
|
||||
requestSession(e, false);
|
||||
break;
|
||||
}
|
||||
case olm::DecryptionErrorCode::DbError:
|
||||
|
@ -701,12 +675,6 @@ EventStore::decryptEvent(const IdIndex &idx,
|
|||
index.session_id,
|
||||
index.sender_key,
|
||||
decryptionResult.error_message.value_or(""));
|
||||
dummy.content.body =
|
||||
tr("-- Decryption Error (failed to retrieve megolm keys from db) --",
|
||||
"Placeholder, when the message can't be decrypted, because the DB "
|
||||
"access "
|
||||
"failed.")
|
||||
.toStdString();
|
||||
break;
|
||||
case olm::DecryptionErrorCode::DecryptionFailed:
|
||||
nhlog::crypto()->critical(
|
||||
|
@ -715,22 +683,8 @@ EventStore::decryptEvent(const IdIndex &idx,
|
|||
index.session_id,
|
||||
index.sender_key,
|
||||
decryptionResult.error_message.value_or(""));
|
||||
dummy.content.body =
|
||||
tr("-- Decryption Error (%1) --",
|
||||
"Placeholder, when the message can't be decrypted. In this case, the "
|
||||
"Olm "
|
||||
"decrytion returned an error, which is passed as %1.")
|
||||
.arg(
|
||||
QString::fromStdString(decryptionResult.error_message.value_or("")))
|
||||
.toStdString();
|
||||
break;
|
||||
case olm::DecryptionErrorCode::ParsingFailed:
|
||||
dummy.content.body =
|
||||
tr("-- Encrypted Event (Unknown event type) --",
|
||||
"Placeholder, when the message was decrypted, but we couldn't parse "
|
||||
"it, because "
|
||||
"Nheko/mtxclient don't support that event type yet.")
|
||||
.toStdString();
|
||||
break;
|
||||
case olm::DecryptionErrorCode::ReplayAttack:
|
||||
nhlog::crypto()->critical(
|
||||
|
@ -738,85 +692,50 @@ EventStore::decryptEvent(const IdIndex &idx,
|
|||
e.event_id,
|
||||
room_id_,
|
||||
index.sender_key);
|
||||
dummy.content.body =
|
||||
tr("-- Replay attack! This message index was reused! --").toStdString();
|
||||
break;
|
||||
case olm::DecryptionErrorCode::UnknownFingerprint:
|
||||
// TODO: don't fail, just show in UI.
|
||||
nhlog::crypto()->critical("Message by unverified fingerprint {}",
|
||||
index.sender_key);
|
||||
dummy.content.body =
|
||||
tr("-- Message by unverified device! --").toStdString();
|
||||
case olm::DecryptionErrorCode::NoError:
|
||||
// unreachable
|
||||
break;
|
||||
}
|
||||
return asCacheEntry(std::move(dummy));
|
||||
}
|
||||
|
||||
std::string msg_str;
|
||||
try {
|
||||
auto session = cache::client()->getInboundMegolmSession(index);
|
||||
auto res =
|
||||
olm::client()->decrypt_group_message(session.get(), e.content.ciphertext);
|
||||
msg_str = std::string((char *)res.data.data(), res.data.size());
|
||||
} catch (const lmdb::error &e) {
|
||||
nhlog::db()->critical("failed to retrieve megolm session with index ({}, {}, {})",
|
||||
index.room_id,
|
||||
index.session_id,
|
||||
index.sender_key,
|
||||
e.what());
|
||||
dummy.content.body =
|
||||
tr("-- Decryption Error (failed to retrieve megolm keys from db) --",
|
||||
"Placeholder, when the message can't be decrypted, because the DB "
|
||||
"access "
|
||||
"failed.")
|
||||
.toStdString();
|
||||
return asCacheEntry(std::move(dummy));
|
||||
} catch (const mtx::crypto::olm_exception &e) {
|
||||
nhlog::crypto()->critical("failed to decrypt message with index ({}, {}, {}): {}",
|
||||
index.room_id,
|
||||
index.session_id,
|
||||
index.sender_key,
|
||||
e.what());
|
||||
dummy.content.body =
|
||||
tr("-- Decryption Error (%1) --",
|
||||
"Placeholder, when the message can't be decrypted. In this case, the "
|
||||
"Olm "
|
||||
"decrytion returned an error, which is passed as %1.")
|
||||
.arg(e.what())
|
||||
.toStdString();
|
||||
return asCacheEntry(std::move(dummy));
|
||||
}
|
||||
|
||||
// Add missing fields for the event.
|
||||
json body = json::parse(msg_str);
|
||||
body["event_id"] = e.event_id;
|
||||
body["sender"] = e.sender;
|
||||
body["origin_server_ts"] = e.origin_server_ts;
|
||||
body["unsigned"] = e.unsigned_data;
|
||||
|
||||
// relations are unencrypted in content...
|
||||
mtx::common::add_relations(body["content"], e.content.relations);
|
||||
|
||||
json event_array = json::array();
|
||||
event_array.push_back(body);
|
||||
|
||||
std::vector<mtx::events::collections::TimelineEvents> temp_events;
|
||||
mtx::responses::utils::parse_timeline_events(event_array, temp_events);
|
||||
|
||||
if (temp_events.size() == 1) {
|
||||
auto encInfo = mtx::accessors::file(temp_events[0]);
|
||||
|
||||
if (encInfo)
|
||||
emit newEncryptedImage(encInfo.value());
|
||||
|
||||
return asCacheEntry(std::move(temp_events[0]));
|
||||
return asCacheEntry(std::move(decryptionResult));
|
||||
}
|
||||
|
||||
auto encInfo = mtx::accessors::file(decryptionResult.event.value());
|
||||
if (encInfo)
|
||||
emit newEncryptedImage(encInfo.value());
|
||||
|
||||
return asCacheEntry(std::move(decryptionResult.event.value()));
|
||||
return asCacheEntry(std::move(decryptionResult));
|
||||
}
|
||||
|
||||
void
|
||||
EventStore::requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev,
|
||||
bool manual)
|
||||
{
|
||||
// we may not want to request keys during initial sync and such
|
||||
if (suppressKeyRequests)
|
||||
return;
|
||||
|
||||
// TODO: Look in key backup
|
||||
auto copy = ev;
|
||||
copy.room_id = room_id_;
|
||||
if (pending_key_requests.count(ev.content.session_id)) {
|
||||
auto &r = pending_key_requests.at(ev.content.session_id);
|
||||
r.events.push_back(copy);
|
||||
|
||||
// automatically request once every 10 min, manually every 1 min
|
||||
qint64 delay = manual ? 60 : (60 * 10);
|
||||
if (r.requested_at + delay < QDateTime::currentSecsSinceEpoch()) {
|
||||
r.requested_at = QDateTime::currentSecsSinceEpoch();
|
||||
olm::send_key_request_for(copy, r.request_id);
|
||||
}
|
||||
} else {
|
||||
PendingKeyRequests request;
|
||||
request.request_id = "key_request." + http::client()->generate_txn_id();
|
||||
request.requested_at = QDateTime::currentSecsSinceEpoch();
|
||||
request.events.push_back(copy);
|
||||
olm::send_key_request_for(copy, request.request_id);
|
||||
pending_key_requests[ev.content.session_id] = request;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -877,15 +796,56 @@ EventStore::get(std::string id, std::string_view related_to, bool decrypt, bool
|
|||
events_by_id_.insert(index, event_ptr);
|
||||
}
|
||||
|
||||
if (decrypt)
|
||||
if (decrypt) {
|
||||
if (auto encrypted =
|
||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
|
||||
event_ptr))
|
||||
return decryptEvent(index, *encrypted);
|
||||
event_ptr)) {
|
||||
auto decrypted = decryptEvent(index, *encrypted);
|
||||
if (decrypted->event)
|
||||
return &*decrypted->event;
|
||||
}
|
||||
}
|
||||
|
||||
return event_ptr;
|
||||
}
|
||||
|
||||
olm::DecryptionErrorCode
|
||||
EventStore::decryptionError(std::string id)
|
||||
{
|
||||
if (this->thread() != QThread::currentThread())
|
||||
nhlog::db()->warn("{} called from a different thread!", __func__);
|
||||
|
||||
if (id.empty())
|
||||
return olm::DecryptionErrorCode::NoError;
|
||||
|
||||
IdIndex index{room_id_, std::move(id)};
|
||||
auto edits_ = edits(index.id);
|
||||
if (!edits_.empty()) {
|
||||
index.id = mtx::accessors::event_id(edits_.back());
|
||||
auto event_ptr =
|
||||
new mtx::events::collections::TimelineEvents(std::move(edits_.back()));
|
||||
events_by_id_.insert(index, event_ptr);
|
||||
}
|
||||
|
||||
auto event_ptr = events_by_id_.object(index);
|
||||
if (!event_ptr) {
|
||||
auto event = cache::client()->getEvent(room_id_, index.id);
|
||||
if (!event) {
|
||||
return olm::DecryptionErrorCode::NoError;
|
||||
}
|
||||
event_ptr = new mtx::events::collections::TimelineEvents(std::move(event->data));
|
||||
events_by_id_.insert(index, event_ptr);
|
||||
}
|
||||
|
||||
if (auto encrypted =
|
||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(event_ptr)) {
|
||||
auto decrypted = decryptEvent(index, *encrypted);
|
||||
return decrypted->error;
|
||||
}
|
||||
|
||||
return olm::DecryptionErrorCode::NoError;
|
||||
}
|
||||
|
||||
void
|
||||
EventStore::fetchMore()
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <mtx/responses/messages.hpp>
|
||||
#include <mtx/responses/sync.hpp>
|
||||
|
||||
#include "Olm.h"
|
||||
#include "Reaction.h"
|
||||
|
||||
class EventStore : public QObject
|
||||
|
@ -78,6 +79,9 @@ public:
|
|||
mtx::events::collections::TimelineEvents *get(int idx, bool decrypt = true);
|
||||
|
||||
QVariantList reactions(const std::string &event_id);
|
||||
olm::DecryptionErrorCode decryptionError(std::string id);
|
||||
void requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev,
|
||||
bool manual);
|
||||
|
||||
int size() const
|
||||
{
|
||||
|
@ -119,7 +123,7 @@ public slots:
|
|||
|
||||
private:
|
||||
std::vector<mtx::events::collections::TimelineEvents> edits(const std::string &event_id);
|
||||
mtx::events::collections::TimelineEvents *decryptEvent(
|
||||
olm::DecryptionResult *decryptEvent(
|
||||
const IdIndex &idx,
|
||||
const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e);
|
||||
void handle_room_verification(mtx::events::collections::TimelineEvents event);
|
||||
|
@ -129,7 +133,7 @@ private:
|
|||
uint64_t first = std::numeric_limits<uint64_t>::max(),
|
||||
last = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
static QCache<IdIndex, mtx::events::collections::TimelineEvents> decryptedEvents_;
|
||||
static QCache<IdIndex, olm::DecryptionResult> decryptedEvents_;
|
||||
static QCache<Index, mtx::events::collections::TimelineEvents> events_;
|
||||
static QCache<IdIndex, mtx::events::collections::TimelineEvents> events_by_id_;
|
||||
|
||||
|
@ -137,6 +141,7 @@ private:
|
|||
{
|
||||
std::string request_id;
|
||||
std::vector<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>> events;
|
||||
qint64 requested_at;
|
||||
};
|
||||
std::map<std::string, PendingKeyRequests> pending_key_requests;
|
||||
|
||||
|
|
|
@ -452,6 +452,7 @@ TimelineModel::roleNames() const
|
|||
{IsEditable, "isEditable"},
|
||||
{IsEncrypted, "isEncrypted"},
|
||||
{Trustlevel, "trustlevel"},
|
||||
{EncryptionError, "encryptionError"},
|
||||
{ReplyTo, "replyTo"},
|
||||
{Reactions, "reactions"},
|
||||
{RoomId, "roomId"},
|
||||
|
@ -639,6 +640,9 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
|
|||
return crypto::Trust::Unverified;
|
||||
}
|
||||
|
||||
case EncryptionError:
|
||||
return events.decryptionError(event_id(event));
|
||||
|
||||
case ReplyTo:
|
||||
return QVariant(QString::fromStdString(relations(event).reply_to().value_or("")));
|
||||
case Reactions: {
|
||||
|
@ -690,6 +694,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
|
|||
m.insert(names[RoomName], data(event, static_cast<int>(RoomName)));
|
||||
m.insert(names[RoomTopic], data(event, static_cast<int>(RoomTopic)));
|
||||
m.insert(names[CallType], data(event, static_cast<int>(CallType)));
|
||||
m.insert(names[EncryptionError], data(event, static_cast<int>(EncryptionError)));
|
||||
|
||||
return QVariant(m);
|
||||
}
|
||||
|
@ -1551,6 +1556,17 @@ TimelineModel::scrollTimerEvent()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimelineModel::requestKeyForEvent(QString id)
|
||||
{
|
||||
auto encrypted_event = events.get(id.toStdString(), "", false);
|
||||
if (encrypted_event) {
|
||||
if (auto ev = std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
|
||||
encrypted_event))
|
||||
events.requestSession(*ev, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimelineModel::copyLinkToEvent(QString eventId) const
|
||||
{
|
||||
|
|
|
@ -212,6 +212,7 @@ public:
|
|||
IsEditable,
|
||||
IsEncrypted,
|
||||
Trustlevel,
|
||||
EncryptionError,
|
||||
ReplyTo,
|
||||
Reactions,
|
||||
RoomId,
|
||||
|
@ -264,6 +265,8 @@ public:
|
|||
endResetModel();
|
||||
}
|
||||
|
||||
Q_INVOKABLE void requestKeyForEvent(QString id);
|
||||
|
||||
std::vector<::Reaction> reactions(const std::string &event_id)
|
||||
{
|
||||
auto list = events.reactions(event_id);
|
||||
|
|
|
@ -157,6 +157,8 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
|
|||
0,
|
||||
"MtxEvent",
|
||||
"Can't instantiate enum!");
|
||||
qmlRegisterUncreatableMetaObject(
|
||||
olm::staticMetaObject, "im.nheko", 1, 0, "Olm", "Can't instantiate enum!");
|
||||
qmlRegisterUncreatableMetaObject(
|
||||
crypto::staticMetaObject, "im.nheko", 1, 0, "Crypto", "Can't instantiate enum!");
|
||||
qmlRegisterUncreatableMetaObject(verification::staticMetaObject,
|
||||
|
|
Loading…
Reference in a new issue