media: dont ignore requested filename on /download for Content-Disposition

Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
strawberry 2024-05-27 14:43:13 -04:00 committed by June 🍓🦴
parent ec7a9ab726
commit a41a60ef07
2 changed files with 31 additions and 15 deletions

View file

@ -192,7 +192,7 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
content_disposition, content_disposition,
}) = services().media.get(mxc.clone()).await? }) = 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()); let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content::v3::Response { Ok(get_content::v3::Response {
@ -220,6 +220,7 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
&response.file, &response.file,
&response.content_type, &response.content_type,
response.content_disposition, response.content_disposition,
None,
)); ));
let content_type = Some(make_content_type(&response.file, &response.content_type).to_owned()); 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, content_disposition,
}) = services().media.get(mxc.clone()).await? }) = 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()); let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content_as_filename::v3::Response { 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.file,
&remote_content_response.content_type, &remote_content_response.content_type,
remote_content_response.content_disposition, remote_content_response.content_disposition,
None,
)); ));
let content_type = Some( let content_type = Some(
make_content_type(&remote_content_response.file, &remote_content_response.content_type).to_owned(), 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? .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()); let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content_thumbnail::v3::Response { 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.file,
&get_thumbnail_response.content_type, &get_thumbnail_response.content_type,
get_thumbnail_response.content_disposition, get_thumbnail_response.content_disposition,
None,
)); ));
let content_type = Some( let content_type = Some(
make_content_type(&get_thumbnail_response.file, &get_thumbnail_response.content_type).to_owned(), 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.file,
&content_response.content_type, &content_response.content_type,
content_response.content_disposition, content_response.content_disposition,
None,
)); ));
let content_type = Some(make_content_type(&content_response.file, &content_response.content_type).to_owned()); let content_type = Some(make_content_type(&content_response.file, &content_response.content_type).to_owned());

View file

@ -74,7 +74,8 @@ pub fn sanitise_filename(filename: String) -> String {
} }
/// creates the final Content-Disposition based on whether the filename exists /// 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: /// if filename exists:
/// `Content-Disposition: attachment/inline; filename=filename.ext` /// `Content-Disposition: attachment/inline; filename=filename.ext`
@ -82,19 +83,25 @@ pub fn sanitise_filename(filename: String) -> String {
/// else: `Content-Disposition: attachment/inline` /// else: `Content-Disposition: attachment/inline`
#[tracing::instrument(skip(file))] #[tracing::instrument(skip(file))]
pub fn make_content_disposition( pub fn make_content_disposition(
file: &[u8], content_type: &Option<String>, content_disposition: Option<String>, file: &[u8], content_type: &Option<String>, content_disposition: Option<String>, req_filename: Option<String>,
) -> String { ) -> String {
let filename = content_disposition.map_or_else(String::new, |content_disposition| { let filename: String;
let (_, filename) = content_disposition
.split_once("filename=")
.unwrap_or(("", ""));
if filename.is_empty() { if let Some(req_filename) = req_filename {
String::new() filename = sanitise_filename(req_filename);
} else { } else {
sanitise_filename(filename.to_owned()) 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() { if !filename.is_empty() {
// Content-Disposition: attachment/inline; filename=filename.ext // Content-Disposition: attachment/inline; filename=filename.ext