From f751eb2a81560b6691783c0d2101643ef60dbcf1 Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Wed, 27 May 2026 13:40:33 -0700 Subject: [PATCH] Handle encrypted `MediaSource`s properly, display encrypted media --- src/home/room_image_viewer.rs | 9 +-- src/home/room_screen.rs | 103 ++++++++++++---------------------- src/media_cache.rs | 28 ++------- 3 files changed, 45 insertions(+), 95 deletions(-) diff --git a/src/home/room_image_viewer.rs b/src/home/room_image_viewer.rs index ba8eaa3a3..67a4833d6 100644 --- a/src/home/room_image_viewer.rs +++ b/src/home/room_image_viewer.rs @@ -6,7 +6,7 @@ use matrix_sdk::{ }; use matrix_sdk::reqwest::StatusCode; -use crate::{media_cache::{MediaCache, MediaCacheEntry}, shared::image_viewer::{ImageViewerAction, ImageViewerError, LoadState}}; +use crate::{media_cache::{MediaCache, MediaCacheEntry}, shared::{attachment_download::media_source_mxc, image_viewer::{ImageViewerAction, ImageViewerError, LoadState}}}; /// Populates the image viewer modal with the given media content. /// @@ -18,11 +18,8 @@ pub fn populate_matrix_image_modal( media_source: MediaSource, media_cache: &mut MediaCache, ) { - let MediaSource::Plain(mxc_uri) = media_source else { - return; - }; // Try to get media from cache or trigger fetch - let media_entry = media_cache.try_get_media_or_fetch(&mxc_uri, MediaFormat::File); + let media_entry = media_cache.try_get_media_or_fetch(&media_source, MediaFormat::File); // Handle the different media states match media_entry { @@ -39,7 +36,7 @@ pub fn populate_matrix_image_modal( }; cx.action(ImageViewerAction::Show(LoadState::Error(error))); // Remove failed media entry from cache for MediaFormat::File so as to start all over again from loading Thumbnail. - media_cache.remove_cache_entry(&mxc_uri, Some(MediaFormat::File)); + media_cache.remove_cache_entry(media_source_mxc(&media_source), Some(MediaFormat::File)); } _ => {} } diff --git a/src/home/room_screen.rs b/src/home/room_screen.rs index 55086ba08..d8c29fb78 100644 --- a/src/home/room_screen.rs +++ b/src/home/room_screen.rs @@ -15,7 +15,7 @@ use matrix_sdk::{ AudioMessageEventContent, EmoteMessageEventContent, FileMessageEventContent, FormattedBody, ImageMessageEventContent, KeyVerificationRequestEventContent, LocationMessageEventContent, MessageFormat, MessageType, NoticeMessageEventContent, TextMessageEventContent, VideoMessageEventContent } }, - sticker::{StickerEventContent, StickerMediaSource}, + sticker::StickerEventContent, }, matrix_uri::MatrixId, uint } }; @@ -3758,48 +3758,32 @@ fn populate_message_view( let was_cached = existed && item_drawn_status.content_drawn; let text_or_image_ref = item.text_or_image(cx, ids!(content.message)); - match source { - StickerMediaSource::Plain(owned_mxc_url) => { - let filename = if body.is_empty() { "sticker".to_owned() } else { body.clone() }; - let size = info.size.map(u64::from); - download_info = if was_cached { - matches!(text_or_image_ref.status(), TextOrImageStatus::Text) - .then(|| DownloadableAttachment { - media_source: MediaSource::Plain(owned_mxc_url.clone()), - filename, - size, - kind: DownloadKind::Image, - }) - } else { - let (is_image_fully_drawn, fallback) = populate_image_message_content_with_fallback( - cx, - &text_or_image_ref, - Some(Box::new(info.clone())), - MediaSource::Plain(owned_mxc_url.clone()), - body, - media_cache, - filename, - size, - DownloadKind::Image, - ); - new_drawn_status.content_drawn = is_image_fully_drawn; - fallback - }; - } - // Encrypted sticker decryption isn't wired up yet; show a - // placeholder so the message doesn't render blank. - _ => { - if !was_cached { - let label = if body.is_empty() { - "[Encrypted sticker]".to_owned() - } else { - format!("[Encrypted sticker: {body}]") - }; - text_or_image_ref.show_text(cx, label); - new_drawn_status.content_drawn = true; - } - } - } + let media_source: MediaSource = source.clone().into(); + let filename = if body.is_empty() { "sticker".to_owned() } else { body.clone() }; + let size = info.size.map(u64::from); + download_info = if was_cached { + matches!(text_or_image_ref.status(), TextOrImageStatus::Text) + .then(|| DownloadableAttachment { + media_source, + filename, + size, + kind: DownloadKind::Image, + }) + } else { + let (is_image_fully_drawn, fallback) = populate_image_message_content_with_fallback( + cx, + &text_or_image_ref, + Some(Box::new(info.clone())), + media_source, + body, + media_cache, + filename, + size, + DownloadKind::Image, + ); + new_drawn_status.content_drawn = is_image_fully_drawn; + fallback + }; (item, was_cached) } // Handle messages that have been redacted (deleted). @@ -4148,12 +4132,10 @@ fn populate_image_message_content( let mut fully_drawn = false; - // A closure that fetches and shows the image from the given `mxc_uri`, - // marking it as fully drawn if the image was available. - let mut fetch_and_show_image_uri = |cx: &mut Cx, mxc_uri: OwnedMxcUri, image_info: Box| { - match media_cache.try_get_media_or_fetch(&mxc_uri, MEDIA_THUMBNAIL_FORMAT.into()) { + let mut fetch_and_show_media_source = |cx: &mut Cx, media_source: MediaSource, image_info: Box| { + match media_cache.try_get_media_or_fetch(&media_source, MEDIA_THUMBNAIL_FORMAT.into()) { (MediaCacheEntry::Loaded(data), _media_format) => { - let show_image_result = text_or_image_ref.show_image(cx, Some(MediaSource::Plain(mxc_uri)),|cx, img| { + let show_image_result = text_or_image_ref.show_image(cx, Some(media_source), |cx, img| { utils::load_png_or_jpg(&img, cx, &data) .map(|()| img.size_in_pixels(cx).unwrap_or_default()) }); @@ -4169,7 +4151,7 @@ fn populate_image_message_content( (MediaCacheEntry::Requested, _media_format) => { // If the image is being fetched, we try to show its blurhash. if let (Some(ref blurhash), Some(width), Some(height)) = (image_info.blurhash.clone(), image_info.width, image_info.height) { - let show_image_result = text_or_image_ref.show_image(cx, Some(MediaSource::Plain(mxc_uri)), |cx, img| { + let show_image_result = text_or_image_ref.show_image(cx, Some(media_source), |cx, img| { let (Ok(width), Ok(height)) = (width.try_into(), height.try_into()) else { return Err(image_cache::ImageError::EmptyData) }; @@ -4202,7 +4184,7 @@ fn populate_image_message_content( Err(e) => { error!("Failed to decode blurhash {e:?}"); Err(image_cache::ImageError::EmptyData) - } + } } }); if let Err(e) = show_image_result { @@ -4218,26 +4200,13 @@ fn populate_image_message_content( fully_drawn = true; return; } - text_or_image_ref - .show_text(cx, format!("{body}\n\nFailed to fetch image from {:?}", mxc_uri)); - // For now, we consider this as being "complete". In the future, we could support - // retrying to fetch thumbnail of the image on a user click/tap. - fully_drawn = true; - } - } - }; - - let mut fetch_and_show_media_source = |cx: &mut Cx, media_source: MediaSource, image_info: Box| { - match media_source { - MediaSource::Encrypted(encrypted) => { - // We consider this as "fully drawn" since we don't yet support encryption. text_or_image_ref.show_text( cx, - format!("{body}\n\n[TODO] fetch encrypted image at {:?}", encrypted.url) + format!("{body}\n\nFailed to fetch image from {:?}", media_source_mxc(&media_source)), ); - }, - MediaSource::Plain(mxc_uri) => { - fetch_and_show_image_uri(cx, mxc_uri, image_info) + // For now, we consider this as being "complete". In the future, we could support + // retrying to fetch thumbnail of the image on a user click/tap. + fully_drawn = true; } } }; diff --git a/src/media_cache.rs b/src/media_cache.rs index 99aac707b..bb62103c3 100644 --- a/src/media_cache.rs +++ b/src/media_cache.rs @@ -1,9 +1,9 @@ -use std::{ops::{Deref, DerefMut}, sync::{Arc, Mutex}, time::SystemTime}; +use std::{ops::{Deref, DerefMut}, sync::{Arc, Mutex}}; use hashbrown::{hash_map::RawEntryMut, HashMap}; -use makepad_widgets::{error, log, SignalToUI}; +use makepad_widgets::{error, SignalToUI}; use matrix_sdk::{media::{MediaFormat, MediaRequestParameters, MediaThumbnailSettings}, ruma::{events::room::MediaSource, OwnedMxcUri}, Error, HttpError}; use matrix_sdk::reqwest::StatusCode; -use crate::{home::room_screen::TimelineUpdate, sliding_sync::{self, MatrixRequest}}; +use crate::{home::room_screen::TimelineUpdate, shared::attachment_download::media_source_mxc, sliding_sync::{self, MatrixRequest}}; /// The value type in the media cache, one per Matrix URI. #[derive(Debug, Clone)] @@ -84,9 +84,10 @@ impl MediaCache { /// Returns a tuple of the media cache entry and the media format of that cached entry. pub fn try_get_media_or_fetch( &mut self, - mxc_uri: &OwnedMxcUri, + source: &MediaSource, requested_format: MediaFormat, ) -> (MediaCacheEntry, MediaFormat) { + let mxc_uri = media_source_mxc(source); let mut post_request_retval = (MediaCacheEntry::Requested, requested_format.clone()); let entry_ref_to_fetch: MediaCacheEntryRef; @@ -161,7 +162,7 @@ impl MediaCache { sliding_sync::submit_async_request(MatrixRequest::FetchMedia { media_request: MediaRequestParameters { - source: MediaSource::Plain(mxc_uri.clone()), + source: source.clone(), format: requested_format, }, on_fetched: insert_into_cache, @@ -278,23 +279,6 @@ fn insert_into_cache>>( let new_value = match data { Ok(data) => { let data = data.into(); - - // debugging: dump out the media image to disk - if false { - if let MediaSource::Plain(mxc_uri) = &request.source { - log!("Fetched media for {mxc_uri}"); - let mut path = crate::temp_storage::get_temp_dir_path().clone(); - let filename = format!("{}_{}_{}", - SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis(), - mxc_uri.server_name().unwrap(), mxc_uri.media_id().unwrap(), - ); - path.push(filename); - path.set_extension("png"); - log!("Writing user media image to disk: {:?}", path); - std::fs::write(path, &data) - .expect("Failed to write user media image to disk"); - } - } MediaCacheEntry::Loaded(data) } Err(e) => error_to_media_cache_entry(e, &request)