diff --git a/Cargo.lock b/Cargo.lock index 6b390a3..b6c4267 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,6 +309,7 @@ dependencies = [ "tokio-rustls", "tokio-stream", "tokio-util", + "urlencoding", "uuid", ] @@ -1154,6 +1155,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "urlencoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" + [[package]] name = "uuid" version = "1.1.1" diff --git a/Cargo.toml b/Cargo.toml index d67c15e..81028c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ rustls-pemfile = "1" md5 = "0.7.0" lazy_static = "1.4.0" uuid = { version = "1.1.1", features = ["v4", "fast-rng"] } +urlencoding = "2.1.0" [profile.release] lto = true diff --git a/src/args.rs b/src/args.rs index 55e70e8..c9c4e77 100644 --- a/src/args.rs +++ b/src/args.rs @@ -143,7 +143,7 @@ impl Args { let uri_prefix = if path_prefix.is_empty() { "/".to_owned() } else { - format!("/{}/", &path_prefix) + format!("/{}/", encode_uri(&path_prefix)) }; let cors = matches.is_present("cors"); let auth = match matches.value_of("auth") { @@ -237,3 +237,8 @@ pub fn load_private_key(filename: &str) -> BoxResult { } Ok(PrivateKey(keys[0].to_owned())) } + +pub fn encode_uri(v: &str) -> String { + let parts: Vec<_> = v.split('/').map(urlencoding::encode).collect(); + parts.join("/") +} diff --git a/src/main.rs b/src/main.rs index 30eb929..793403d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ mod server; pub type BoxResult = Result>; -use crate::args::{matches, Args}; +use crate::args::{encode_uri, matches, Args}; use crate::server::serve; #[tokio::main] diff --git a/src/server.rs b/src/server.rs index 2bbfd5c..d175453 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,5 +1,5 @@ use crate::auth::{generate_www_auth, valid_digest}; -use crate::{Args, BoxResult}; +use crate::{encode_uri, Args, BoxResult}; use async_walkdir::WalkDir; use async_zip::write::{EntryOptions, ZipFileWriter}; @@ -370,7 +370,11 @@ impl InnerService { *res.body_mut() = Body::wrap_stream(stream); res.headers_mut().insert( CONTENT_DISPOSITION, - HeaderValue::from_str(&format!("attachment; filename=\"{}.zip\"", filename,)).unwrap(), + HeaderValue::from_str(&format!( + "attachment; filename=\"{}.zip\"", + encode_uri(filename), + )) + .unwrap(), ); Ok(()) } @@ -821,7 +825,10 @@ impl PathItem { HTTP/1.1 200 OK "#, - prefix, self.name, self.base_name, mtime + prefix, + encode_uri(&self.name), + urlencoding::encode(&self.base_name), + mtime ), PathType::File | PathType::SymlinkFile => format!( r#" @@ -837,8 +844,8 @@ impl PathItem { "#, prefix, - self.name, - self.base_name, + encode_uri(&self.name), + urlencoding::encode(&self.base_name), self.size.unwrap_or_default(), mtime ),