use crate::controllers::HttpResult; use crate::extractors::matrix_client_extractor::MatrixClientExtractor; use crate::utils::crypt_utils::sha512; use actix_web::dev::Payload; use actix_web::http::header; use actix_web::{FromRequest, HttpRequest, HttpResponse, web}; use matrix_sdk::crypto::{AttachmentDecryptor, MediaEncryptionInfo}; use matrix_sdk::media::{MediaFormat, MediaRequestParameters, MediaThumbnailSettings}; use matrix_sdk::ruma::events::room::MediaSource; use matrix_sdk::ruma::{OwnedMxcUri, UInt}; use std::io::{Cursor, Read}; #[derive(serde::Deserialize)] pub struct MediaMXCInPath { mxc: OwnedMxcUri, } /// Serve media resource handler pub async fn serve_mxc_handler(req: HttpRequest, media: web::Path) -> HttpResult { serve_mxc_file(req, media.into_inner().mxc).await } #[derive(serde::Deserialize)] pub struct MediaQuery { #[serde(default)] pub thumbnail: bool, } pub async fn serve_mxc_file(req: HttpRequest, media: OwnedMxcUri) -> HttpResult { let query = web::Query::::from_request(&req, &mut Payload::None).await?; serve_media(req, MediaSource::Plain(media), query.thumbnail).await } /// Serve a media file pub async fn serve_media(req: HttpRequest, source: MediaSource, thumbnail: bool) -> HttpResult { let client = MatrixClientExtractor::from_request(&req, &mut Payload::None).await?; let media = client .client .client .media() .get_media_content( &MediaRequestParameters { source: source.clone(), format: match thumbnail { true => MediaFormat::Thumbnail(MediaThumbnailSettings::new( UInt::new(100).unwrap(), UInt::new(100).unwrap(), )), false => MediaFormat::File, }, }, true, ) .await?; // Decrypt file if needed let media = if let MediaSource::Encrypted(file) = source { let mut cursor = Cursor::new(media); let mut decryptor = AttachmentDecryptor::new(&mut cursor, MediaEncryptionInfo::from(*file))?; let mut decrypted_data = Vec::new(); decryptor.read_to_end(&mut decrypted_data)?; decrypted_data } else { media }; let digest = sha512(&media); let mime_type = infer::get(&media).map(|x| x.mime_type()); // Check if the browser already knows the etag if let Some(c) = req.headers().get(header::IF_NONE_MATCH) && c.to_str().unwrap_or("") == digest { return Ok(HttpResponse::NotModified().finish()); } Ok(HttpResponse::Ok() .content_type(mime_type.unwrap_or("application/octet-stream")) .insert_header(("etag", digest)) .insert_header(("cache-control", "max-age=360000")) .body(media)) }