fractal/session/model/verification/
identity_verification.rs

1use futures_util::StreamExt;
2use gtk::{
3    glib,
4    glib::{clone, closure_local},
5    prelude::*,
6    subclass::prelude::*,
7};
8use matrix_sdk::encryption::verification::{
9    CancelInfo, Emoji, QrVerification, QrVerificationData, QrVerificationState, SasState,
10    SasVerification, Verification, VerificationRequest, VerificationRequestState,
11};
12use qrcode::QrCode;
13use ruma::{
14    events::key::verification::{cancel::CancelCode, VerificationMethod, REQUEST_RECEIVED_TIMEOUT},
15    OwnedDeviceId,
16};
17use tracing::{debug, error};
18
19use super::{load_supported_verification_methods, VerificationKey};
20use crate::{
21    components::QrCodeScanner,
22    prelude::*,
23    session::model::{Member, Membership, Room, User},
24    spawn, spawn_tokio,
25    utils::BoundConstructOnlyObject,
26};
27
28#[glib::flags(name = "VerificationSupportedMethods")]
29pub enum VerificationSupportedMethods {
30    SAS = 0b0000_0001,
31    QR_SHOW = 0b0000_0010,
32    QR_SCAN = 0b0000_0100,
33}
34
35impl<'a> From<&'a [VerificationMethod]> for VerificationSupportedMethods {
36    fn from(methods: &'a [VerificationMethod]) -> Self {
37        let mut result = Self::empty();
38
39        for method in methods {
40            match method {
41                VerificationMethod::SasV1 => result.insert(Self::SAS),
42                VerificationMethod::QrCodeScanV1 => result.insert(Self::QR_SCAN),
43                VerificationMethod::QrCodeShowV1 => result.insert(Self::QR_SHOW),
44                _ => {}
45            }
46        }
47
48        result
49    }
50}
51
52impl Default for VerificationSupportedMethods {
53    fn default() -> Self {
54        Self::empty()
55    }
56}
57
58#[derive(Debug, Default, Eq, PartialEq, Clone, Copy, glib::Enum)]
59#[enum_type(name = "VerificationState")]
60pub enum VerificationState {
61    /// We created and sent the request.
62    ///
63    /// We must wait for the other user/device to accept it.
64    #[default]
65    Created,
66    /// The other user/device sent us a request.
67    ///
68    /// We should ask the user if they want to accept it.
69    Requested,
70    /// We support none of the other user's verification methods.
71    NoSupportedMethods,
72    /// The request was accepted.
73    ///
74    /// We should ask the user to choose a method.
75    Ready,
76    /// An SAS verification was started.
77    ///
78    /// We should show the emojis and ask the user to confirm that they match.
79    SasConfirm,
80    /// The user wants to scan a QR Code.
81    QrScan,
82    /// The user scanned a QR Code.
83    QrScanned,
84    /// Our QR Code was scanned.
85    ///
86    /// We should ask the user to confirm that the QR Code was scanned
87    /// successfully.
88    QrConfirm,
89    /// The verification was successful.
90    Done,
91    /// The verification was cancelled.
92    Cancelled,
93    /// The verification was automatically dismissed.
94    ///
95    /// Happens when a received request is not accepted by us after 2 minutes.
96    Dismissed,
97    /// The verification was happening in-room but the room was left.
98    RoomLeft,
99    /// An unexpected error happened.
100    Error,
101}
102
103mod imp {
104    use std::{
105        cell::{Cell, OnceCell, RefCell},
106        marker::PhantomData,
107        sync::LazyLock,
108    };
109
110    use glib::subclass::Signal;
111
112    use super::*;
113
114    #[derive(Default, glib::Properties)]
115    #[properties(wrapper_type = super::IdentityVerification)]
116    pub struct IdentityVerification {
117        /// The SDK's verification request.
118        request: OnceCell<VerificationRequest>,
119        request_changes_abort_handle: RefCell<Option<tokio::task::AbortHandle>>,
120        /// The SDK's verification, if one was started.
121        verification: RefCell<Option<Verification>>,
122        verification_changes_abort_handle: RefCell<Option<tokio::task::AbortHandle>>,
123        /// The user to verify.
124        #[property(get, set = Self::set_user, construct_only)]
125        user: BoundConstructOnlyObject<User>,
126        /// The room of this verification, if any.
127        #[property(get, set = Self::set_room, construct_only)]
128        room: glib::WeakRef<Room>,
129        membership_handler: RefCell<Option<glib::SignalHandlerId>>,
130        /// The state of this verification
131        #[property(get, set = Self::set_state, construct_only, builder(VerificationState::default()))]
132        state: Cell<VerificationState>,
133        /// Whether the verification request was accepted.
134        ///
135        /// It means that the verification reached at least the `Ready` state.
136        #[property(get)]
137        was_accepted: Cell<bool>,
138        /// Whether this verification is finished.
139        #[property(get = Self::is_finished)]
140        is_finished: PhantomData<bool>,
141        /// The supported methods of the verification request.
142        #[property(get = Self::supported_methods, type = VerificationSupportedMethods)]
143        supported_methods: RefCell<Vec<VerificationMethod>>,
144        /// The flow ID of this verification.
145        #[property(get = Self::flow_id)]
146        flow_id: PhantomData<String>,
147        /// The time and date when the verification request was received.
148        #[property(get)]
149        received_time: OnceCell<glib::DateTime>,
150        received_timeout_source: RefCell<Option<glib::SourceId>>,
151        /// The display name of this verification.
152        #[property(get = Self::display_name)]
153        display_name: PhantomData<String>,
154        /// The QR Code, if the `QrCodeShowV1` method is supported.
155        pub(super) qr_code: RefCell<Option<QrCode>>,
156        /// The QR code scanner, if the user wants to scan a QR Code and we
157        /// have access to the camera.
158        #[property(get)]
159        pub(super) qrcode_scanner: RefCell<Option<QrCodeScanner>>,
160        /// Whether this verification was viewed by the user.
161        #[property(get, set = Self::set_was_viewed, explicit_notify)]
162        was_viewed: Cell<bool>,
163    }
164
165    #[glib::object_subclass]
166    impl ObjectSubclass for IdentityVerification {
167        const NAME: &'static str = "IdentityVerification";
168        type Type = super::IdentityVerification;
169    }
170
171    #[glib::derived_properties]
172    impl ObjectImpl for IdentityVerification {
173        fn signals() -> &'static [Signal] {
174            static SIGNALS: LazyLock<Vec<Signal>> = LazyLock::new(|| {
175                vec![
176                    // The SAS data changed.
177                    Signal::builder("sas-data-changed").build(),
178                    // The cancel info changed.
179                    Signal::builder("cancel-info-changed").build(),
180                    // The verification has been replaced by a new one.
181                    Signal::builder("replaced")
182                        .param_types([super::IdentityVerification::static_type()])
183                        .build(),
184                    // The verification is done, but has not changed its state yes.
185                    //
186                    // Return `glib::Propagation::Stop` in a signal handler to prevent the state
187                    // from changing to `VerificationState::Done`. Can be used to replace the last
188                    // screen of `IdentityVerificationView`.
189                    Signal::builder("done").return_type::<bool>().build(),
190                    // The verification can be dismissed.
191                    Signal::builder("dismiss").build(),
192                    // The verification should be removed from the verification list.
193                    Signal::builder("remove-from-list").build(),
194                ]
195            });
196            SIGNALS.as_ref()
197        }
198
199        fn dispose(&self) {
200            if let Some(handler) = self.membership_handler.take() {
201                if let Some(room) = self.room.upgrade() {
202                    room.own_member().disconnect(handler);
203                }
204            }
205            if let Some(handle) = self.request_changes_abort_handle.take() {
206                handle.abort();
207            }
208            if let Some(handle) = self.verification_changes_abort_handle.take() {
209                handle.abort();
210            }
211            if let Some(source) = self.received_timeout_source.take() {
212                source.remove();
213            }
214
215            let request = self.request().clone();
216            if !request.is_done() && !request.is_passive() && !request.is_cancelled() {
217                spawn_tokio!(async move {
218                    if let Err(error) = request.cancel().await {
219                        error!("Could not cancel verification request on dispose: {error}");
220                    }
221                });
222            }
223        }
224    }
225
226    impl IdentityVerification {
227        /// Set the SDK's verification request.
228        pub(super) async fn set_request(&self, request: VerificationRequest) {
229            let request = self.request.get_or_init(|| request);
230
231            let Ok(datetime) = glib::DateTime::now_local() else {
232                error!("Could not get current GDateTime");
233                return;
234            };
235
236            // Set up the timeout if we received the request and it is not accepted yet.
237            if matches!(request.state(), VerificationRequestState::Requested { .. }) {
238                let source_id = glib::timeout_add_local_once(
239                    REQUEST_RECEIVED_TIMEOUT,
240                    clone!(
241                        #[weak(rename_to = imp)]
242                        self,
243                        move || {
244                            imp.received_timeout_source.take();
245
246                            imp.set_state(VerificationState::Dismissed);
247                            imp.obj().dismiss();
248                        }
249                    ),
250                );
251                self.received_timeout_source.replace(Some(source_id));
252            }
253
254            self.received_time
255                .set(datetime)
256                .expect("received time should be uninitialized");
257
258            let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
259            let fut = request.changes().for_each(move |state| {
260                let obj_weak = obj_weak.clone();
261                async move {
262                    let ctx = glib::MainContext::default();
263                    ctx.spawn(async move {
264                        spawn!(async move {
265                            if let Some(obj) = obj_weak.upgrade() {
266                                obj.imp().handle_request_state(state).await;
267                            }
268                        });
269                    });
270                }
271            });
272
273            self.request_changes_abort_handle
274                .replace(Some(spawn_tokio!(fut).abort_handle()));
275
276            let state = request.state();
277            self.handle_request_state(state).await;
278        }
279
280        /// The SDK's verification request.
281        pub(super) fn request(&self) -> &VerificationRequest {
282            self.request.get().expect("request should be initialized")
283        }
284
285        /// Set the user to verify.
286        fn set_user(&self, user: User) {
287            let mut handlers = Vec::new();
288
289            // If the user is a room member, it means it's an in-room verification, we need
290            // to keep track of their name since it's used as the display name.
291            if user.is::<Member>() {
292                let obj = self.obj();
293                let display_name_handler = user.connect_display_name_notify(clone!(
294                    #[weak]
295                    obj,
296                    move |_| {
297                        obj.notify_display_name();
298                    }
299                ));
300                handlers.push(display_name_handler);
301            }
302
303            self.user.set(user, handlers);
304        }
305
306        /// Set the room of the verification, if any.
307        fn set_room(&self, room: Option<&Room>) {
308            let Some(room) = room else {
309                // Nothing to do if there is no room.
310                return;
311            };
312
313            let handler = room.own_member().connect_membership_notify(clone!(
314                #[weak(rename_to = imp)]
315                self,
316                move |own_member| {
317                    if matches!(own_member.membership(), Membership::Leave | Membership::Ban) {
318                        // If the user is not in the room anymore, nothing can be done with this
319                        // verification.
320                        imp.set_state(VerificationState::RoomLeft);
321
322                        if let Some(handler) = imp.membership_handler.take() {
323                            own_member.disconnect(handler);
324                        }
325                    }
326                }
327            ));
328            self.membership_handler.replace(Some(handler));
329
330            self.room.set(Some(room));
331        }
332
333        /// Set the state of this verification.
334        pub(super) fn set_state(&self, state: VerificationState) {
335            if self.state.get() == state {
336                return;
337            }
338            let obj = self.obj();
339
340            if state == VerificationState::Done {
341                let ret = obj.emit_by_name::<bool>("done", &[]);
342                if glib::Propagation::from(ret).is_stop() {
343                    return;
344                }
345            } else if state != VerificationState::QrScan && self.qrcode_scanner.take().is_some() {
346                obj.notify_qrcode_scanner();
347            }
348
349            self.state.set(state);
350
351            obj.notify_state();
352
353            if self.is_finished() {
354                obj.notify_is_finished();
355            }
356        }
357
358        /// Whether this verification is finished.
359        fn is_finished(&self) -> bool {
360            matches!(
361                self.state.get(),
362                VerificationState::Cancelled
363                    | VerificationState::Dismissed
364                    | VerificationState::Done
365                    | VerificationState::Error
366                    | VerificationState::RoomLeft
367            )
368        }
369
370        /// Set the supported methods of this verification.
371        fn set_supported_methods(&self, supported_methods: Vec<VerificationMethod>) {
372            if *self.supported_methods.borrow() == supported_methods {
373                return;
374            }
375
376            self.supported_methods.replace(supported_methods);
377            self.obj().notify_supported_methods();
378        }
379
380        /// The supported methods of this verifications.
381        fn supported_methods(&self) -> VerificationSupportedMethods {
382            self.supported_methods.borrow().as_slice().into()
383        }
384
385        /// The display name of this verification request.
386        fn display_name(&self) -> String {
387            let user = self.user.obj();
388
389            if user.is_own_user() {
390                // TODO: give this request a name based on the device
391                "Login Request".to_string()
392            } else {
393                user.display_name()
394            }
395        }
396
397        /// The flow ID of this verification request.
398        fn flow_id(&self) -> String {
399            self.request().flow_id().to_owned()
400        }
401
402        /// Set whether this verification was viewed by the user.
403        fn set_was_viewed(&self, was_viewed: bool) {
404            if !was_viewed {
405                // The user cannot unview the verification.
406                return;
407            }
408
409            self.was_viewed.set(was_viewed);
410            self.obj().notify_was_viewed();
411        }
412
413        /// Set whether this request was accepted.
414        fn set_was_accepted(&self, was_accepted: bool) {
415            if !was_accepted || self.was_accepted.get() {
416                // The state cannot go backwards.
417                return;
418            }
419
420            self.was_accepted.set(true);
421            self.obj().notify_was_accepted();
422        }
423
424        /// Handle a change in the request's state.
425        async fn handle_request_state(&self, state: VerificationRequestState) {
426            let request = self.request();
427
428            if !matches!(state, VerificationRequestState::Requested { .. }) {
429                if let Some(source) = self.received_timeout_source.take() {
430                    source.remove();
431                }
432            }
433            if !matches!(
434                state,
435                VerificationRequestState::Created { .. }
436                    | VerificationRequestState::Requested { .. }
437            ) {
438                self.set_was_accepted(true);
439            }
440
441            match state {
442                VerificationRequestState::Created { .. } => {}
443                VerificationRequestState::Requested { their_methods, .. } => {
444                    let our_methods = load_supported_verification_methods().await;
445                    let supported_methods = intersect_methods(our_methods, &their_methods);
446
447                    if supported_methods.is_empty() {
448                        self.set_state(VerificationState::NoSupportedMethods);
449                    } else {
450                        self.set_state(VerificationState::Requested);
451                    }
452                }
453                VerificationRequestState::Ready {
454                    their_methods,
455                    our_methods,
456                    ..
457                } => {
458                    let mut supported_methods = intersect_methods(our_methods, &their_methods);
459
460                    // Remove the reciprocate method, it's not a flow in itself.
461                    let reciprocate_idx =
462                        supported_methods.iter().enumerate().find_map(|(idx, m)| {
463                            (*m == VerificationMethod::ReciprocateV1).then_some(idx)
464                        });
465                    if let Some(idx) = reciprocate_idx {
466                        supported_methods.remove(idx);
467                    }
468
469                    // Check that we can get the QR Code, to avoid exposing the method if it doesn't
470                    // work.
471                    let show_qr_idx = supported_methods.iter().enumerate().find_map(|(idx, m)| {
472                        (*m == VerificationMethod::QrCodeShowV1).then_some(idx)
473                    });
474                    if let Some(idx) = show_qr_idx {
475                        if !self.load_qr_code().await {
476                            supported_methods.remove(idx);
477                        }
478                    }
479
480                    if supported_methods.is_empty() {
481                        // This should not happen.
482                        error!("Invalid verification: no methods are supported by both sessions, cancelling…");
483                        if self.obj().cancel().await.is_err() {
484                            self.set_state(VerificationState::NoSupportedMethods);
485                        }
486                    } else {
487                        self.set_supported_methods(supported_methods.clone());
488
489                        if supported_methods.len() == 1
490                            && !request.we_started()
491                            && supported_methods[0] == VerificationMethod::SasV1
492                        {
493                            // We only go forward for SAS, because QrCodeShow is the
494                            // same screen as the one to choose a method and we need
495                            // to tell the user we are going to need to access the
496                            // camera for QrCodeScan.
497                            if self.obj().start_sas().await.is_ok() {
498                                return;
499                            }
500                        }
501
502                        self.set_state(VerificationState::Ready);
503                    }
504                }
505                VerificationRequestState::Transitioned { verification } => {
506                    self.set_verification(verification).await;
507                }
508                VerificationRequestState::Done => {
509                    self.set_state(VerificationState::Done);
510                }
511                VerificationRequestState::Cancelled(info) => self.handle_cancelled_state(&info),
512            }
513        }
514
515        /// Handle when the request was cancelled.
516        fn handle_cancelled_state(&self, cancel_info: &CancelInfo) {
517            debug!("Verification was cancelled: {cancel_info:?}");
518            let cancel_code = cancel_info.cancel_code();
519
520            if cancel_info.cancelled_by_us() && *cancel_code == CancelCode::User {
521                // We should handle this already.
522                return;
523            }
524
525            if *cancel_code == CancelCode::Accepted && !self.was_viewed.get() {
526                // We can safely remove it.
527                self.obj().dismiss();
528                return;
529            }
530
531            self.obj().emit_by_name::<()>("cancel-info-changed", &[]);
532            self.set_state(VerificationState::Cancelled);
533        }
534
535        /// Set the SDK's Verification.
536        async fn set_verification(&self, verification: Verification) {
537            if let Some(handle) = self.verification_changes_abort_handle.take() {
538                handle.abort();
539            }
540
541            let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
542            let handle = match &verification {
543                Verification::SasV1(sas_verification) => {
544                    let fut = sas_verification.changes().for_each(move |state| {
545                        let obj_weak = obj_weak.clone();
546                        async move {
547                            let ctx = glib::MainContext::default();
548                            ctx.spawn(async move {
549                                spawn!(async move {
550                                    if let Some(obj) = obj_weak.upgrade() {
551                                        obj.imp().handle_sas_verification_state(state).await;
552                                    }
553                                });
554                            });
555                        }
556                    });
557                    spawn_tokio!(fut).abort_handle()
558                }
559                Verification::QrV1(qr_verification) => {
560                    let fut = qr_verification.changes().for_each(move |state| {
561                        let obj_weak = obj_weak.clone();
562                        async move {
563                            let ctx = glib::MainContext::default();
564                            ctx.spawn(async move {
565                                spawn!(async move {
566                                    if let Some(obj) = obj_weak.upgrade() {
567                                        obj.imp().handle_qr_verification_state(state);
568                                    }
569                                });
570                            });
571                        }
572                    });
573                    spawn_tokio!(fut).abort_handle()
574                }
575                _ => {
576                    error!("We only support SAS and QR verification");
577                    self.set_state(VerificationState::Error);
578                    return;
579                }
580            };
581
582            self.verification.replace(Some(verification.clone()));
583            self.verification_changes_abort_handle.replace(Some(handle));
584
585            match verification {
586                Verification::SasV1(sas_verification) => {
587                    self.handle_sas_verification_state(sas_verification.state())
588                        .await;
589                }
590                Verification::QrV1(qr_verification) => {
591                    self.handle_qr_verification_state(qr_verification.state());
592                }
593                _ => unreachable!(),
594            }
595        }
596
597        /// Handle a change in the QR verification's state.
598        fn handle_qr_verification_state(&self, state: QrVerificationState) {
599            match state {
600                QrVerificationState::Started
601                | QrVerificationState::Confirmed
602                | QrVerificationState::Reciprocated => {}
603                QrVerificationState::Scanned => self.set_state(VerificationState::QrConfirm),
604                QrVerificationState::Done { .. } => self.set_state(VerificationState::Done),
605                QrVerificationState::Cancelled(info) => self.handle_cancelled_state(&info),
606            }
607        }
608
609        /// The SDK's QR verification, if one was started.
610        pub(super) fn qr_verification(&self) -> Option<QrVerification> {
611            match self.verification.borrow().as_ref()? {
612                Verification::QrV1(v) => Some(v.clone()),
613                _ => None,
614            }
615        }
616
617        /// Handle a change in the SAS verification's state.
618        async fn handle_sas_verification_state(&self, state: SasState) {
619            let Some(sas_verification) = self.sas_verification() else {
620                return;
621            };
622
623            match state {
624                SasState::Created { .. } | SasState::Accepted { .. } | SasState::Confirmed => {}
625                SasState::Started { .. } => {
626                    let handle = spawn_tokio!(async move { sas_verification.accept().await });
627                    if let Err(error) = handle.await.expect("task was not aborted") {
628                        error!("Could not accept SAS verification: {error}");
629                        self.set_state(VerificationState::Error);
630                    }
631                }
632                SasState::KeysExchanged { .. } => {
633                    self.obj().emit_by_name::<()>("sas-data-changed", &[]);
634                    self.set_state(VerificationState::SasConfirm);
635                }
636                SasState::Done { .. } => self.set_state(VerificationState::Done),
637                SasState::Cancelled(info) => self.handle_cancelled_state(&info),
638            }
639        }
640
641        /// The SDK's SAS verification, if one was started.
642        pub(super) fn sas_verification(&self) -> Option<SasVerification> {
643            match self.verification.borrow().as_ref()? {
644                Verification::SasV1(v) => Some(v.clone()),
645                _ => None,
646            }
647        }
648
649        /// Try to load the QR Code.
650        ///
651        /// Return `true` if it was successfully loaded, `false` otherwise.
652        async fn load_qr_code(&self) -> bool {
653            let request = self.request().clone();
654
655            let handle = spawn_tokio!(async move { request.generate_qr_code().await });
656
657            let qr_verification = match handle.await.expect("task was not aborted") {
658                Ok(Some(qr_verification)) => qr_verification,
659                Ok(None) => {
660                    error!("Could not start QR verification generation: unknown reason");
661                    return false;
662                }
663                Err(error) => {
664                    error!("Could not start QR verification generation: {error}");
665                    return false;
666                }
667            };
668
669            match qr_verification.to_qr_code() {
670                Ok(qr_code) => {
671                    self.qr_code.replace(Some(qr_code));
672                    true
673                }
674                Err(error) => {
675                    error!("Could not generate verification QR code: {error}");
676                    false
677                }
678            }
679        }
680    }
681}
682
683glib::wrapper! {
684    /// An identity verification request.
685    pub struct IdentityVerification(ObjectSubclass<imp::IdentityVerification>);
686}
687
688impl IdentityVerification {
689    /// Construct a verification for the given request.
690    pub async fn new(request: VerificationRequest, user: &User, room: Option<&Room>) -> Self {
691        let obj = glib::Object::builder::<Self>()
692            .property("user", user)
693            .property("room", room)
694            .build();
695        obj.imp().set_request(request).await;
696        obj
697    }
698
699    /// The unique identifying key of this verification.
700    pub(crate) fn key(&self) -> VerificationKey {
701        VerificationKey::from_request(self.imp().request())
702    }
703
704    /// Whether this is a self-verification.
705    pub(crate) fn is_self_verification(&self) -> bool {
706        self.imp().request().is_self_verification()
707    }
708
709    /// Whether we started this verification.
710    pub(crate) fn started_by_us(&self) -> bool {
711        self.imp().request().we_started()
712    }
713
714    /// The ID of the other device that is being verified.
715    pub(crate) fn other_device_id(&self) -> Option<OwnedDeviceId> {
716        let request_state = self.imp().request().state();
717        let other_device_data = match &request_state {
718            VerificationRequestState::Requested {
719                other_device_data, ..
720            }
721            | VerificationRequestState::Ready {
722                other_device_data, ..
723            } => other_device_data,
724            VerificationRequestState::Transitioned { verification } => match verification {
725                Verification::SasV1(sas) => sas.other_device(),
726                Verification::QrV1(qr) => qr.other_device(),
727                _ => None?,
728            },
729            VerificationRequestState::Created { .. }
730            | VerificationRequestState::Done
731            | VerificationRequestState::Cancelled(_) => None?,
732        };
733
734        Some(other_device_data.device_id().to_owned())
735    }
736
737    /// Information about the verification cancellation, if any.
738    pub(crate) fn cancel_info(&self) -> Option<CancelInfo> {
739        self.imp().request().cancel_info()
740    }
741
742    /// Cancel the verification request.
743    ///
744    /// This can be used to decline the request or cancel it at any time.
745    pub(crate) async fn cancel(&self) -> Result<(), matrix_sdk::Error> {
746        let request = self.imp().request().clone();
747
748        if request.is_done() || request.is_passive() || request.is_cancelled() {
749            return Err(matrix_sdk::Error::UnknownError(
750                "Cannot cancel request that is already finished".into(),
751            ));
752        }
753
754        let handle = spawn_tokio!(async move { request.cancel().await });
755
756        match handle.await.expect("task was not aborted") {
757            Ok(()) => {
758                self.dismiss();
759                Ok(())
760            }
761            Err(error) => {
762                error!("Could not cancel verification request: {error}");
763                Err(error)
764            }
765        }
766    }
767
768    /// Accept the verification request.
769    pub(crate) async fn accept(&self) -> Result<(), ()> {
770        let request = self.imp().request().clone();
771
772        let VerificationRequestState::Requested { their_methods, .. } = request.state() else {
773            error!("Cannot accept verification that is not in the requested state");
774            return Err(());
775        };
776        let our_methods = load_supported_verification_methods().await;
777        let methods = intersect_methods(our_methods, &their_methods);
778
779        let handle = spawn_tokio!(async move { request.accept_with_methods(methods).await });
780
781        match handle.await.expect("task was not aborted") {
782            Ok(()) => Ok(()),
783            Err(error) => {
784                error!("Could not accept verification request: {error}");
785                Err(())
786            }
787        }
788    }
789
790    /// Go back to the state to choose a verification method.
791    pub(crate) fn choose_method(&self) {
792        self.imp().set_state(VerificationState::Ready);
793    }
794
795    /// Whether the current SAS verification supports emoji.
796    pub(crate) fn sas_supports_emoji(&self) -> bool {
797        self.imp()
798            .sas_verification()
799            .is_some_and(|v| v.supports_emoji())
800    }
801
802    /// The list of emojis for the current SAS verification, if any.
803    pub(crate) fn sas_emoji(&self) -> Option<[Emoji; 7]> {
804        self.imp().sas_verification()?.emoji()
805    }
806
807    /// The list of decimals for the current SAS verification, if any.
808    pub(crate) fn sas_decimals(&self) -> Option<(u16, u16, u16)> {
809        self.imp().sas_verification()?.decimals()
810    }
811
812    /// The QR Code, if the `QrCodeShowV1` method is supported.
813    pub(crate) fn qr_code(&self) -> Option<QrCode> {
814        self.imp().qr_code.borrow().clone()
815    }
816
817    /// Whether we have the QR code.
818    pub(crate) fn has_qr_code(&self) -> bool {
819        self.imp().qr_code.borrow().is_some()
820    }
821
822    /// Start a QR Code scan.
823    pub(crate) async fn start_qr_code_scan(&self) -> Result<(), ()> {
824        let imp = self.imp();
825
826        match QrCodeScanner::new().await {
827            Some(qrcode_scanner) => {
828                imp.qrcode_scanner.replace(Some(qrcode_scanner));
829                self.notify_qrcode_scanner();
830
831                imp.set_state(VerificationState::QrScan);
832
833                Ok(())
834            }
835            None => Err(()),
836        }
837    }
838
839    /// The QR Code was scanned.
840    pub(crate) async fn qr_code_scanned(&self, data: QrVerificationData) -> Result<(), ()> {
841        let imp = self.imp();
842        imp.set_state(VerificationState::QrScanned);
843        let request = imp.request().clone();
844
845        let handle = spawn_tokio!(async move { request.scan_qr_code(data).await });
846
847        match handle.await.expect("task was not aborted") {
848            Ok(Some(_)) => Ok(()),
849            Ok(None) => {
850                error!("Could not validate scanned verification QR code: unknown reason");
851                Err(())
852            }
853            Err(error) => {
854                error!("Could not validate scanned verification QR code: {error}");
855                Err(())
856            }
857        }
858    }
859
860    /// Confirm that the QR Code was scanned by the other party.
861    pub(crate) async fn confirm_qr_code_scanned(&self) -> Result<(), ()> {
862        let Some(qr_verification) = self.imp().qr_verification() else {
863            error!("Cannot confirm QR Code scan without an ongoing QR verification");
864            return Err(());
865        };
866
867        let handle = spawn_tokio!(async move { qr_verification.confirm().await });
868
869        match handle.await.expect("task was not aborted") {
870            Ok(()) => Ok(()),
871            Err(error) => {
872                error!("Could not confirm scanned verification QR code: {error}");
873                Err(())
874            }
875        }
876    }
877
878    /// Start a SAS verification.
879    pub(crate) async fn start_sas(&self) -> Result<(), ()> {
880        let request = self.imp().request().clone();
881        let handle = spawn_tokio!(async move { request.start_sas().await });
882
883        match handle.await.expect("task was not aborted") {
884            Ok(Some(_)) => Ok(()),
885            Ok(None) => {
886                error!("Could not start SAS verification: unknown reason");
887                Err(())
888            }
889            Err(error) => {
890                error!("Could not start SAS verification: {error}");
891                Err(())
892            }
893        }
894    }
895
896    /// The SAS data does not match.
897    pub(crate) async fn sas_mismatch(&self) -> Result<(), ()> {
898        let Some(sas_verification) = self.imp().sas_verification() else {
899            error!("Cannot send SAS mismatch without an ongoing SAS verification");
900            return Err(());
901        };
902
903        let handle = spawn_tokio!(async move { sas_verification.mismatch().await });
904
905        match handle.await.expect("task was not aborted") {
906            Ok(()) => Ok(()),
907            Err(error) => {
908                error!("Could not send SAS verification mismatch: {error}");
909                Err(())
910            }
911        }
912    }
913
914    /// The SAS data matches.
915    pub(crate) async fn sas_match(&self) -> Result<(), ()> {
916        let Some(sas_verification) = self.imp().sas_verification() else {
917            error!("Cannot send SAS match without an ongoing SAS verification");
918            return Err(());
919        };
920
921        let handle = spawn_tokio!(async move { sas_verification.confirm().await });
922
923        match handle.await.expect("task was not aborted") {
924            Ok(()) => Ok(()),
925            Err(error) => {
926                error!("Could not send SAS verification match: {error}");
927                Err(())
928            }
929        }
930    }
931
932    /// Restart this verification with a new one to the same user.
933    pub(crate) async fn restart(&self) -> Result<Self, ()> {
934        let user = self.user();
935        let verification_list = user.session().verification_list();
936
937        let new_user = (!self.is_self_verification()).then_some(user);
938        let new_verification = verification_list.create(new_user).await?;
939
940        self.emit_by_name::<()>("replaced", &[&new_verification]);
941
942        // If we restart because an unexpected error happened, try to cancel it.
943        if self.cancel().await.is_err() {
944            self.dismiss();
945        }
946
947        Ok(new_verification)
948    }
949
950    /// The verification can be dismissed.
951    ///
952    /// Also removes it from the verification list.
953    pub(crate) fn dismiss(&self) {
954        self.remove_from_list();
955        self.emit_by_name::<()>("dismiss", &[]);
956    }
957
958    /// The verification can be removed from the verification list.
959    ///
960    /// You will usually want to use [`IdentityVerification::dismiss()`] because
961    /// the interface listens for the signal it emits, and it calls this method
962    /// internally.
963    pub(crate) fn remove_from_list(&self) {
964        self.emit_by_name::<()>("remove-from-list", &[]);
965    }
966
967    /// Connect to the signal emitted when the SAS data changed.
968    pub fn connect_sas_data_changed<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
969        self.connect_closure(
970            "sas-data-changed",
971            true,
972            closure_local!(move |obj: Self| {
973                f(&obj);
974            }),
975        )
976    }
977
978    /// Connect to the signal emitted when the cancel info changed.
979    pub fn connect_cancel_info_changed<F: Fn(&Self) + 'static>(
980        &self,
981        f: F,
982    ) -> glib::SignalHandlerId {
983        self.connect_closure(
984            "cancel-info-changed",
985            true,
986            closure_local!(move |obj: Self| {
987                f(&obj);
988            }),
989        )
990    }
991
992    /// Connect to the signal emitted when the verification has been replaced.
993    ///
994    /// The second parameter to the function is the new verification.
995    pub fn connect_replaced<F: Fn(&Self, &Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
996        self.connect_closure(
997            "replaced",
998            true,
999            closure_local!(move |obj: Self, new_verification: Self| {
1000                f(&obj, &new_verification);
1001            }),
1002        )
1003    }
1004
1005    /// Connect to the signal emitted when the verification is done, but its
1006    /// state does not reflect that yet.
1007    ///
1008    /// Return `glib::Propagation::Stop` in the signal handler to prevent the
1009    /// state from changing to `VerificationState::Done`. Can be used to replace
1010    /// the last screen of `IdentityVerificationView`.
1011    pub fn connect_done<F: Fn(&Self) -> glib::Propagation + 'static>(
1012        &self,
1013        f: F,
1014    ) -> glib::SignalHandlerId {
1015        self.connect_closure(
1016            "done",
1017            true,
1018            closure_local!(move |obj: Self| {
1019                let ret = f(&obj);
1020
1021                if ret.is_stop() {
1022                    obj.stop_signal_emission_by_name("done");
1023                }
1024
1025                bool::from(ret)
1026            }),
1027        )
1028    }
1029
1030    /// Connect to the signal emitted when the verification can be dismissed.
1031    pub fn connect_dismiss<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
1032        self.connect_closure(
1033            "dismiss",
1034            true,
1035            closure_local!(move |obj: Self| {
1036                f(&obj);
1037            }),
1038        )
1039    }
1040
1041    /// Connect to the signal emitted when the verification can be removed from
1042    /// the verification list.
1043    pub(super) fn connect_remove_from_list<F: Fn(&Self) + 'static>(
1044        &self,
1045        f: F,
1046    ) -> glib::SignalHandlerId {
1047        self.connect_closure(
1048            "remove-from-list",
1049            true,
1050            closure_local!(move |obj: Self| {
1051                f(&obj);
1052            }),
1053        )
1054    }
1055}
1056
1057/// Get the intersection or our methods and their methods.
1058fn intersect_methods(
1059    our_methods: Vec<VerificationMethod>,
1060    their_methods: &[VerificationMethod],
1061) -> Vec<VerificationMethod> {
1062    let mut supported_methods = our_methods;
1063
1064    supported_methods.retain(|m| match m {
1065        VerificationMethod::SasV1 => their_methods.contains(&VerificationMethod::SasV1),
1066        VerificationMethod::QrCodeScanV1 => {
1067            their_methods.contains(&VerificationMethod::QrCodeShowV1)
1068                && their_methods.contains(&VerificationMethod::ReciprocateV1)
1069        }
1070        VerificationMethod::QrCodeShowV1 => {
1071            their_methods.contains(&VerificationMethod::QrCodeScanV1)
1072                && their_methods.contains(&VerificationMethod::ReciprocateV1)
1073        }
1074        VerificationMethod::ReciprocateV1 => {
1075            (their_methods.contains(&VerificationMethod::QrCodeShowV1)
1076                || their_methods.contains(&VerificationMethod::QrCodeScanV1))
1077                && their_methods.contains(&VerificationMethod::ReciprocateV1)
1078        }
1079        _ => false,
1080    });
1081
1082    supported_methods
1083}