room: Retry to decrypt room messages
This commit is contained in:
parent
6345b24d2f
commit
002be9075e
4 changed files with 161 additions and 5 deletions
|
@ -412,7 +412,7 @@ fn build_content(parent: &adw::Bin, event: &Event, compact: bool) {
|
|||
parent.set_child(Some(&child));
|
||||
child
|
||||
};
|
||||
child.text(gettext("Fractal couldn’t decrypt this message."));
|
||||
child.text(gettext("Fractal couldn’t 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>())
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue