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));
|
parent.set_child(Some(&child));
|
||||||
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(_)) => {
|
Some(AnyMessageLikeEventContent::RoomRedaction(_)) => {
|
||||||
let child = if let Some(Ok(child)) = parent.child().map(|w| w.downcast::<MessageText>())
|
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::{
|
use matrix_sdk::{
|
||||||
config::{RequestConfig, SyncSettings},
|
config::{RequestConfig, SyncSettings},
|
||||||
deserialized_responses::SyncResponse,
|
deserialized_responses::SyncResponse,
|
||||||
|
room::Room as MatrixRoom,
|
||||||
ruma::{
|
ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::{
|
client::{
|
||||||
|
@ -36,7 +37,10 @@ use matrix_sdk::{
|
||||||
error::{FromHttpResponseError, ServerError},
|
error::{FromHttpResponseError, ServerError},
|
||||||
},
|
},
|
||||||
assign,
|
assign,
|
||||||
events::{direct::DirectEventContent, GlobalAccountDataEvent},
|
events::{
|
||||||
|
direct::DirectEventContent, room::encryption::SyncRoomEncryptionEvent,
|
||||||
|
GlobalAccountDataEvent,
|
||||||
|
},
|
||||||
RoomId,
|
RoomId,
|
||||||
},
|
},
|
||||||
store::{make_store_config, OpenStoreError},
|
store::{make_store_config, OpenStoreError},
|
||||||
|
@ -476,6 +480,7 @@ impl Session {
|
||||||
|
|
||||||
self.room_list().load();
|
self.room_list().load();
|
||||||
self.setup_direct_room_handler();
|
self.setup_direct_room_handler();
|
||||||
|
self.setup_room_encrypted_changes();
|
||||||
|
|
||||||
self.sync();
|
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 {
|
impl Default for Session {
|
||||||
|
|
|
@ -11,11 +11,13 @@ use matrix_sdk::{
|
||||||
ruma::{
|
ruma::{
|
||||||
events::{
|
events::{
|
||||||
room::{
|
room::{
|
||||||
|
encrypted::RoomEncryptedEventContent,
|
||||||
message::{MessageType, Relation},
|
message::{MessageType, Relation},
|
||||||
redaction::SyncRoomRedactionEvent,
|
redaction::SyncRoomRedactionEvent,
|
||||||
},
|
},
|
||||||
AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncRoomEvent,
|
AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncRoomEvent,
|
||||||
AnySyncStateEvent, MessageLikeUnsigned, SyncMessageLikeEvent, SyncStateEvent,
|
AnySyncStateEvent, MessageLikeUnsigned, OriginalSyncMessageLikeEvent,
|
||||||
|
SyncMessageLikeEvent, SyncStateEvent,
|
||||||
},
|
},
|
||||||
MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
|
MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
|
||||||
},
|
},
|
||||||
|
@ -27,7 +29,7 @@ use super::{
|
||||||
Member, ReactionList, Room,
|
Member, ReactionList, Room,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
spawn_tokio,
|
spawn, spawn_tokio,
|
||||||
utils::{filename_for_mime, media_type_uid},
|
utils::{filename_for_mime, media_type_uid},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,6 +56,7 @@ mod imp {
|
||||||
pub replacing_events: RefCell<Vec<super::Event>>,
|
pub replacing_events: RefCell<Vec<super::Event>>,
|
||||||
pub reactions: ReactionList,
|
pub reactions: ReactionList,
|
||||||
pub source_changed_handler: RefCell<Option<SignalHandlerId>>,
|
pub source_changed_handler: RefCell<Option<SignalHandlerId>>,
|
||||||
|
pub keys_handle: RefCell<Option<SignalHandlerId>>,
|
||||||
pub room: OnceCell<WeakRef<Room>>,
|
pub room: OnceCell<WeakRef<Room>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,6 +224,16 @@ impl Event {
|
||||||
let priv_ = self.imp();
|
let priv_ = self.imp();
|
||||||
|
|
||||||
if let Ok(deserialized) = event.event.deserialize() {
|
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));
|
priv_.event.replace(Some(deserialized));
|
||||||
} else {
|
} else {
|
||||||
warn!("Failed to deserialize event: {:?}", event);
|
warn!("Failed to deserialize event: {:?}", event);
|
||||||
|
@ -230,6 +243,35 @@ impl Event {
|
||||||
|
|
||||||
self.notify("event");
|
self.notify("event");
|
||||||
self.notify("activatable");
|
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 {
|
pub fn matrix_sender(&self) -> OwnedUserId {
|
||||||
|
|
|
@ -31,11 +31,12 @@ use matrix_sdk::{
|
||||||
redaction::{OriginalSyncRoomRedactionEvent, RoomRedactionEventContent},
|
redaction::{OriginalSyncRoomRedactionEvent, RoomRedactionEventContent},
|
||||||
topic::RoomTopicEventContent,
|
topic::RoomTopicEventContent,
|
||||||
},
|
},
|
||||||
|
room_key::ToDeviceRoomKeyEventContent,
|
||||||
tag::{TagInfo, TagName},
|
tag::{TagInfo, TagName},
|
||||||
AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncMessageLikeEvent,
|
AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncMessageLikeEvent,
|
||||||
AnySyncRoomEvent, AnySyncStateEvent, EventContent, MessageLikeEventType,
|
AnySyncRoomEvent, AnySyncStateEvent, EventContent, MessageLikeEventType,
|
||||||
MessageLikeUnsigned, OriginalSyncMessageLikeEvent, StateEventType,
|
MessageLikeUnsigned, OriginalSyncMessageLikeEvent, StateEventType,
|
||||||
SyncMessageLikeEvent, SyncStateEvent,
|
SyncMessageLikeEvent, SyncStateEvent, ToDeviceEvent,
|
||||||
},
|
},
|
||||||
receipt::ReceiptType,
|
receipt::ReceiptType,
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
|
@ -110,6 +111,8 @@ mod imp {
|
||||||
pub successor: OnceCell<OwnedRoomId>,
|
pub successor: OnceCell<OwnedRoomId>,
|
||||||
/// The most recent verification request event.
|
/// The most recent verification request event.
|
||||||
pub verification: RefCell<Option<IdentityVerification>>,
|
pub verification: RefCell<Option<IdentityVerification>>,
|
||||||
|
/// Whether this room is encrypted
|
||||||
|
pub is_encrypted: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -241,6 +244,13 @@ mod imp {
|
||||||
IdentityVerification::static_type(),
|
IdentityVerification::static_type(),
|
||||||
glib::ParamFlags::READWRITE,
|
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);
|
obj.store_topic(topic);
|
||||||
}
|
}
|
||||||
"verification" => obj.set_verification(value.get().unwrap()),
|
"verification" => obj.set_verification(value.get().unwrap()),
|
||||||
|
"encrypted" => obj.set_is_encrypted(value.get().unwrap()),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,6 +321,7 @@ mod imp {
|
||||||
|id| id.as_ref().to_value(),
|
|id| id.as_ref().to_value(),
|
||||||
),
|
),
|
||||||
"verification" => obj.verification().to_value(),
|
"verification" => obj.verification().to_value(),
|
||||||
|
"encrypted" => obj.is_encrypted().to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,6 +331,7 @@ mod imp {
|
||||||
vec![
|
vec![
|
||||||
Signal::builder("order-changed", &[], <()>::static_type().into()).build(),
|
Signal::builder("order-changed", &[], <()>::static_type().into()).build(),
|
||||||
Signal::builder("room-forgotten", &[], <()>::static_type().into()).build(),
|
Signal::builder("room-forgotten", &[], <()>::static_type().into()).build(),
|
||||||
|
Signal::builder("new-encryption-keys", &[], <()>::static_type().into()).build(),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
SIGNALS.as_ref()
|
SIGNALS.as_ref()
|
||||||
|
@ -338,6 +351,7 @@ mod imp {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
obj.load_power_levels();
|
obj.load_power_levels();
|
||||||
|
obj.setup_is_encrypted();
|
||||||
|
|
||||||
obj.bind_property("display-name", obj.avatar(), "display-name")
|
obj.bind_property("display-name", obj.avatar(), "display-name")
|
||||||
.flags(glib::BindingFlags::SYNC_CREATE)
|
.flags(glib::BindingFlags::SYNC_CREATE)
|
||||||
|
@ -1637,6 +1651,75 @@ impl Room {
|
||||||
|
|
||||||
self.set_latest_unread(latest_unread);
|
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.
|
/// Whether the given event can count as an unread message.
|
||||||
|
|
Loading…
Reference in a new issue