tauri_plugin_matrix_svelte/matrix/
emoji_verification.rs

1use 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                // We only support SAS verification.
153                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}