From a41a60ef07be9ac63d2b0724b4e789d3b049a3ce Mon Sep 17 00:00:00 2001 From: strawberry Date: Mon, 27 May 2024 14:43:13 -0400 Subject: [PATCH] media: dont ignore requested filename on /download for Content-Disposition Signed-off-by: strawberry --- src/api/client_server/media.rs | 15 ++++++++++--- src/core/utils/content_disposition.rs | 31 ++++++++++++++++----------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 0e70c1dc..7b492913 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -192,7 +192,7 @@ pub(crate) async fn get_content_route(body: Ruma) -> R content_disposition, }) = services().media.get(mxc.clone()).await? { - let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition)); + let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition, None)); let content_type = Some(make_content_type(&file, &content_type).to_owned()); Ok(get_content::v3::Response { @@ -220,6 +220,7 @@ pub(crate) async fn get_content_route(body: Ruma) -> R &response.file, &response.content_type, response.content_disposition, + None, )); let content_type = Some(make_content_type(&response.file, &response.content_type).to_owned()); @@ -272,7 +273,12 @@ pub(crate) async fn get_content_as_filename_route( content_disposition, }) = services().media.get(mxc.clone()).await? { - let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition)); + let content_disposition = Some(make_content_disposition( + &file, + &content_type, + content_disposition, + Some(body.filename.clone()), + )); let content_type = Some(make_content_type(&file, &content_type).to_owned()); Ok(get_content_as_filename::v3::Response { @@ -297,6 +303,7 @@ pub(crate) async fn get_content_as_filename_route( &remote_content_response.file, &remote_content_response.content_type, remote_content_response.content_disposition, + None, )); let content_type = Some( make_content_type(&remote_content_response.file, &remote_content_response.content_type).to_owned(), @@ -368,7 +375,7 @@ pub(crate) async fn get_content_thumbnail_route( ) .await? { - let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition)); + let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition, None)); let content_type = Some(make_content_type(&file, &content_type).to_owned()); Ok(get_content_thumbnail::v3::Response { @@ -425,6 +432,7 @@ pub(crate) async fn get_content_thumbnail_route( &get_thumbnail_response.file, &get_thumbnail_response.content_type, get_thumbnail_response.content_disposition, + None, )); let content_type = Some( make_content_type(&get_thumbnail_response.file, &get_thumbnail_response.content_type).to_owned(), @@ -498,6 +506,7 @@ async fn get_remote_content( &content_response.file, &content_response.content_type, content_response.content_disposition, + None, )); let content_type = Some(make_content_type(&content_response.file, &content_response.content_type).to_owned()); diff --git a/src/core/utils/content_disposition.rs b/src/core/utils/content_disposition.rs index 85828be7..2a2a6d18 100644 --- a/src/core/utils/content_disposition.rs +++ b/src/core/utils/content_disposition.rs @@ -74,7 +74,8 @@ pub fn sanitise_filename(filename: String) -> String { } /// creates the final Content-Disposition based on whether the filename exists -/// or not. +/// or not, or if a requested filename was specified (media download with +/// filename) /// /// if filename exists: /// `Content-Disposition: attachment/inline; filename=filename.ext` @@ -82,19 +83,25 @@ pub fn sanitise_filename(filename: String) -> String { /// else: `Content-Disposition: attachment/inline` #[tracing::instrument(skip(file))] pub fn make_content_disposition( - file: &[u8], content_type: &Option, content_disposition: Option, + file: &[u8], content_type: &Option, content_disposition: Option, req_filename: Option, ) -> String { - let filename = content_disposition.map_or_else(String::new, |content_disposition| { - let (_, filename) = content_disposition - .split_once("filename=") - .unwrap_or(("", "")); + let filename: String; - if filename.is_empty() { - String::new() - } else { - sanitise_filename(filename.to_owned()) - } - }); + if let Some(req_filename) = req_filename { + filename = sanitise_filename(req_filename); + } else { + filename = content_disposition.map_or_else(String::new, |content_disposition| { + let (_, filename) = content_disposition + .split_once("filename=") + .unwrap_or(("", "")); + + if filename.is_empty() { + String::new() + } else { + sanitise_filename(filename.to_owned()) + } + }); + }; if !filename.is_empty() { // Content-Disposition: attachment/inline; filename=filename.ext