refactor for ruma cow headers; update for ContentDisposition type
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
f540bed61e
commit
17a54bc4f8
6 changed files with 104 additions and 119 deletions
|
@ -87,6 +87,8 @@ pub enum Error {
|
|||
Config(&'static str, Cow<'static, str>),
|
||||
#[error("{0}")]
|
||||
Conflict(&'static str), // This is only needed for when a room alias already exists
|
||||
#[error(transparent)]
|
||||
ContentDisposition(#[from] ruma::http_headers::ContentDispositionParseError),
|
||||
#[error("{0}")]
|
||||
Database(Cow<'static, str>),
|
||||
#[error("Remote server {0} responded with: {1}")]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::debug_info;
|
||||
use std::borrow::Cow;
|
||||
|
||||
const ATTACHMENT: &str = "attachment";
|
||||
const INLINE: &str = "inline";
|
||||
use ruma::http_headers::{ContentDisposition, ContentDispositionType};
|
||||
|
||||
use crate::debug_info;
|
||||
|
||||
/// as defined by MSC2702
|
||||
const ALLOWED_INLINE_CONTENT_TYPES: [&str; 26] = [
|
||||
|
@ -38,42 +39,44 @@ const ALLOWED_INLINE_CONTENT_TYPES: [&str; 26] = [
|
|||
/// Content-Type against MSC2702 list of safe inline Content-Types
|
||||
/// (`ALLOWED_INLINE_CONTENT_TYPES`)
|
||||
#[must_use]
|
||||
pub fn content_disposition_type(content_type: &Option<String>) -> &'static str {
|
||||
pub fn content_disposition_type(content_type: Option<&str>) -> ContentDispositionType {
|
||||
let Some(content_type) = content_type else {
|
||||
debug_info!("No Content-Type was given, assuming attachment for Content-Disposition");
|
||||
return ATTACHMENT;
|
||||
return ContentDispositionType::Attachment;
|
||||
};
|
||||
|
||||
// is_sorted is unstable
|
||||
/* debug_assert!(ALLOWED_INLINE_CONTENT_TYPES.is_sorted(),
|
||||
* "ALLOWED_INLINE_CONTENT_TYPES is not sorted"); */
|
||||
|
||||
let content_type = content_type
|
||||
let content_type: Cow<'_, str> = content_type
|
||||
.split(';')
|
||||
.next()
|
||||
.unwrap_or(content_type)
|
||||
.to_ascii_lowercase();
|
||||
.to_ascii_lowercase()
|
||||
.into();
|
||||
|
||||
if ALLOWED_INLINE_CONTENT_TYPES
|
||||
.binary_search(&content_type.as_str())
|
||||
.binary_search(&content_type.as_ref())
|
||||
.is_ok()
|
||||
{
|
||||
INLINE
|
||||
ContentDispositionType::Inline
|
||||
} else {
|
||||
ATTACHMENT
|
||||
ContentDispositionType::Attachment
|
||||
}
|
||||
}
|
||||
|
||||
/// sanitises the file name for the Content-Disposition using
|
||||
/// `sanitize_filename` crate
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub fn sanitise_filename(filename: String) -> String {
|
||||
let options = sanitize_filename::Options {
|
||||
truncate: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
sanitize_filename::sanitize_with_options(filename, options)
|
||||
pub fn sanitise_filename(filename: &str) -> String {
|
||||
sanitize_filename::sanitize_with_options(
|
||||
filename,
|
||||
sanitize_filename::Options {
|
||||
truncate: false,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// creates the final Content-Disposition based on whether the filename exists
|
||||
|
@ -85,33 +88,13 @@ pub fn sanitise_filename(filename: String) -> String {
|
|||
///
|
||||
/// else: `Content-Disposition: attachment/inline`
|
||||
pub fn make_content_disposition(
|
||||
content_type: &Option<String>, content_disposition: Option<String>, req_filename: Option<String>,
|
||||
) -> String {
|
||||
let filename: String;
|
||||
|
||||
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
|
||||
format!("{}; filename={}", content_disposition_type(content_type), filename)
|
||||
} else {
|
||||
// Content-Disposition: attachment/inline
|
||||
String::from(content_disposition_type(content_type))
|
||||
}
|
||||
content_disposition: Option<&ContentDisposition>, content_type: Option<&str>, filename: Option<&str>,
|
||||
) -> ContentDisposition {
|
||||
ContentDisposition::new(content_disposition_type(content_type)).with_filename(
|
||||
filename
|
||||
.or_else(|| content_disposition.and_then(|content_disposition| content_disposition.filename.as_deref()))
|
||||
.map(sanitise_filename),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -136,4 +119,20 @@ mod tests {
|
|||
|
||||
assert_eq!(SANITISED, sanitize_filename::sanitize_with_options(SAMPLE, options.clone()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_sanitisation() {
|
||||
use crate::utils::string::EMPTY;
|
||||
|
||||
let result = sanitize_filename::sanitize_with_options(
|
||||
EMPTY,
|
||||
sanitize_filename::Options {
|
||||
windows: true,
|
||||
truncate: true,
|
||||
replacement: "",
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(EMPTY, result);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue