sofie_storage/
disposition.rs1use sofie_core::encoding::percent;
2
3use crate::Disposition;
4
5#[must_use]
8pub fn content_disposition(disposition: Disposition, filename: Option<&str>) -> String {
9 let mut value = disposition.as_str().to_owned();
10 let Some(filename) = filename.map(shown).filter(|name| !name.is_empty()) else {
11 return value;
12 };
13 value.push_str("; filename=\"");
14 value.extend(
15 filename
16 .chars()
17 .map(|character| if character.is_ascii() { character } else { '_' }),
18 );
19 value.push_str("\"; filename*=UTF-8''");
20 percent::push_encoded(&mut value, &filename, is_attribute_character);
21 value
22}
23
24fn is_attribute_character(byte: u8) -> bool {
26 byte.is_ascii_alphanumeric() || b"!#$&+-.^_`|~".contains(&byte)
27}
28
29fn shown(filename: &str) -> String {
30 filename
31 .chars()
32 .filter(|character| {
33 !sofie_core::misleads_display(*character) && !matches!(character, '"' | '\\')
34 })
35 .collect()
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[test]
43 fn a_name_is_given_as_ascii_and_as_utf_8() {
44 assert_eq!(
45 content_disposition(Disposition::Attachment, None),
46 "attachment"
47 );
48 assert_eq!(
49 content_disposition(Disposition::Attachment, Some("Fatura Şubat.pdf")),
50 "attachment; filename=\"Fatura _ubat.pdf\"; filename*=UTF-8''Fatura%20%C5%9Eubat.pdf"
51 );
52 assert_eq!(
53 content_disposition(Disposition::Inline, Some("a\"b\r\n;c%.png")),
54 "inline; filename=\"ab;c%.png\"; filename*=UTF-8''ab%3Bc%25.png"
55 );
56 }
57}