matrix_sdk/room/
shared_room_history.rs1use std::iter;
16
17use matrix_sdk_base::{
18 crypto::types::events::room_key_bundle::RoomKeyBundleContent,
19 media::{MediaFormat, MediaRequestParameters},
20};
21use ruma::{OwnedUserId, UserId, events::room::MediaSource};
22use tracing::{info, instrument, warn};
23
24use crate::{Error, Result, Room};
25
26#[instrument(skip(room), fields(room_id = ?room.room_id()))]
31pub(super) async fn share_room_history(room: &Room, user_id: OwnedUserId) -> Result<()> {
32 let client = &room.client;
33
34 let own_identity = match client.user_id() {
36 Some(own_user) => client.encryption().get_user_identity(own_user).await?,
37 None => None,
38 };
39
40 if own_identity.is_none() {
41 warn!("Not sharing message history as cross-signing is not set up");
42 return Ok(());
43 }
44
45 info!("Sharing message history");
46
47 let olm_machine = client.olm_machine().await;
48 let olm_machine = olm_machine.as_ref().ok_or(Error::NoOlmMachine)?;
49
50 let bundle = olm_machine.store().build_room_key_bundle(room.room_id()).await?;
52
53 if bundle.is_empty() {
54 info!("No keys to share");
55 return Ok(());
56 }
57
58 let json = serde_json::to_vec(&bundle)?;
60 let upload = client.upload_encrypted_file(&mut (json.as_slice())).await?;
61
62 info!(
63 media_url = ?upload.url,
64 shared_keys = bundle.room_keys.len(),
65 withheld_keys = bundle.withheld.len(),
66 "Uploaded encrypted key blob"
67 );
68
69 let (req_id, request) = olm_machine.query_keys_for_users(iter::once(user_id.as_ref()));
71
72 if !request.device_keys.is_empty() {
73 room.client.keys_query(&req_id, request.device_keys).await?;
74 }
75
76 client.claim_one_time_keys(iter::once(user_id.as_ref())).await?;
78
79 let content = RoomKeyBundleContent { room_id: room.room_id().to_owned(), file: upload };
81 let requests = {
82 let olm_machine = client.olm_machine().await;
83 let olm_machine = olm_machine.as_ref().ok_or(Error::NoOlmMachine)?;
84 olm_machine
85 .share_room_key_bundle_data(
86 &user_id,
87 &client.base_client().room_key_recipient_strategy,
88 content,
89 )
90 .await?
91 };
92
93 for request in requests {
94 let response = client.send_to_device(&request).await?;
95 client.mark_request_as_sent(&request.txn_id, &response).await?;
96 }
97
98 Ok(())
99}
100
101#[instrument(skip(room), fields(room_id = ?room.room_id(), bundle_sender))]
115pub(crate) async fn maybe_accept_key_bundle(room: &Room, inviter: &UserId) -> Result<()> {
116 let client = &room.client;
120 let olm_machine = client.olm_machine().await;
121
122 let Some(olm_machine) = olm_machine.as_ref() else {
123 warn!("Not fetching room key bundle as the Olm machine is not available");
124 return Ok(());
125 };
126
127 let Some(bundle_info) =
128 olm_machine.store().get_received_room_key_bundle_data(room.room_id(), inviter).await?
129 else {
130 info!("No room key bundle from inviter found");
132 return Ok(());
133 };
134
135 tracing::Span::current().record("bundle_sender", bundle_info.sender_user.as_str());
136
137 let (req_id, request) =
143 olm_machine.query_keys_for_users(iter::once(bundle_info.sender_user.as_ref()));
144
145 if !request.device_keys.is_empty() {
146 room.client.keys_query(&req_id, request.device_keys).await?;
147 }
148
149 let bundle_content = client
150 .media()
151 .get_media_content(
152 &MediaRequestParameters {
153 source: MediaSource::Encrypted(Box::new(bundle_info.bundle_data.file.clone())),
154 format: MediaFormat::File,
155 },
156 false,
157 )
158 .await?;
159
160 match serde_json::from_slice(&bundle_content) {
161 Ok(bundle) => {
162 olm_machine
163 .store()
164 .receive_room_key_bundle(
165 &bundle_info,
166 bundle,
167 |_, _| {},
169 )
170 .await?;
171 }
172 Err(err) => {
173 warn!("Failed to deserialize room key bundle: {err}");
174 }
175 }
176
177 Ok(())
183}