Skip to content

Commit 940b7b2

Browse files
committed
irondrop: fuck AppleDouble Probe
1 parent 0b392e6 commit 940b7b2

2 files changed

Lines changed: 110 additions & 9 deletions

File tree

‎src/webdav.rs‎

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,13 @@ fn handle_propfind(
255255
return Ok(status_response(404, "Not Found"));
256256
}
257257
let effective_depth = DavDepth::Zero;
258-
let (response, resource_count) =
259-
build_propfind_response(base_dir, &target_path, &mode, effective_depth)?;
258+
let (response, resource_count) = build_propfind_response(
259+
base_dir,
260+
&target_path,
261+
&mode,
262+
effective_depth,
263+
context.aggressive_finder_compat_enabled(),
264+
)?;
260265
debug!(
261266
"WebDAV PROPFIND existing noise path served path={} resolved_target={} mode={} resource_count={}",
262267
request.path,
@@ -291,8 +296,13 @@ fn handle_propfind(
291296
effective_depth,
292297
propfind_mode_name(&mode),
293298
);
294-
let (response, resource_count) =
295-
build_propfind_response(base_dir, &target_path, &mode, effective_depth)?;
299+
let (response, resource_count) = build_propfind_response(
300+
base_dir,
301+
&target_path,
302+
&mode,
303+
effective_depth,
304+
context.aggressive_finder_compat_enabled(),
305+
)?;
296306
log_propfind_result(
297307
context,
298308
&request.path,
@@ -521,8 +531,9 @@ fn build_propfind_response(
521531
target_path: &Path,
522532
mode: &PropfindMode,
523533
depth: DavDepth,
534+
filter_noise: bool,
524535
) -> Result<(Response, usize), AppError> {
525-
let resources = collect_propfind_resources(target_path, depth)?;
536+
let resources = collect_propfind_resources(target_path, depth, filter_noise)?;
526537
let resource_count = resources.len();
527538

528539
let mut body = String::from(
@@ -558,6 +569,7 @@ fn build_propfind_response(
558569
fn collect_propfind_resources(
559570
target_path: &Path,
560571
depth: DavDepth,
572+
filter_noise: bool,
561573
) -> Result<Vec<PathBuf>, AppError> {
562574
let mut resources = vec![target_path.to_path_buf()];
563575
if target_path.is_dir() {
@@ -567,13 +579,20 @@ fn collect_propfind_resources(
567579
let mut entries = Vec::new();
568580
for entry in std::fs::read_dir(target_path)? {
569581
let entry = entry?;
570-
entries.push(entry.path());
582+
let path = entry.path();
583+
if filter_noise
584+
&& let Some(file_name) = path.file_name().and_then(|n| n.to_str())
585+
&& crate::utils::is_macos_finder_noise_path(file_name)
586+
{
587+
continue;
588+
}
589+
entries.push(path);
571590
}
572591
entries.sort_by(|a, b| a.to_string_lossy().cmp(&b.to_string_lossy()));
573592
resources.extend(entries);
574593
}
575594
DavDepth::Infinity => {
576-
collect_recursive_resources(target_path, &mut resources)?;
595+
collect_recursive_resources(target_path, &mut resources, filter_noise)?;
577596
}
578597
}
579598
}
@@ -767,13 +786,24 @@ fn parse_prop_name_from_token(
767786
})
768787
}
769788

770-
fn collect_recursive_resources(root: &Path, resources: &mut Vec<PathBuf>) -> Result<(), AppError> {
789+
fn collect_recursive_resources(
790+
root: &Path,
791+
resources: &mut Vec<PathBuf>,
792+
filter_noise: bool,
793+
) -> Result<(), AppError> {
771794
let mut stack = vec![root.to_path_buf()];
772795
while let Some(current_dir) = stack.pop() {
773796
let mut children = Vec::new();
774797
for entry in std::fs::read_dir(&current_dir)? {
775798
let entry = entry?;
776-
children.push((entry.path(), entry.file_type()?));
799+
let path = entry.path();
800+
if filter_noise
801+
&& let Some(file_name) = path.file_name().and_then(|n| n.to_str())
802+
&& crate::utils::is_macos_finder_noise_path(file_name)
803+
{
804+
continue;
805+
}
806+
children.push((path, entry.file_type()?));
777807
}
778808
children.sort_by(|(a, _), (b, _)| a.to_string_lossy().cmp(&b.to_string_lossy()));
779809

‎tests/webdav_options_propfind_test.rs‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,77 @@ fn test_propfind_finder_infinity_depth_is_capped_to_one_in_safe_mode() {
541541
assert!(!body.contains("<D:href>/dir/nested/deeper/</D:href>"));
542542
}
543543

544+
#[test]
545+
fn test_propfind_finder_filters_noise_from_collection_listings_in_aggressive_mode() {
546+
let server = setup_test_server_with_finder_mode(
547+
|root| {
548+
create_dir_all(root.join("dir")).unwrap();
549+
let mut f1 = File::create(root.join("dir").join("child.txt")).unwrap();
550+
writeln!(f1, "real child").unwrap();
551+
let mut f2 = File::create(root.join("dir").join("._child.txt")).unwrap();
552+
writeln!(f2, "apple double").unwrap();
553+
let mut f3 = File::create(root.join("dir").join(".DS_Store")).unwrap();
554+
writeln!(f3, "finder metadata").unwrap();
555+
},
556+
"aggressive",
557+
);
558+
let client = Client::new();
559+
560+
let response = client
561+
.request(
562+
Method::from_bytes(b"PROPFIND").unwrap(),
563+
format!("http://{}/dir/", server.addr),
564+
)
565+
.header("Depth", "1")
566+
.header("User-Agent", FINDER_USER_AGENT)
567+
.header("Content-Type", "application/xml")
568+
.body(allprop_body().to_string())
569+
.send()
570+
.unwrap();
571+
572+
assert_eq!(response.status().as_u16(), 207);
573+
let body = response.text().unwrap();
574+
assert!(body.contains("<D:href>/dir/</D:href>"));
575+
assert!(body.contains("<D:href>/dir/child.txt</D:href>"));
576+
// The noisy files should be completely hidden from the listing
577+
assert!(!body.contains("._child.txt"));
578+
assert!(!body.contains(".DS_Store"));
579+
}
580+
581+
#[test]
582+
fn test_propfind_finder_does_not_filter_noise_from_collection_listings_in_safe_mode() {
583+
let server = setup_test_server_with_finder_mode(
584+
|root| {
585+
create_dir_all(root.join("dir")).unwrap();
586+
let mut f1 = File::create(root.join("dir").join("child.txt")).unwrap();
587+
writeln!(f1, "real child").unwrap();
588+
let mut f2 = File::create(root.join("dir").join("._child.txt")).unwrap();
589+
writeln!(f2, "apple double").unwrap();
590+
},
591+
"safe",
592+
);
593+
let client = Client::new();
594+
595+
let response = client
596+
.request(
597+
Method::from_bytes(b"PROPFIND").unwrap(),
598+
format!("http://{}/dir/", server.addr),
599+
)
600+
.header("Depth", "1")
601+
.header("User-Agent", FINDER_USER_AGENT)
602+
.header("Content-Type", "application/xml")
603+
.body(allprop_body().to_string())
604+
.send()
605+
.unwrap();
606+
607+
assert_eq!(response.status().as_u16(), 207);
608+
let body = response.text().unwrap();
609+
assert!(body.contains("<D:href>/dir/</D:href>"));
610+
assert!(body.contains("<D:href>/dir/child.txt</D:href>"));
611+
// Safe mode preserves literal filesystem layout
612+
assert!(body.contains("._child.txt"));
613+
}
614+
544615
#[test]
545616
fn test_propfind_finder_missing_depth_defaults_to_one_in_safe_mode() {
546617
let server = setup_test_server_with_finder_mode(

0 commit comments

Comments
 (0)