[fractal] verification: Move in-room verification outside of Timeline



commit 1614ce91797149421cc855698df67abf743676fa
Author: Kévin Commaille <zecakeh tedomum fr>
Date:   Fri Apr 22 20:17:38 2022 +0200

    verification: Move in-room verification outside of Timeline

 data/resources/ui/content-room-history.ui     |   6 +-
 src/session/room/mod.rs                       |  33 ++++-
 src/session/room/timeline/mod.rs              | 151 +++--------------------
 src/session/verification/verification_list.rs | 171 ++++++++++++++++++++++----
 4 files changed, 194 insertions(+), 167 deletions(-)
---
diff --git a/data/resources/ui/content-room-history.ui b/data/resources/ui/content-room-history.ui
index 921f3d5dc..90cd23d48 100644
--- a/data/resources/ui/content-room-history.ui
+++ b/data/resources/ui/content-room-history.ui
@@ -113,10 +113,8 @@
         <child>
           <object class="ContentVerificationInfoBar" id="verification_info_bar">
             <binding name="request">
-              <lookup name="verification" type="Timeline">
-                <lookup name="timeline" type="Room">
-                  <lookup name="room">ContentRoomHistory</lookup>
-                </lookup>
+              <lookup name="verification">
+                <lookup name="room">ContentRoomHistory</lookup>
               </lookup>
             </binding>
           </object>
diff --git a/src/session/room/mod.rs b/src/session/room/mod.rs
index 9688e5975..28e913daa 100644
--- a/src/session/room/mod.rs
+++ b/src/session/room/mod.rs
@@ -57,6 +57,7 @@ pub use self::{
         TimelineSpinner, TimelineState,
     },
 };
+use super::verification::IdentityVerification;
 use crate::{
     components::{Pill, Toast},
     gettext_f, ngettext_f,
@@ -104,6 +105,8 @@ mod imp {
         pub highlight: Cell<HighlightFlags>,
         pub predecessor: OnceCell<Box<RoomId>>,
         pub successor: OnceCell<Box<RoomId>>,
+        /// The most recent verification request event.
+        pub verification: RefCell<Option<IdentityVerification>>,
     }
 
     #[glib::object_subclass]
@@ -228,6 +231,13 @@ mod imp {
                         None,
                         glib::ParamFlags::READABLE,
                     ),
+                    glib::ParamSpecObject::new(
+                        "verification",
+                        "Verification",
+                        "The most recent active verification for a user in this room",
+                        IdentityVerification::static_type(),
+                        glib::ParamFlags::READWRITE,
+                    ),
                 ]
             });
 
@@ -262,6 +272,7 @@ mod imp {
                     let topic = value.get().unwrap();
                     obj.store_topic(topic);
                 }
+                "verification" => obj.set_verification(value.get().unwrap()),
                 _ => unimplemented!(),
             }
         }
@@ -295,6 +306,7 @@ mod imp {
                     },
                     |id| id.as_ref().to_value(),
                 ),
+                "verification" => obj.verification().to_value(),
                 _ => unimplemented!(),
             }
         }
@@ -1031,8 +1043,13 @@ impl Room {
         // FIXME: notify only when the count has changed
         self.notify_notification_count();
 
-        for event in batch.iter().flat_map(|e| e.event.deserialize().ok()) {
-            match &event {
+        let events: Vec<_> = batch
+            .iter()
+            .flat_map(|e| e.event.deserialize().ok())
+            .collect();
+
+        for event in events.iter() {
+            match event {
                 AnySyncRoomEvent::State(AnySyncStateEvent::RoomMember(event)) => {
                     self.members().update_member_for_member_event(event)
                 }
@@ -1055,6 +1072,9 @@ impl Room {
                 _ => {}
             }
         }
+        self.session()
+            .verification_list()
+            .handle_response_room(self, events.iter());
 
         self.emit_by_name::<()>("order-changed", &[]);
     }
@@ -1512,6 +1532,15 @@ impl Room {
             error!("Can’t invite users, because this room isn’t a joined room");
         }
     }
+
+    pub fn set_verification(&self, verification: IdentityVerification) {
+        self.imp().verification.replace(Some(verification));
+        self.notify("verification");
+    }
+
+    pub fn verification(&self) -> Option<IdentityVerification> {
+        self.imp().verification.borrow().clone()
+    }
 }
 
 trait GlibDateTime {
diff --git a/src/session/room/timeline/mod.rs b/src/session/room/timeline/mod.rs
index ad14c91b1..aaa6de3bf 100644
--- a/src/session/room/timeline/mod.rs
+++ b/src/session/room/timeline/mod.rs
@@ -14,10 +14,7 @@ use gtk::{gio, glib, prelude::*, subclass::prelude::*};
 use log::{error, warn};
 use matrix_sdk::{
     deserialized_responses::SyncRoomEvent,
-    ruma::{
-        events::{room::message::MessageType, AnySyncMessageLikeEvent, AnySyncRoomEvent},
-        EventId, TransactionId,
-    },
+    ruma::{EventId, TransactionId},
     Error as MatrixError,
 };
 pub use timeline_day_divider::TimelineDayDivider;
@@ -27,11 +24,7 @@ pub use timeline_spinner::TimelineSpinner;
 use tokio::task::JoinHandle;
 
 use crate::{
-    session::{
-        room::{Event, Room},
-        user::UserExt,
-        verification::{IdentityVerification, VERIFICATION_CREATION_TIMEOUT},
-    },
+    session::room::{Event, Room},
     spawn_tokio,
 };
 
@@ -79,8 +72,6 @@ mod imp {
         /// A Hashset of `EventId`s that where just redacted.
         pub redacted_events: RefCell<HashSet<Box<EventId>>>,
         pub state: Cell<TimelineState>,
-        /// The most recent verification request event
-        pub verification: RefCell<Option<IdentityVerification>>,
         pub backward_stream: Arc<Mutex<Option<BackwardStream>>>,
         pub forward_handle: Arc<Mutex<Option<JoinHandle<()>>>>,
     }
@@ -118,13 +109,6 @@ mod imp {
                         TimelineState::default() as i32,
                         glib::ParamFlags::READABLE,
                     ),
-                    glib::ParamSpecObject::new(
-                        "verification",
-                        "Verification",
-                        "The most recent active verification for a user in this timeline",
-                        IdentityVerification::static_type(),
-                        glib::ParamFlags::READABLE,
-                    ),
                 ]
             });
 
@@ -152,7 +136,6 @@ mod imp {
                 "room" => obj.room().to_value(),
                 "empty" => obj.is_empty().to_value(),
                 "state" => obj.state().to_value(),
-                "verification" => obj.verification().to_value(),
                 _ => unimplemented!(),
             }
         }
@@ -571,7 +554,7 @@ impl Timeline {
 
         match handle.await.unwrap() {
             Ok(Some(events)) => {
-                let events: Vec<Event> = events
+                let events: Vec<_> = events
                     .into_iter()
                     .filter_map(|event| match event {
                         Ok(event) => Some(event),
@@ -580,7 +563,20 @@ impl Timeline {
                             None
                         }
                     })
-                    .map(|event| Event::new(event, &self.room()))
+                    .collect();
+
+                let deser_events: Vec<_> = events
+                    .iter()
+                    .filter_map(|event| event.event.deserialize().ok())
+                    .collect();
+                let room = self.room();
+                room.session()
+                    .verification_list()
+                    .handle_response_room(&room, deser_events.iter());
+
+                let events: Vec<Event> = events
+                    .into_iter()
+                    .map(|event| Event::new(event, &room))
                     .collect();
 
                 self.remove_loading_spinner();
@@ -670,8 +666,6 @@ impl Timeline {
             for event in batch.into_iter() {
                 let event_id = event.matrix_event_id();
 
-                self.handle_verification(&event);
-
                 if let Some(pending_id) = event
                     .matrix_transaction_id()
                     .and_then(|txn_id| pending_events.remove(&txn_id))
@@ -784,8 +778,6 @@ impl Timeline {
             priv_.list.borrow_mut().reserve(added);
 
             for event in batch {
-                self.handle_verification(&event);
-
                 priv_
                     .event_map
                     .borrow_mut()
@@ -847,115 +839,6 @@ impl Timeline {
         self.imp().list.borrow_mut().pop_front();
         self.upcast_ref::<gio::ListModel>().items_changed(0, 1, 0);
     }
-
-    fn set_verification(&self, verification: IdentityVerification) {
-        self.imp().verification.replace(Some(verification));
-        self.notify("verification");
-    }
-
-    pub fn verification(&self) -> Option<IdentityVerification> {
-        self.imp().verification.borrow().clone()
-    }
-
-    fn handle_verification(&self, event: &Event) {
-        let message = if let Some(AnySyncRoomEvent::MessageLike(message)) = event.matrix_event() {
-            message
-        } else {
-            return;
-        };
-
-        let session = self.room().session();
-        let verification_list = session.verification_list();
-
-        let request = match message {
-            AnySyncMessageLikeEvent::RoomMessage(message) => {
-                if let MessageType::VerificationRequest(request) = message.content.msgtype {
-                    // Ignore request that are too old
-                    if let Some(time) = message.origin_server_ts.to_system_time() {
-                        if let Ok(duration) = time.elapsed() {
-                            if duration > VERIFICATION_CREATION_TIMEOUT {
-                                return;
-                            }
-                        } else {
-                            warn!("Ignoring verification request because it was sent in the future. The 
system time of the server or the local machine is probably wrong.");
-                            return;
-                        }
-                    } else {
-                        return;
-                    }
-
-                    let user = session.user().unwrap();
-
-                    let user_to_verify = if *request.to == *user.user_id() {
-                        // The request was sent by another user to verify us
-                        event.sender()
-                    } else if *message.sender == *user.user_id() {
-                        // The request was sent by us to verify another user
-                        self.room().members().member_by_id(request.to.into())
-                    } else {
-                        // Ignore the request when it doesn't verify us or wasn't set by us
-                        return;
-                    };
-
-                    // Ignore the request when we have a newer one
-                    let previous_verification = self.verification();
-                    if !(previous_verification.is_none()
-                        || &event.timestamp() > previous_verification.unwrap().start_time())
-                    {
-                        return;
-                    }
-
-                    let request = if let Some(request) = verification_list
-                        .get_by_id(&user_to_verify.user_id(), &event.matrix_event_id())
-                    {
-                        request
-                    } else {
-                        let request = IdentityVerification::for_flow_id(
-                            event.matrix_event_id().as_str(),
-                            &session,
-                            &user_to_verify.upcast(),
-                            &event.timestamp(),
-                        );
-
-                        verification_list.add(request.clone());
-                        request
-                    };
-
-                    self.set_verification(request);
-                }
-
-                return;
-            }
-            AnySyncMessageLikeEvent::KeyVerificationReady(e) => {
-                verification_list.get_by_id(&e.sender, &e.content.relates_to.event_id)
-            }
-            AnySyncMessageLikeEvent::KeyVerificationStart(e) => {
-                verification_list.get_by_id(&e.sender, &e.content.relates_to.event_id)
-            }
-            AnySyncMessageLikeEvent::KeyVerificationCancel(e) => {
-                verification_list.get_by_id(&e.sender, &e.content.relates_to.event_id)
-            }
-            AnySyncMessageLikeEvent::KeyVerificationAccept(e) => {
-                verification_list.get_by_id(&e.sender, &e.content.relates_to.event_id)
-            }
-            AnySyncMessageLikeEvent::KeyVerificationKey(e) => {
-                verification_list.get_by_id(&e.sender, &e.content.relates_to.event_id)
-            }
-            AnySyncMessageLikeEvent::KeyVerificationMac(e) => {
-                verification_list.get_by_id(&e.sender, &e.content.relates_to.event_id)
-            }
-            AnySyncMessageLikeEvent::KeyVerificationDone(e) => {
-                verification_list.get_by_id(&e.sender, &e.content.relates_to.event_id)
-            }
-            _ => {
-                return;
-            }
-        };
-
-        if let Some(request) = request {
-            request.notify_state();
-        }
-    }
 }
 
 async fn handle_forward_stream(
diff --git a/src/session/verification/verification_list.rs b/src/session/verification/verification_list.rs
index bbe3da93d..3f4e76489 100644
--- a/src/session/verification/verification_list.rs
+++ b/src/session/verification/verification_list.rs
@@ -3,13 +3,17 @@ use std::sync::Arc;
 use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
 use log::{debug, warn};
 use matrix_sdk::ruma::{
-    api::client::sync::sync_events::v3::ToDevice, events::AnyToDeviceEvent, UserId,
+    api::client::sync::sync_events::v3::ToDevice,
+    events::{
+        room::message::MessageType, AnySyncMessageLikeEvent, AnySyncRoomEvent, AnyToDeviceEvent,
+    },
+    MilliSecondsSinceUnixEpoch, UserId,
 };
 
 use crate::session::{
     user::UserExt,
     verification::{IdentityVerification, VERIFICATION_CREATION_TIMEOUT},
-    Session,
+    Room, Session,
 };
 
 #[derive(Hash, PartialEq, Eq, Debug)]
@@ -135,7 +139,7 @@ impl VerificationList {
 
     pub fn handle_response_to_device(&self, to_device: ToDevice) {
         for event in to_device.events.iter().filter_map(|e| e.deserialize().ok()) {
-            debug!("Received verification event: {:?}", event);
+            debug!("Received to-device verification event: {:?}", event);
             let request = match event {
                 AnyToDeviceEvent::KeyVerificationRequest(e) => {
                     if let Some(request) = self.get_by_id(&e.sender, &e.content.transaction_id) {
@@ -149,32 +153,13 @@ impl VerificationList {
                             continue;
                         }
 
-                        // Ignore request that are too old
-                        let start_time = if let Some(time) = e.content.timestamp.to_system_time() {
-                            if let Ok(duration) = time.elapsed() {
-                                if duration > VERIFICATION_CREATION_TIMEOUT {
-                                    debug!("Received verification event that already timedout");
-                                    continue;
-                                }
-
-                                if let Ok(time) = glib::DateTime::from_unix_utc(
-                                    e.content.timestamp.as_secs().into(),
-                                )
-                                .and_then(|t| t.to_local())
-                                {
-                                    time
-                                } else {
-                                    warn!("Ignore verification request because getting a correct timestamp 
failed");
-                                    continue;
-                                }
+                        // Ignore requests that are too old
+                        let start_time =
+                            if let Some(time) = start_time_from_timestamp(&e.content.timestamp) {
+                                time
                             } else {
-                                warn!("Ignore verification request because it was sent in the future. The 
system time of the server or the local machine is probably wrong.");
                                 continue;
-                            }
-                        } else {
-                            warn!("Ignore verification request because getting a correct timestamp failed");
-                            continue;
-                        };
+                            };
 
                         let request = IdentityVerification::for_flow_id(
                             e.content.transaction_id.as_str(),
@@ -217,6 +202,113 @@ impl VerificationList {
         }
     }
 
+    pub fn handle_response_room<'a>(
+        &self,
+        room: &Room,
+        events: impl Iterator<Item = &'a AnySyncRoomEvent>,
+    ) {
+        for message_event in events.filter_map(|event| {
+            if let AnySyncRoomEvent::MessageLike(message_event) = event {
+                Some(message_event)
+            } else {
+                None
+            }
+        }) {
+            let request = match message_event {
+                AnySyncMessageLikeEvent::RoomMessage(message) => {
+                    if let MessageType::VerificationRequest(request) = &message.content.msgtype {
+                        debug!("Received in-room verification event: {:?}", message);
+                        // Ignore request that are too old
+                        let start_time = if let Some(time) =
+                            start_time_from_timestamp(&message.origin_server_ts)
+                        {
+                            time
+                        } else {
+                            continue;
+                        };
+
+                        let session = self.session();
+                        let user = session.user().unwrap();
+
+                        let user_to_verify = if *request.to == *user.user_id() {
+                            // The request was sent by another user to verify us
+                            room.members().member_by_id(message.sender.clone().into())
+                        } else if *message.sender == *user.user_id() {
+                            // The request was sent by us to verify another user
+                            room.members().member_by_id(request.to.clone().into())
+                        } else {
+                            // Ignore the request when it doesn't verify us or wasn't set by us
+                            continue;
+                        };
+
+                        // Ignore the request when we have a newer one
+                        let previous_verification = room.verification();
+                        if !(previous_verification.is_none()
+                            || &start_time > previous_verification.unwrap().start_time())
+                        {
+                            continue;
+                        }
+
+                        let request = if let Some(request) =
+                            self.get_by_id(&user_to_verify.user_id(), &message.event_id)
+                        {
+                            request
+                        } else {
+                            let request = IdentityVerification::for_flow_id(
+                                message.event_id.as_str(),
+                                &session,
+                                &user_to_verify.upcast(),
+                                &start_time,
+                            );
+
+                            self.add(request.clone());
+                            request
+                        };
+
+                        room.set_verification(request);
+                    }
+
+                    continue;
+                }
+                AnySyncMessageLikeEvent::KeyVerificationReady(e) => {
+                    debug!("Received in-room verification event: {:?}", e);
+                    self.get_by_id(&e.sender, &e.content.relates_to.event_id)
+                }
+                AnySyncMessageLikeEvent::KeyVerificationStart(e) => {
+                    debug!("Received in-room verification event: {:?}", e);
+                    self.get_by_id(&e.sender, &e.content.relates_to.event_id)
+                }
+                AnySyncMessageLikeEvent::KeyVerificationCancel(e) => {
+                    debug!("Received in-room verification event: {:?}", e);
+                    self.get_by_id(&e.sender, &e.content.relates_to.event_id)
+                }
+                AnySyncMessageLikeEvent::KeyVerificationAccept(e) => {
+                    debug!("Received in-room verification event: {:?}", e);
+                    self.get_by_id(&e.sender, &e.content.relates_to.event_id)
+                }
+                AnySyncMessageLikeEvent::KeyVerificationKey(e) => {
+                    debug!("Received in-room verification event: {:?}", e);
+                    self.get_by_id(&e.sender, &e.content.relates_to.event_id)
+                }
+                AnySyncMessageLikeEvent::KeyVerificationMac(e) => {
+                    debug!("Received in-room verification event: {:?}", e);
+                    self.get_by_id(&e.sender, &e.content.relates_to.event_id)
+                }
+                AnySyncMessageLikeEvent::KeyVerificationDone(e) => {
+                    debug!("Received in-room verification event: {:?}", e);
+                    self.get_by_id(&e.sender, &e.content.relates_to.event_id)
+                }
+                _ => {
+                    continue;
+                }
+            };
+
+            if let Some(request) = request {
+                request.notify_state();
+            }
+        }
+    }
+
     /// Add a new `IdentityVerification` request
     pub fn add(&self, request: IdentityVerification) {
         // Don't add requests that are already finished
@@ -287,3 +379,28 @@ impl VerificationList {
         None
     }
 }
+
+fn start_time_from_timestamp(timestamp: &MilliSecondsSinceUnixEpoch) -> Option<glib::DateTime> {
+    if let Some(time) = timestamp.to_system_time() {
+        if let Ok(duration) = time.elapsed() {
+            if duration > VERIFICATION_CREATION_TIMEOUT {
+                debug!("Received verification event that already timedout");
+                return None;
+            }
+
+            if let Ok(time) =
+                glib::DateTime::from_unix_utc(timestamp.as_secs().into()).and_then(|t| t.to_local())
+            {
+                return Some(time);
+            } else {
+                warn!("Ignore verification request because getting a correct timestamp failed");
+            }
+        } else {
+            warn!("Ignore verification request because it was sent in the future. The system time of the 
server or the local machine is probably wrong.");
+        }
+    } else {
+        warn!("Ignore verification request because getting a correct timestamp failed");
+    }
+
+    None
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]