Repository: sigoden/dufs (Rust)

Issue:
Panics on PROPFIND //
Thank you for making dufs! I just found it and set it up to use with Joplin and it's working a treat.
Glad to have a simple Rust webdav server available.

**Problem**

dufs panics when getting a PROPFIND request against `//`. (PROPFIND against `/` works.)
I consider the client sending a // to be buggy and will look to fix it upstream and have it send a `/`
but I would expect dufs not to panic and either handle the request or return an error.

I debugged this locally and it looks like the problem is in or around `extract_path`:
when the incoming request is for `//`, we attempt to [`join`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join)
an absolute path onto `self.args.path`.
Because it's an absolute path it is returned as-is without `self.args.path` at the front.

**Log**

If applicable, add logs to help explain your problem.

Setup:
```
[aneesh@cheetah dufs]$ mkdir empty && dufs empty --allow-all
```

Works with `/`:
```
[aneesh@cheetah dufs]$ curl -X PROPFIND http://localhost:5000/
<?xml version="1.0" encoding="utf-8" ?>
<D:multistatus xmlns:D="DAV:">
<D:response>
<D:href>/</D:href>
<D:propstat>
<D:prop>
<D:displayname></D:displayname>
<D:getlastmodified>Wed, 09 Nov 2022 16:37:07 +0000</D:getlastmodified>
<D:resourcetype><D:collection/></D:resourcetype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>%
[aneesh@cheetah dufs]$
```
with a regular log line in the dufs output:
```
2022-11-09T11:37:18-05:00 INFO - 127.0.0.1 "PROPFIND /" 207
```

However, dufs panics on `//`:
```
[aneesh@cheetah dufs]$ curl -X PROPFIND http://localhost:5000//
curl: (52) Empty reply from server
[aneesh@cheetah dufs]$
```
with the following log output:
```
thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: StripPrefixError(())', src/server.rs:939:53
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

**Environment:**
 - Dufs version: dufs 0.30.0 (both with [dufs-v0.30.0-x86_64-unknown-linux-musl.tar.gz](https://github.com/sigoden/dufs/releases/download/v0.30.0/dufs-v0.30.0-x86_64-unknown-linux-musl.tar.gz) and built from git on the current tip dbf2de9cb9216c554788b1bcc01d3d44d3c4ad23)
 - Browser/Webdav Info: Originally encountered with [Joplin](https://joplinapp.org/) during sync but reproducable with curl as above
 - OS Info: Arch Linux



Discussion:

Patch:
diff --git a/src/server.rs b/src/server.rs
index 595721f8c..a32b2195c 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -897,7 +897,11 @@ impl Server {
     }
 
     fn extract_path(&self, path: &str) -> Option<PathBuf> {
-        let decoded_path = decode_uri(&path[1..])?;
+        let mut slash_stripped_path = path;
+        while let Some(p) = slash_stripped_path.strip_prefix('/') {
+            slash_stripped_path = p
+        }
+        let decoded_path = decode_uri(slash_stripped_path)?;
         let slashes_switched = if cfg!(windows) {
             decoded_path.replace('/', "\\")
         } else {


Test patch:
diff --git a/tests/webdav.rs b/tests/webdav.rs
index 04b4ae047..36261fef2 100644
--- a/tests/webdav.rs
+++ b/tests/webdav.rs
@@ -47,6 +47,13 @@ fn propfind_404(server: TestServer) -> Result<(), Error> {
     Ok(())
 }
 
+#[rstest]
+fn propfind_double_slash(server: TestServer) -> Result<(), Error> {
+    let resp = fetch!(b"PROPFIND", format!("{}/", server.url())).send()?;
+    assert_eq!(resp.status(), 207);
+    Ok(())
+}
+
 #[rstest]
 fn propfind_file(server: TestServer) -> Result<(), Error> {
     let resp = fetch!(b"PROPFIND", format!("{}test.html", server.url())).send()?;