Skip to main content

fractal/session/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    OwnedDeviceId,
15    events::key::verification::{REQUEST_RECEIVED_TIMEOUT, VerificationMethod, cancel::CancelCode},
16};
17use tracing::{debug, error};
18
19use super::{VerificationKey, load_supported_verification_methods};
20use crate::{
21    components::QrCodeScanner,
22    prelude::*,
23    session::{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                && let Some(room) = self.room.upgrade()
202            {
203                room.own_member().disconnect(handler);
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                && let Some(source) = self.received_timeout_source.take()
430            {
431                source.remove();
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                        && !self.load_qr_code().await
476                    {
477                        supported_methods.remove(idx);
478                    }
479
480                    if supported_methods.is_empty() {
481                        // This should not happen.
482                        error!(
483                            "Invalid verification: no methods are supported by both sessions, cancelling…"
484                        );
485                        if self.obj().cancel().await.is_err() {
486                            self.set_state(VerificationState::NoSupportedMethods);
487                        }
488                    } else {
489                        self.set_supported_methods(supported_methods.clone());
490
491                        if supported_methods.len() == 1
492                            && !request.we_started()
493                            && supported_methods[0] == VerificationMethod::SasV1
494                        {
495                            // We only go forward for SAS, because QrCodeShow is the
496                            // same screen as the one to choose a method and we need
497                            // to tell the user we are going to need to access the
498                            // camera for QrCodeScan.
499                            if self.obj().start_sas().await.is_ok() {
500                                return;
501                            }
502                        }
503
504                        self.set_state(VerificationState::Ready);
505                    }
506                }
507                VerificationRequestState::Transitioned { verification } => {
508                    self.set_verification(verification).await;
509                }
510                VerificationRequestState::Done => {
511                    self.set_state(VerificationState::Done);
512                }
513                VerificationRequestState::Cancelled(info) => self.handle_cancelled_state(&info),
514            }
515        }
516
517        /// Handle when the request was cancelled.
518        fn handle_cancelled_state(&self, cancel_info: &CancelInfo) {
519            debug!("Verification was cancelled: {cancel_info:?}");
520            let cancel_code = cancel_info.cancel_code();
521
522            if cancel_info.cancelled_by_us() && *cancel_code == CancelCode::User {
523                // We should handle this already.
524                return;
525            }
526
527            if *cancel_code == CancelCode::Accepted && !self.was_viewed.get() {
528                // We can safely remove it.
529                self.obj().dismiss();
530                return;
531            }
532
533            self.obj().emit_by_name::<()>("cancel-info-changed", &[]);
534            self.set_state(VerificationState::Cancelled);
535        }
536
537        /// Set the SDK's Verification.
538        async fn set_verification(&self, verification: Verification) {
539            if let Some(handle) = self.verification_changes_abort_handle.take() {
540                handle.abort();
541            }
542
543            let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
544            let handle = match &verification {
545                Verification::SasV1(sas_verification) => {
546                    let fut = sas_verification.changes().for_each(move |state| {
547                        let obj_weak = obj_weak.clone();
548                        async move {
549                            let ctx = glib::MainContext::default();
550                            ctx.spawn(async move {
551                                spawn!(async move {
552                                    if let Some(obj) = obj_weak.upgrade() {
553                                        obj.imp().handle_sas_verification_state(state).await;
554                                    }
555                                });
556                            });
557                        }
558                    });
559                    spawn_tokio!(fut).abort_handle()
560                }
561                Verification::QrV1(qr_verification) => {
562                    let fut = qr_verification.changes().for_each(move |state| {
563                        let obj_weak = obj_weak.clone();
564                        async move {
565                            let ctx = glib::MainContext::default();
566                            ctx.spawn(async move {
567                                spawn!(async move {
568                                    if let Some(obj) = obj_weak.upgrade() {
569                                        obj.imp().handle_qr_verification_state(state);
570                                    }
571                                });
572                            });
573                        }
574                    });
575                    spawn_tokio!(fut).abort_handle()
576                }
577                _ => {
578                    error!("We only support SAS and QR verification");
579                    self.set_state(VerificationState::Error);
580                    return;
581                }
582            };
583
584            self.verification.replace(Some(verification.clone()));
585            self.verification_changes_abort_handle.replace(Some(handle));
586
587            match verification {
588                Verification::SasV1(sas_verification) => {
589                    self.handle_sas_verification_state(sas_verification.state())
590                        .await;
591                }
592                Verification::QrV1(qr_verification) => {
593                    self.handle_qr_verification_state(qr_verification.state());
594                }
595                _ => unreachable!(),
596            }
597        }
598
599        /// Handle a change in the QR verification's state.
600        fn handle_qr_verification_state(&self, state: QrVerificationState) {
601            match state {
602                QrVerificationState::Started
603                | QrVerificationState::Confirmed
604                | QrVerificationState::Reciprocated => {}
605                QrVerificationState::Scanned => self.set_state(VerificationState::QrConfirm),
606                QrVerificationState::Done { .. } => self.set_state(VerificationState::Done),
607                QrVerificationState::Cancelled(info) => self.handle_cancelled_state(&info),
608            }
609        }
610
611        /// The SDK's QR verification, if one was started.
612        pub(super) fn qr_verification(&self) -> Option<QrVerification> {
613            match self.verification.borrow().as_ref()? {
614                Verification::QrV1(v) => Some(v.clone()),
615                _ => None,
616            }
617        }
618
619        /// Handle a change in the SAS verification's state.
620        async fn handle_sas_verification_state(&self, state: SasState) {
621            let Some(sas_verification) = self.sas_verification() else {
622                return;
623            };
624
625            match state {
626                SasState::Created { .. } | SasState::Accepted { .. } | SasState::Confirmed => {}
627                SasState::Started { .. } => {
628                    let handle = spawn_tokio!(async move { sas_verification.accept().await });
629                    if let Err(error) = handle.await.expect("task was not aborted") {
630                        error!("Could not accept SAS verification: {error}");
631                        self.set_state(VerificationState::Error);
632                    }
633                }
634                SasState::KeysExchanged { .. } => {
635                    self.obj().emit_by_name::<()>("sas-data-changed", &[]);
636                    self.set_state(VerificationState::SasConfirm);
637                }
638                SasState::Done { .. } => self.set_state(VerificationState::Done),
639                SasState::Cancelled(info) => self.handle_cancelled_state(&info),
640            }
641        }
642
643        /// The SDK's SAS verification, if one was started.
644        pub(super) fn sas_verification(&self) -> Option<SasVerification> {
645            match self.verification.borrow().as_ref()? {
646                Verification::SasV1(v) => Some(v.clone()),
647                _ => None,
648            }
649        }
650
651        /// Try to load the QR Code.
652        ///
653        /// Return `true` if it was successfully loaded, `false` otherwise.
654        async fn load_qr_code(&self) -> bool {
655            let request = self.request().clone();
656
657            let handle = spawn_tokio!(async move { request.generate_qr_code().await });
658
659            let qr_verification = match handle.await.expect("task was not aborted") {
660                Ok(Some(qr_verification)) => qr_verification,
661                Ok(None) => {
662                    error!("Could not start QR verification generation: unknown reason");
663                    return false;
664                }
665                Err(error) => {
666                    error!("Could not start QR verification generation: {error}");
667                    return false;
668                }
669            };
670
671            match qr_verification.to_qr_code() {
672                Ok(qr_code) => {
673                    self.qr_code.replace(Some(qr_code));
674                    true
675                }
676                Err(error) => {
677                    error!("Could not generate verification QR code: {error}");
678                    false
679                }
680            }
681        }
682    }
683}
684
685glib::wrapper! {
686    /// An identity verification request.
687    pub struct IdentityVerification(ObjectSubclass<imp::IdentityVerification>);
688}
689
690impl IdentityVerification {
691    /// Construct a verification for the given request.
692    pub async fn new(request: VerificationRequest, user: &User, room: Option<&Room>) -> Self {
693        let obj = glib::Object::builder::<Self>()
694            .property("user", user)
695            .property("room", room)
696            .build();
697        obj.imp().set_request(request).await;
698        obj
699    }
700
701    /// The unique identifying key of this verification.
702    pub(crate) fn key(&self) -> VerificationKey {
703        VerificationKey::from_request(self.imp().request())
704    }
705
706    /// Whether this is a self-verification.
707    pub(crate) fn is_self_verification(&self) -> bool {
708        self.imp().request().is_self_verification()
709    }
710
711    /// Whether we started this verification.
712    pub(crate) fn started_by_us(&self) -> bool {
713        self.imp().request().we_started()
714    }
715
716    /// The ID of the other device that is being verified.
717    pub(crate) fn other_device_id(&self) -> Option<OwnedDeviceId> {
718        let request_state = self.imp().request().state();
719        let other_device_data = match &request_state {
720            VerificationRequestState::Requested {
721                other_device_data, ..
722            }
723            | VerificationRequestState::Ready {
724                other_device_data, ..
725            } => other_device_data,
726            VerificationRequestState::Transitioned { verification } => match verification {
727                Verification::SasV1(sas) => sas.other_device(),
728                Verification::QrV1(qr) => qr.other_device(),
729                _ => None?,
730            },
731            VerificationRequestState::Created { .. }
732            | VerificationRequestState::Done
733            | VerificationRequestState::Cancelled(_) => None?,
734        };
735
736        Some(other_device_data.device_id().to_owned())
737    }
738
739    /// Information about the verification cancellation, if any.
740    pub(crate) fn cancel_info(&self) -> Option<CancelInfo> {
741        self.imp().request().cancel_info()
742    }
743
744    /// Cancel the verification request.
745    ///
746    /// This can be used to decline the request or cancel it at any time.
747    pub(crate) async fn cancel(&self) -> Result<(), matrix_sdk::Error> {
748        let request = self.imp().request().clone();
749
750        if request.is_done() || request.is_passive() || request.is_cancelled() {
751            return Err(matrix_sdk::Error::UnknownError(
752                "Cannot cancel request that is already finished".into(),
753            ));
754        }
755
756        let handle = spawn_tokio!(async move { request.cancel().await });
757
758        match handle.await.expect("task was not aborted") {
759            Ok(()) => {
760                self.dismiss();
761                Ok(())
762            }
763            Err(error) => {
764                error!("Could not cancel verification request: {error}");
765                Err(error)
766            }
767        }
768    }
769
770    /// Accept the verification request.
771    pub(crate) async fn accept(&self) -> Result<(), ()> {
772        let request = self.imp().request().clone();
773
774        let VerificationRequestState::Requested { their_methods, .. } = request.state() else {
775            error!("Cannot accept verification that is not in the requested state");
776            return Err(());
777        };
778        let our_methods = load_supported_verification_methods().await;
779        let methods = intersect_methods(our_methods, &their_methods);
780
781        let handle = spawn_tokio!(async move { request.accept_with_methods(methods).await });
782
783        match handle.await.expect("task was not aborted") {
784            Ok(()) => Ok(()),
785            Err(error) => {
786                error!("Could not accept verification request: {error}");
787                Err(())
788            }
789        }
790    }
791
792    /// Go back to the state to choose a verification method.
793    pub(crate) fn choose_method(&self) {
794        self.imp().set_state(VerificationState::Ready);
795    }
796
797    /// Whether the current SAS verification supports emoji.
798    pub(crate) fn sas_supports_emoji(&self) -> bool {
799        self.imp()
800            .sas_verification()
801            .is_some_and(|v| v.supports_emoji())
802    }
803
804    /// The list of emojis for the current SAS verification, if any.
805    pub(crate) fn sas_emoji(&self) -> Option<[Emoji; 7]> {
806        self.imp().sas_verification()?.emoji()
807    }
808
809    /// The list of decimals for the current SAS verification, if any.
810    pub(crate) fn sas_decimals(&self) -> Option<(u16, u16, u16)> {
811        self.imp().sas_verification()?.decimals()
812    }
813
814    /// The QR Code, if the `QrCodeShowV1` method is supported.
815    pub(crate) fn qr_code(&self) -> Option<QrCode> {
816        self.imp().qr_code.borrow().clone()
817    }
818
819    /// Whether we have the QR code.
820    pub(crate) fn has_qr_code(&self) -> bool {
821        self.imp().qr_code.borrow().is_some()
822    }
823
824    /// Start a QR Code scan.
825    pub(crate) async fn start_qr_code_scan(&self) -> Result<(), ()> {
826        let imp = self.imp();
827
828        match QrCodeScanner::new().await {
829            Some(qrcode_scanner) => {
830                imp.qrcode_scanner.replace(Some(qrcode_scanner));
831                self.notify_qrcode_scanner();
832
833                imp.set_state(VerificationState::QrScan);
834
835                Ok(())
836            }
837            None => Err(()),
838        }
839    }
840
841    /// The QR Code was scanned.
842    pub(crate) async fn qr_code_scanned(&self, data: QrVerificationData) -> Result<(), ()> {
843        let imp = self.imp();
844        imp.set_state(VerificationState::QrScanned);
845        let request = imp.request().clone();
846
847        let handle = spawn_tokio!(async move { request.scan_qr_code(data).await });
848
849        match handle.await.expect("task was not aborted") {
850            Ok(Some(_)) => Ok(()),
851            Ok(None) => {
852                error!("Could not validate scanned verification QR code: unknown reason");
853                Err(())
854            }
855            Err(error) => {
856                error!("Could not validate scanned verification QR code: {error}");
857                Err(())
858            }
859        }
860    }
861
862    /// Confirm that the QR Code was scanned by the other party.
863    pub(crate) async fn confirm_qr_code_scanned(&self) -> Result<(), ()> {
864        let Some(qr_verification) = self.imp().qr_verification() else {
865            error!("Cannot confirm QR Code scan without an ongoing QR verification");
866            return Err(());
867        };
868
869        let handle = spawn_tokio!(async move { qr_verification.confirm().await });
870
871        match handle.await.expect("task was not aborted") {
872            Ok(()) => Ok(()),
873            Err(error) => {
874                error!("Could not confirm scanned verification QR code: {error}");
875                Err(())
876            }
877        }
878    }
879
880    /// Start a SAS verification.
881    pub(crate) async fn start_sas(&self) -> Result<(), ()> {
882        let request = self.imp().request().clone();
883        let handle = spawn_tokio!(async move { request.start_sas().await });
884
885        match handle.await.expect("task was not aborted") {
886            Ok(Some(_)) => Ok(()),
887            Ok(None) => {
888                error!("Could not start SAS verification: unknown reason");
889                Err(())
890            }
891            Err(error) => {
892                error!("Could not start SAS verification: {error}");
893                Err(())
894            }
895        }
896    }
897
898    /// The SAS data does not match.
899    pub(crate) async fn sas_mismatch(&self) -> Result<(), ()> {
900        let Some(sas_verification) = self.imp().sas_verification() else {
901            error!("Cannot send SAS mismatch without an ongoing SAS verification");
902            return Err(());
903        };
904
905        let handle = spawn_tokio!(async move { sas_verification.mismatch().await });
906
907        match handle.await.expect("task was not aborted") {
908            Ok(()) => Ok(()),
909            Err(error) => {
910                error!("Could not send SAS verification mismatch: {error}");
911                Err(())
912            }
913        }
914    }
915
916    /// The SAS data matches.
917    pub(crate) async fn sas_match(&self) -> Result<(), ()> {
918        let Some(sas_verification) = self.imp().sas_verification() else {
919            error!("Cannot send SAS match without an ongoing SAS verification");
920            return Err(());
921        };
922
923        let handle = spawn_tokio!(async move { sas_verification.confirm().await });
924
925        match handle.await.expect("task was not aborted") {
926            Ok(()) => Ok(()),
927            Err(error) => {
928                error!("Could not send SAS verification match: {error}");
929                Err(())
930            }
931        }
932    }
933
934    /// Restart this verification with a new one to the same user.
935    pub(crate) async fn restart(&self) -> Result<Self, ()> {
936        let user = self.user();
937        let verification_list = user.session().verification_list();
938
939        let new_user = (!self.is_self_verification()).then_some(user);
940        let new_verification = verification_list.create(new_user).await?;
941
942        self.emit_by_name::<()>("replaced", &[&new_verification]);
943
944        // If we restart because an unexpected error happened, try to cancel it.
945        if self.cancel().await.is_err() {
946            self.dismiss();
947        }
948
949        Ok(new_verification)
950    }
951
952    /// The verification can be dismissed.
953    ///
954    /// Also removes it from the verification list.
955    pub(crate) fn dismiss(&self) {
956        self.remove_from_list();
957        self.emit_by_name::<()>("dismiss", &[]);
958    }
959
960    /// The verification can be removed from the verification list.
961    ///
962    /// You will usually want to use [`IdentityVerification::dismiss()`] because
963    /// the interface listens for the signal it emits, and it calls this method
964    /// internally.
965    pub(crate) fn remove_from_list(&self) {
966        self.emit_by_name::<()>("remove-from-list", &[]);
967    }
968
969    /// Connect to the signal emitted when the SAS data changed.
970    pub fn connect_sas_data_changed<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
971        self.connect_closure(
972            "sas-data-changed",
973            true,
974            closure_local!(move |obj: Self| {
975                f(&obj);
976            }),
977        )
978    }
979
980    /// Connect to the signal emitted when the cancel info changed.
981    pub fn connect_cancel_info_changed<F: Fn(&Self) + 'static>(
982        &self,
983        f: F,
984    ) -> glib::SignalHandlerId {
985        self.connect_closure(
986            "cancel-info-changed",
987            true,
988            closure_local!(move |obj: Self| {
989                f(&obj);
990            }),
991        )
992    }
993
994    /// Connect to the signal emitted when the verification has been replaced.
995    ///
996    /// The second parameter to the function is the new verification.
997    pub fn connect_replaced<F: Fn(&Self, &Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
998        self.connect_closure(
999            "replaced",
1000            true,
1001            closure_local!(move |obj: Self, new_verification: Self| {
1002                f(&obj, &new_verification);
1003            }),
1004        )
1005    }
1006
1007    /// Connect to the signal emitted when the verification is done, but its
1008    /// state does not reflect that yet.
1009    ///
1010    /// Return `glib::Propagation::Stop` in the signal handler to prevent the
1011    /// state from changing to `VerificationState::Done`. Can be used to replace
1012    /// the last screen of `IdentityVerificationView`.
1013    pub fn connect_done<F: Fn(&Self) -> glib::Propagation + 'static>(
1014        &self,
1015        f: F,
1016    ) -> glib::SignalHandlerId {
1017        self.connect_closure(
1018            "done",
1019            true,
1020            closure_local!(move |obj: Self| {
1021                let ret = f(&obj);
1022
1023                if ret.is_stop() {
1024                    obj.stop_signal_emission_by_name("done");
1025                }
1026
1027                bool::from(ret)
1028            }),
1029        )
1030    }
1031
1032    /// Connect to the signal emitted when the verification can be dismissed.
1033    pub fn connect_dismiss<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
1034        self.connect_closure(
1035            "dismiss",
1036            true,
1037            closure_local!(move |obj: Self| {
1038                f(&obj);
1039            }),
1040        )
1041    }
1042
1043    /// Connect to the signal emitted when the verification can be removed from
1044    /// the verification list.
1045    pub(super) fn connect_remove_from_list<F: Fn(&Self) + 'static>(
1046        &self,
1047        f: F,
1048    ) -> glib::SignalHandlerId {
1049        self.connect_closure(
1050            "remove-from-list",
1051            true,
1052            closure_local!(move |obj: Self| {
1053                f(&obj);
1054            }),
1055        )
1056    }
1057}
1058
1059/// Get the intersection or our methods and their methods.
1060fn intersect_methods(
1061    our_methods: Vec<VerificationMethod>,
1062    their_methods: &[VerificationMethod],
1063) -> Vec<VerificationMethod> {
1064    let mut supported_methods = our_methods;
1065
1066    supported_methods.retain(|m| match m {
1067        VerificationMethod::SasV1 => their_methods.contains(&VerificationMethod::SasV1),
1068        VerificationMethod::QrCodeScanV1 => {
1069            their_methods.contains(&VerificationMethod::QrCodeShowV1)
1070                && their_methods.contains(&VerificationMethod::ReciprocateV1)
1071        }
1072        VerificationMethod::QrCodeShowV1 => {
1073            their_methods.contains(&VerificationMethod::QrCodeScanV1)
1074                && their_methods.contains(&VerificationMethod::ReciprocateV1)
1075        }
1076        VerificationMethod::ReciprocateV1 => {
1077            (their_methods.contains(&VerificationMethod::QrCodeShowV1)
1078                || their_methods.contains(&VerificationMethod::QrCodeScanV1))
1079                && their_methods.contains(&VerificationMethod::ReciprocateV1)
1080        }
1081        _ => false,
1082    });
1083
1084    supported_methods
1085}