room: Retry to decrypt room messages

This commit is contained in:
Julian Sparber 2022-05-13 12:24:08 +02:00
parent 6345b24d2f
commit 002be9075e
4 changed files with 161 additions and 5 deletions

View file

@ -412,7 +412,7 @@ fn build_content(parent: &adw::Bin, event: &Event, compact: bool) {
parent.set_child(Some(&child));
child
};
child.text(gettext("Fractal couldnt decrypt this message."));
child.text(gettext("Fractal couldnt decrypt this message, but will retry once the keys are available."));
}
Some(AnyMessageLikeEventContent::RoomRedaction(_)) => {
let child = if let Some(Ok(child)) = parent.child().map(|w| w.downcast::<MessageText>())

View file

@ -26,6 +26,7 @@ use log::{debug, error, warn};
use matrix_sdk::{
config::{RequestConfig, SyncSettings},
deserialized_responses::SyncResponse,
room::Room as MatrixRoom,
ruma::{
api::{
client::{
@ -36,7 +37,10 @@ use matrix_sdk::{
error::{FromHttpResponseError, ServerError},
},
assign,
events::{direct::DirectEventContent, GlobalAccountDataEvent},
events::{
direct::DirectEventContent, room::encryption::SyncRoomEncryptionEvent,
GlobalAccountDataEvent,
},
RoomId,
},
store::{make_store_config, OpenStoreError},
@ -476,6 +480,7 @@ impl Session {
self.room_list().load();
self.setup_direct_room_handler();
self.setup_room_encrypted_changes();
self.sync();
@ -881,6 +886,32 @@ impl Session {
})
);
}
fn setup_room_encrypted_changes(&self) {
let session_weak = glib::SendWeakRef::from(self.downgrade());
let client = self.client();
spawn_tokio!(async move {
client
.register_event_handler(
move |_: SyncRoomEncryptionEvent, matrix_room: MatrixRoom| {
let session_weak = session_weak.clone();
async move {
let ctx = glib::MainContext::default();
ctx.spawn(async move {
if let Some(session) = session_weak.upgrade() {
if let Some(room) =
session.room_list().get(matrix_room.room_id())
{
room.set_is_encrypted(true);
}
}
});
}
},
)
.await;
});
}
}
impl Default for Session {

View file

@ -11,11 +11,13 @@ use matrix_sdk::{
ruma::{
events::{
room::{
encrypted::RoomEncryptedEventContent,
message::{MessageType, Relation},
redaction::SyncRoomRedactionEvent,
},
AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncRoomEvent,
AnySyncStateEvent, MessageLikeUnsigned, SyncMessageLikeEvent, SyncStateEvent,
AnySyncStateEvent, MessageLikeUnsigned, OriginalSyncMessageLikeEvent,
SyncMessageLikeEvent, SyncStateEvent,
},
MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
},
@ -27,7 +29,7 @@ use super::{
Member, ReactionList, Room,
};
use crate::{
spawn_tokio,
spawn, spawn_tokio,
utils::{filename_for_mime, media_type_uid},
};
@ -54,6 +56,7 @@ mod imp {
pub replacing_events: RefCell<Vec<super::Event>>,
pub reactions: ReactionList,
pub source_changed_handler: RefCell<Option<SignalHandlerId>>,
pub keys_handle: RefCell<Option<SignalHandlerId>>,
pub room: OnceCell<WeakRef<Room>>,
}
@ -221,6 +224,16 @@ impl Event {
let priv_ = self.imp();
if let Ok(deserialized) = event.event.deserialize() {
if let AnySyncRoomEvent::MessageLike(AnySyncMessageLikeEvent::RoomEncrypted(
SyncMessageLikeEvent::Original(ref encrypted),
)) = deserialized
{
let encrypted = encrypted.to_owned();
spawn!(clone!(@weak self as obj => async move {
obj.try_to_decrypt(encrypted).await;
}));
}
priv_.event.replace(Some(deserialized));
} else {
warn!("Failed to deserialize event: {:?}", event);
@ -230,6 +243,35 @@ impl Event {
self.notify("event");
self.notify("activatable");
self.notify("source");
}
async fn try_to_decrypt(&self, event: OriginalSyncMessageLikeEvent<RoomEncryptedEventContent>) {
let priv_ = self.imp();
let room = self.room().matrix_room();
let handle = spawn_tokio!(async move { room.decrypt_event(&event).await });
match handle.await.unwrap() {
Ok(decrypted) => {
if let Some(keys_handle) = priv_.keys_handle.take() {
self.room().disconnect(keys_handle);
}
self.set_matrix_pure_event(decrypted.into());
}
Err(error) => {
warn!("Failed to decrypt event: {}", error);
if priv_.keys_handle.borrow().is_none() {
let handle = self.room().connect_new_encryption_keys(
clone!(@weak self as obj => move |_| {
// Try to decrypt the event again
obj.set_matrix_pure_event(obj.matrix_pure_event());
}),
);
priv_.keys_handle.replace(Some(handle));
}
}
}
}
pub fn matrix_sender(&self) -> OwnedUserId {

View file

@ -31,11 +31,12 @@ use matrix_sdk::{
redaction::{OriginalSyncRoomRedactionEvent, RoomRedactionEventContent},
topic::RoomTopicEventContent,
},
room_key::ToDeviceRoomKeyEventContent,
tag::{TagInfo, TagName},
AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncMessageLikeEvent,
AnySyncRoomEvent, AnySyncStateEvent, EventContent, MessageLikeEventType,
MessageLikeUnsigned, OriginalSyncMessageLikeEvent, StateEventType,
SyncMessageLikeEvent, SyncStateEvent,
SyncMessageLikeEvent, SyncStateEvent, ToDeviceEvent,
},
receipt::ReceiptType,
serde::Raw,
@ -110,6 +111,8 @@ mod imp {
pub successor: OnceCell<OwnedRoomId>,
/// The most recent verification request event.
pub verification: RefCell<Option<IdentityVerification>>,
/// Whether this room is encrypted
pub is_encrypted: Cell<bool>,
}
#[glib::object_subclass]
@ -241,6 +244,13 @@ mod imp {
IdentityVerification::static_type(),
glib::ParamFlags::READWRITE,
),
glib::ParamSpecBoolean::new(
"encrypted",
"Encrypted",
"Whether this room is encrypted",
false,
glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
),
]
});
@ -276,6 +286,7 @@ mod imp {
obj.store_topic(topic);
}
"verification" => obj.set_verification(value.get().unwrap()),
"encrypted" => obj.set_is_encrypted(value.get().unwrap()),
_ => unimplemented!(),
}
}
@ -310,6 +321,7 @@ mod imp {
|id| id.as_ref().to_value(),
),
"verification" => obj.verification().to_value(),
"encrypted" => obj.is_encrypted().to_value(),
_ => unimplemented!(),
}
}
@ -319,6 +331,7 @@ mod imp {
vec![
Signal::builder("order-changed", &[], <()>::static_type().into()).build(),
Signal::builder("room-forgotten", &[], <()>::static_type().into()).build(),
Signal::builder("new-encryption-keys", &[], <()>::static_type().into()).build(),
]
});
SIGNALS.as_ref()
@ -338,6 +351,7 @@ mod imp {
.unwrap();
obj.load_power_levels();
obj.setup_is_encrypted();
obj.bind_property("display-name", obj.avatar(), "display-name")
.flags(glib::BindingFlags::SYNC_CREATE)
@ -1637,6 +1651,75 @@ impl Room {
self.set_latest_unread(latest_unread);
}
pub fn is_encrypted(&self) -> bool {
self.imp().is_encrypted.get()
}
pub fn set_is_encrypted(&self, is_encrypted: bool) {
let was_encrypted = self.is_encrypted();
if was_encrypted == is_encrypted {
return;
}
if was_encrypted && !is_encrypted {
error!("Encryption for a room can't be disabled");
return;
}
if self.matrix_room().is_encrypted() != is_encrypted {
// TODO: enable encryption if it isn't enabled yet
}
self.setup_is_encrypted();
}
fn setup_is_encrypted(&self) {
if !self.matrix_room().is_encrypted() {
return;
}
self.setup_new_encryption_keys_handler();
self.imp().is_encrypted.set(true);
self.notify("encrypted");
}
fn setup_new_encryption_keys_handler(&self) {
spawn!(
glib::PRIORITY_DEFAULT_IDLE,
clone!(@weak self as obj => async move {
let obj_weak = glib::SendWeakRef::from(obj.downgrade());
obj.session().client().register_event_handler(
move |event: ToDeviceEvent<ToDeviceRoomKeyEventContent>| {
let obj_weak = obj_weak.clone();
async move {
let ctx = glib::MainContext::default();
ctx.spawn(async move {
if let Some(room) = obj_weak.upgrade() {
if room.room_id() == event.content.room_id {
room.emit_by_name::<()>("new-encryption-keys", &[]);
}
}
});
}
},
)
.await;
})
);
}
pub fn connect_new_encryption_keys<F: Fn(&Self) + 'static>(
&self,
f: F,
) -> glib::SignalHandlerId {
self.connect_local("new-encryption-keys", true, move |values| {
let obj = values[0].get::<Self>().unwrap();
f(&obj);
None
})
}
}
/// Whether the given event can count as an unread message.