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 #[default]
65 Created,
66 Requested,
70 NoSupportedMethods,
72 Ready,
76 SasConfirm,
80 QrScan,
82 QrScanned,
84 QrConfirm,
89 Done,
91 Cancelled,
93 Dismissed,
97 RoomLeft,
99 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 request: OnceCell<VerificationRequest>,
119 request_changes_abort_handle: RefCell<Option<tokio::task::AbortHandle>>,
120 verification: RefCell<Option<Verification>>,
122 verification_changes_abort_handle: RefCell<Option<tokio::task::AbortHandle>>,
123 #[property(get, set = Self::set_user, construct_only)]
125 user: BoundConstructOnlyObject<User>,
126 #[property(get, set = Self::set_room, construct_only)]
128 room: glib::WeakRef<Room>,
129 membership_handler: RefCell<Option<glib::SignalHandlerId>>,
130 #[property(get, set = Self::set_state, construct_only, builder(VerificationState::default()))]
132 state: Cell<VerificationState>,
133 #[property(get)]
137 was_accepted: Cell<bool>,
138 #[property(get = Self::is_finished)]
140 is_finished: PhantomData<bool>,
141 #[property(get = Self::supported_methods, type = VerificationSupportedMethods)]
143 supported_methods: RefCell<Vec<VerificationMethod>>,
144 #[property(get = Self::flow_id)]
146 flow_id: PhantomData<String>,
147 #[property(get)]
149 received_time: OnceCell<glib::DateTime>,
150 received_timeout_source: RefCell<Option<glib::SourceId>>,
151 #[property(get = Self::display_name)]
153 display_name: PhantomData<String>,
154 pub(super) qr_code: RefCell<Option<QrCode>>,
156 #[property(get)]
159 pub(super) qrcode_scanner: RefCell<Option<QrCodeScanner>>,
160 #[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 Signal::builder("sas-data-changed").build(),
178 Signal::builder("cancel-info-changed").build(),
180 Signal::builder("replaced")
182 .param_types([super::IdentityVerification::static_type()])
183 .build(),
184 Signal::builder("done").return_type::<bool>().build(),
190 Signal::builder("dismiss").build(),
192 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 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 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 pub(super) fn request(&self) -> &VerificationRequest {
282 self.request.get().expect("request should be initialized")
283 }
284
285 fn set_user(&self, user: User) {
287 let mut handlers = Vec::new();
288
289 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 fn set_room(&self, room: Option<&Room>) {
308 let Some(room) = room else {
309 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 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 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 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 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 fn supported_methods(&self) -> VerificationSupportedMethods {
382 self.supported_methods.borrow().as_slice().into()
383 }
384
385 fn display_name(&self) -> String {
387 let user = self.user.obj();
388
389 if user.is_own_user() {
390 "Login Request".to_string()
392 } else {
393 user.display_name()
394 }
395 }
396
397 fn flow_id(&self) -> String {
399 self.request().flow_id().to_owned()
400 }
401
402 fn set_was_viewed(&self, was_viewed: bool) {
404 if !was_viewed {
405 return;
407 }
408
409 self.was_viewed.set(was_viewed);
410 self.obj().notify_was_viewed();
411 }
412
413 fn set_was_accepted(&self, was_accepted: bool) {
415 if !was_accepted || self.was_accepted.get() {
416 return;
418 }
419
420 self.was_accepted.set(true);
421 self.obj().notify_was_accepted();
422 }
423
424 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 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 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 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 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 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 return;
525 }
526
527 if *cancel_code == CancelCode::Accepted && !self.was_viewed.get() {
528 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 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 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 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 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 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 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 pub struct IdentityVerification(ObjectSubclass<imp::IdentityVerification>);
688}
689
690impl IdentityVerification {
691 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 pub(crate) fn key(&self) -> VerificationKey {
703 VerificationKey::from_request(self.imp().request())
704 }
705
706 pub(crate) fn is_self_verification(&self) -> bool {
708 self.imp().request().is_self_verification()
709 }
710
711 pub(crate) fn started_by_us(&self) -> bool {
713 self.imp().request().we_started()
714 }
715
716 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 pub(crate) fn cancel_info(&self) -> Option<CancelInfo> {
741 self.imp().request().cancel_info()
742 }
743
744 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 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 pub(crate) fn choose_method(&self) {
794 self.imp().set_state(VerificationState::Ready);
795 }
796
797 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 pub(crate) fn sas_emoji(&self) -> Option<[Emoji; 7]> {
806 self.imp().sas_verification()?.emoji()
807 }
808
809 pub(crate) fn sas_decimals(&self) -> Option<(u16, u16, u16)> {
811 self.imp().sas_verification()?.decimals()
812 }
813
814 pub(crate) fn qr_code(&self) -> Option<QrCode> {
816 self.imp().qr_code.borrow().clone()
817 }
818
819 pub(crate) fn has_qr_code(&self) -> bool {
821 self.imp().qr_code.borrow().is_some()
822 }
823
824 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 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 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 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 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 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 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 self.cancel().await.is_err() {
946 self.dismiss();
947 }
948
949 Ok(new_verification)
950 }
951
952 pub(crate) fn dismiss(&self) {
956 self.remove_from_list();
957 self.emit_by_name::<()>("dismiss", &[]);
958 }
959
960 pub(crate) fn remove_from_list(&self) {
966 self.emit_by_name::<()>("remove-from-list", &[]);
967 }
968
969 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 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 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 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 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 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
1059fn 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}