diff --git a/Cargo.lock b/Cargo.lock index e73d7b9..b1a9a53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,7 @@ dependencies = [ "log", "mime_guess", "once_cell", + "percent-encoding", "rustls", "url", ] diff --git a/Cargo.toml b/Cargo.toml index b54e940..0ec5f6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ getopts = "0.2.21" log = "0.4" mime_guess = "2.0" once_cell = "1.4" +percent-encoding = "2.1" rustls = "0.19.0" url = "2.1" diff --git a/src/main.rs b/src/main.rs index f97a563..06d98f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use async_std::{ }; use async_tls::TlsAcceptor; use once_cell::sync::Lazy; +use percent_encoding::{AsciiSet, CONTROLS, percent_decode_str, percent_encode}; use rustls::{ internal::pemfile::{certs, pkcs8_private_keys}, NoClientAuth, ServerConfig, @@ -181,7 +182,9 @@ async fn parse_request( async fn send_response(url: Url, stream: &mut W) -> Result { let mut path = std::path::PathBuf::from(&ARGS.content_dir); if let Some(segments) = url.path_segments() { - path.extend(segments); + for segment in segments { + path.push(&*percent_decode_str(segment).decode_utf8()?); + } } if async_std::fs::metadata(&path).await?.is_dir() { if url.path().ends_with('/') || url.path().is_empty() { @@ -226,6 +229,7 @@ async fn send_header(stream: &mut W, status: &str, meta: &[&st } async fn list_directory(stream: &mut W, path: &Path) -> Result { + const WHITESPACE: AsciiSet = CONTROLS.add(b' '); log::info!("Listing directory {:?}", path); send_text_gemini_header(stream).await?; let mut entries = async_std::fs::read_dir(path).await?; @@ -239,7 +243,12 @@ async fn list_directory(stream: &mut W, path: &Path) -> Result if entry.file_type().await?.is_dir() { name += "/"; } - lines.push(format!("=> {}\n", name)); + if name.contains(char::is_whitespace) { + let url = percent_encode(name.as_bytes(), &WHITESPACE); + lines.push(format!("=> {} {}\n", url, name)); + } else { + lines.push(format!("=> {}\n", name)); + } } lines.sort(); for line in lines {