tauri_plugin_matrix_svelte/matrix/
emoji_verification.rs1use futures_util::stream::StreamExt;
2use matrix_sdk::{
3 encryption::verification::{
4 format_emojis, Emoji, SasState, SasVerification, Verification, VerificationRequest,
5 VerificationRequestState,
6 },
7 ruma::UserId,
8 Client,
9};
10use tauri::{AppHandle, Emitter, Listener, Runtime};
11
12use crate::models::matrix::{
13 MatrixSvelteEmitEvent, MatrixSvelteListenEvent, MatrixVerificationEmojis,
14 MatrixVerificationResponse,
15};
16
17async fn wait_for_confirmation<'a, R: Runtime>(
18 sas: SasVerification,
19 emoji: [Emoji; 7],
20 app_handle: AppHandle<R>,
21) -> anyhow::Result<()> {
22 let payload = MatrixVerificationEmojis::new(format_emojis(emoji));
23 let _ = &app_handle.emit(MatrixSvelteEmitEvent::VerificationStart.as_str(), payload)?;
24
25 let _ = &app_handle.listen(
26 MatrixSvelteListenEvent::VerificationResult.as_str(),
27 move |event| {
28 let sas_clone = sas.clone();
29 tauri::async_runtime::spawn(async move {
30 if let Ok(payload) =
31 serde_json::from_str::<MatrixVerificationResponse>(event.payload())
32 {
33 match payload.confirmed {
34 true => sas_clone.confirm().await.unwrap(),
35 false => sas_clone.cancel().await.unwrap(),
36 }
37 }
38 });
39 },
40 );
41 Ok(())
42}
43
44async fn print_devices(user_id: &UserId, client: &Client) {
45 println!("Devices of user {user_id}");
46
47 for device in client
48 .encryption()
49 .get_user_devices(user_id)
50 .await
51 .unwrap()
52 .devices()
53 {
54 if device.device_id()
55 == client
56 .device_id()
57 .expect("We should be logged in now and know our device id")
58 {
59 continue;
60 }
61
62 println!(
63 " {:<10} {:<30} {:<}",
64 device.device_id(),
65 device.display_name().unwrap_or("-"),
66 if device.is_verified() { "✅" } else { "❌" }
67 );
68 }
69}
70
71async fn sas_verification_handler<R: Runtime>(
72 client: Client,
73 sas: SasVerification,
74 app_handle: AppHandle<R>,
75) {
76 println!(
77 "Starting verification with {} {}",
78 &sas.other_device().user_id(),
79 &sas.other_device().device_id()
80 );
81 print_devices(sas.other_device().user_id(), &client).await;
82 sas.accept().await.unwrap();
83
84 let mut stream = sas.changes();
85
86 while let Some(state) = stream.next().await {
87 match state {
88 SasState::KeysExchanged {
89 emojis,
90 decimals: _,
91 } => {
92 tauri::async_runtime::spawn(wait_for_confirmation(
93 sas.clone(),
94 emojis
95 .expect("We only support verifications using emojis")
96 .emojis,
97 app_handle.clone(),
98 ));
99 }
100 SasState::Done { .. } => {
101 let device = sas.other_device();
102
103 println!(
104 "Successfully verified device {} {} {:?}",
105 device.user_id(),
106 device.device_id(),
107 device.local_trust_state()
108 );
109
110 print_devices(sas.other_device().user_id(), &client).await;
111
112 break;
113 }
114 SasState::Cancelled(cancel_info) => {
115 println!(
116 "The verification has been cancelled, reason: {}",
117 cancel_info.reason()
118 );
119
120 break;
121 }
122 SasState::Created { .. }
123 | SasState::Started { .. }
124 | SasState::Accepted { .. }
125 | SasState::Confirmed => (),
126 }
127 }
128}
129
130pub async fn request_verification_handler<R: Runtime>(
131 client: Client,
132 request: VerificationRequest,
133 app_handle: AppHandle<R>,
134) {
135 println!(
136 "Accepting verification request from {}",
137 request.other_user_id(),
138 );
139 request
140 .accept()
141 .await
142 .expect("Can't accept verification request");
143
144 let mut stream = request.changes();
145
146 while let Some(state) = stream.next().await {
147 match state {
148 VerificationRequestState::Created { .. }
149 | VerificationRequestState::Requested { .. }
150 | VerificationRequestState::Ready { .. } => (),
151 VerificationRequestState::Transitioned { verification } => {
152 if let Verification::SasV1(s) = verification {
154 tauri::async_runtime::spawn(sas_verification_handler(
155 client,
156 s,
157 app_handle.clone(),
158 ));
159 break;
160 }
161 }
162 VerificationRequestState::Done | VerificationRequestState::Cancelled(_) => break,
163 }
164 }
165}