diff --git a/src/blob.rs b/src/blob.rs index cfad617b3..ddd1873fa 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -232,41 +232,44 @@ impl<'a> BlobObject<'a> { /// Get a file extension if any, including the dot, in lower case, otherwise an empty string. fn get_extension(name: &str) -> String { - let mut name = name.to_string(); + let mut name = name; for part in name.rsplit('/') { if !part.is_empty() { - name = part.to_string(); + name = part; break; } } for part in name.rsplit('\\') { if !part.is_empty() { - name = part.to_string(); + name = part; break; } } // Let's take the tricky filename // "file.with_lots_of_characters_behind_point_and_double_ending.tar.gz" as an example. - // Split it into "file" and "with_lots_of_characters_behind_point_and_double_ending.tar.gz": - let mut iter = name.splitn(2, '.'); - iter.next(); - - let ext_chars = iter.next().unwrap_or_default().chars(); - let ext: String = ext_chars + // Assume that the extension is 32 chars maximum. + let ext: String = name + .chars() .rev() - .take(32) + .take_while(|c| !c.is_whitespace()) + .take(33) .collect::>() .iter() .rev() .collect(); - // ext == "d_point_and_double_ending.tar.gz" + // ext == "nd_point_and_double_ending.tar.gz" + // Split it into "nd_point_and_double_ending" and "tar.gz": + let mut iter = ext.splitn(2, '.'); + iter.next(); + + let ext = iter.next().unwrap_or_default(); if ext.is_empty() { - ext + String::new() } else { format!(".{ext}").to_lowercase() - // Return ".d_point_and_double_ending.tar.gz", which is not perfect but acceptable. + // Return ".tar.gz". } } diff --git a/src/receive_imf/tests.rs b/src/receive_imf/tests.rs index b1d99ec0b..9b010fb47 100644 --- a/src/receive_imf/tests.rs +++ b/src/receive_imf/tests.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use regex::Regex; use tokio::fs; use super::*; @@ -2960,20 +2961,27 @@ Reply from different address } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_long_and_duplicated_filenames() -> Result<()> { +async fn test_weird_and_duplicated_filenames() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = tcm.alice().await; let bob = tcm.bob().await; - for filename_sent in &[ - "foo.bar very long file name test baz.tar.gz", - "foobarabababababababbababababverylongfilenametestbaz.tar.gz", - "fooo...tar.gz", - "foo. .tar.gz", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.tar.gz", - "a.tar.gz", - "a.tar.gz", - "a.a..a.a.a.a.tar.gz", + for (filename_sent, expected_ext) in &[ + ("foo.bar very long file name test baz.tar.gz", "tar.gz"), + ( + "foo.barabababababababbababababverylongfilenametestbaz.tar.gz", + "tar.gz", + ), + ("fooo...tar.gz", "..tar.gz"), + ("foo. .tar.gz", "tar.gz"), + ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.tar.gz", + "tar.gz", + ), + ("a.tar.gz", "tar.gz"), + ("a.tar.gz", "tar.gz"), + ("a.a..a.a.a.a.tar.gz", "a..a.a.a.a.tar.gz"), + ("a. tar.tar.gz", "tar.gz"), ] { let attachment = alice.blobdir.join(filename_sent); let content = format!("File content of {filename_sent}"); @@ -2987,23 +2995,33 @@ async fn test_long_and_duplicated_filenames() -> Result<()> { let msg_bob = bob.recv_msg(&sent).await; - async fn check_message(msg: &Message, t: &TestContext, filename: &str, content: &str) { + async fn check_message( + msg: &Message, + t: &TestContext, + filename: &str, + expected_ext: &str, + content: &str, + ) { assert_eq!(msg.get_viewtype(), Viewtype::File); let resulting_filename = msg.get_filename().unwrap(); assert_eq!(resulting_filename, filename); let path = msg.get_file(t).unwrap(); + if !msg.get_state().is_outgoing() { + let re = + Regex::new(&("^[[:xdigit:]]{16}.".to_string() + expected_ext + "$")).unwrap(); + assert!( + re.is_match(path.file_name().unwrap().to_str().unwrap()), + "invalid path {path:?}" + ); + } let path2 = path.with_file_name("saved.txt"); msg.save_file(t, &path2).await.unwrap(); - assert!( - path.to_str().unwrap().ends_with(".tar.gz"), - "path {path:?} doesn't end with .tar.gz" - ); assert_eq!(fs::read_to_string(&path).await.unwrap(), content); assert_eq!(fs::read_to_string(&path2).await.unwrap(), content); fs::remove_file(path2).await.unwrap(); } - check_message(&msg_alice, &alice, filename_sent, &content).await; - check_message(&msg_bob, &bob, filename_sent, &content).await; + check_message(&msg_alice, &alice, filename_sent, expected_ext, &content).await; + check_message(&msg_bob, &bob, filename_sent, expected_ext, &content).await; } Ok(())