Skip to content

Commit 8f41aed

Browse files
committed
fix: Assume file extensions are 32 chars max and don't contain whitespace (#5338)
Before file extensions were also limited to 32 chars, but extra chars in the beginning were just cut off, e.g. "file.with_lots_of_characters_behind_point_and_double_ending.tar.gz" was considered to have an extension "d_point_and_double_ending.tar.gz". Better to take only "tar.gz" then. Also don't include whitespace-containing parts in extensions. File extensions generally don't contain whitespaces.
1 parent 19be12a commit 8f41aed

File tree

2 files changed

+44
-23
lines changed

2 files changed

+44
-23
lines changed

src/blob.rs

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,16 @@ impl<'a> BlobObject<'a> {
253253
///
254254
/// The extension part will always be lowercased.
255255
fn sanitise_name(name: &str) -> (String, String) {
256-
let mut name = name.to_string();
256+
let mut name = name;
257257
for part in name.rsplit('/') {
258258
if !part.is_empty() {
259-
name = part.to_string();
259+
name = part;
260260
break;
261261
}
262262
}
263263
for part in name.rsplit('\\') {
264264
if !part.is_empty() {
265-
name = part.to_string();
265+
name = part;
266266
break;
267267
}
268268
}
@@ -272,32 +272,39 @@ impl<'a> BlobObject<'a> {
272272
replacement: "",
273273
};
274274

275-
let clean = sanitize_filename::sanitize_with_options(name, opts);
276-
// Let's take the tricky filename
275+
let name = sanitize_filename::sanitize_with_options(name, opts);
276+
// Let's take a tricky filename,
277277
// "file.with_lots_of_characters_behind_point_and_double_ending.tar.gz" as an example.
278-
// Split it into "file" and "with_lots_of_characters_behind_point_and_double_ending.tar.gz":
279-
let mut iter = clean.splitn(2, '.');
280-
281-
let stem: String = iter.next().unwrap_or_default().chars().take(64).collect();
282-
// stem == "file"
283-
284-
let ext_chars = iter.next().unwrap_or_default().chars();
285-
let ext: String = ext_chars
278+
// Assume that the extension is 32 chars maximum.
279+
let ext: String = name
280+
.chars()
286281
.rev()
287-
.take(32)
282+
.take_while(|c| !c.is_whitespace())
283+
.take(33)
288284
.collect::<Vec<_>>()
289285
.iter()
290286
.rev()
291287
.collect();
292-
// ext == "d_point_and_double_ending.tar.gz"
288+
// ext == "nd_point_and_double_ending.tar.gz"
293289

294-
if ext.is_empty() {
295-
(stem, "".to_string())
290+
// Split it into "nd_point_and_double_ending" and "tar.gz":
291+
let mut iter = ext.splitn(2, '.');
292+
iter.next();
293+
294+
let ext = iter.next().unwrap_or_default();
295+
let ext = if ext.is_empty() {
296+
String::new()
296297
} else {
297-
(stem, format!(".{ext}").to_lowercase())
298-
// Return ("file", ".d_point_and_double_ending.tar.gz")
299-
// which is not perfect but acceptable.
300-
}
298+
format!(".{ext}")
299+
// ".tar.gz"
300+
};
301+
let stem = name
302+
.strip_suffix(&ext)
303+
.unwrap_or_default()
304+
.chars()
305+
.take(64)
306+
.collect();
307+
(stem, ext.to_lowercase())
301308
}
302309

303310
/// Checks whether a name is a valid blob name.
@@ -963,6 +970,19 @@ mod tests {
963970
assert!(!stem.contains(':'));
964971
assert!(!stem.contains('*'));
965972
assert!(!stem.contains('?'));
973+
974+
let (stem, ext) = BlobObject::sanitise_name(
975+
"file.with_lots_of_characters_behind_point_and_double_ending.tar.gz",
976+
);
977+
assert_eq!(
978+
stem,
979+
"file.with_lots_of_characters_behind_point_and_double_ending"
980+
);
981+
assert_eq!(ext, ".tar.gz");
982+
983+
let (stem, ext) = BlobObject::sanitise_name("a. tar.tar.gz");
984+
assert_eq!(stem, "a. tar");
985+
assert_eq!(ext, ".tar.gz");
966986
}
967987

968988
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]

src/receive_imf/tests.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,20 +3163,21 @@ Reply from different address
31633163
}
31643164

31653165
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3166-
async fn test_long_and_duplicated_filenames() -> Result<()> {
3166+
async fn test_weird_and_duplicated_filenames() -> Result<()> {
31673167
let mut tcm = TestContextManager::new();
31683168
let alice = tcm.alice().await;
31693169
let bob = tcm.bob().await;
31703170

31713171
for filename_sent in &[
31723172
"foo.bar very long file name test baz.tar.gz",
3173-
"foobarabababababababbababababverylongfilenametestbaz.tar.gz",
3173+
"foo.barabababababababbababababverylongfilenametestbaz.tar.gz",
31743174
"fooo...tar.gz",
31753175
"foo. .tar.gz",
31763176
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.tar.gz",
31773177
"a.tar.gz",
31783178
"a.tar.gz",
31793179
"a.a..a.a.a.a.tar.gz",
3180+
"a. tar.tar.gz",
31803181
] {
31813182
let attachment = alice.blobdir.join(filename_sent);
31823183
let content = format!("File content of {filename_sent}");

0 commit comments

Comments
 (0)