From a83f8cc00b184b83a510ea43434c03872fe79118 Mon Sep 17 00:00:00 2001 From: pebbleKite Date: Thu, 8 Nov 2018 14:41:31 +0200 Subject: add option to ignore symlinks --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/main.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f9f3da..81b872e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -711,6 +711,8 @@ dependencies = [ "actix-web 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index bfd0180..82a1019 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,5 @@ actix = "0.7" actix-web = "0.7" simplelog = "0.5" base64 = "0.10" +percent-encoding = "1.0.1" +htmlescape = "0.3.1" diff --git a/src/main.rs b/src/main.rs index 29bb468..127d364 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ extern crate actix; extern crate actix_web; extern crate base64; +extern crate htmlescape; +extern crate percent_encoding; extern crate simplelog; extern crate yansi; #[macro_use] @@ -9,10 +11,13 @@ extern crate clap; use actix_web::http::header; use actix_web::middleware::{Middleware, Response}; use actix_web::{fs, middleware, server, App, HttpMessage, HttpRequest, HttpResponse, Result}; +use htmlescape::encode_minimal as escape_html_entity; +use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; use simplelog::{Config, LevelFilter, TermLogger}; +use std::fmt::Write as FmtWrite; use std::io::{self, Write}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::thread; use std::time::Duration; use yansi::{Color, Paint}; @@ -36,6 +41,7 @@ pub struct MiniserveConfig { interfaces: Vec, auth: Option, path_explicitly_chosen: bool, + no_symlinks: bool, } /// Decode a HTTP basic auth string into a tuple of username and password. @@ -85,7 +91,7 @@ fn is_valid_auth(auth: String) -> Result<(), String> { } pub fn parse_args() -> MiniserveConfig { - use clap::{App, Arg, AppSettings}; + use clap::{App, AppSettings, Arg}; let matches = App::new(crate_name!()) .version(crate_version!()) @@ -127,9 +133,15 @@ pub fn parse_args() -> MiniserveConfig { .validator(is_valid_auth) .help("Set authentication (username:password)") .takes_value(true), + ).arg( + Arg::with_name("no-symlinks") + .short("P") + .long("no-symlinks") + .help("Do not follow symbolic symbolic links"), ).get_matches(); let verbose = matches.is_present("verbose"); + let no_symlinks = matches.is_present("no-symlinks"); let path = matches.value_of("PATH"); let port = matches.value_of("port").unwrap().parse().unwrap(); let interfaces = if let Some(interfaces) = matches.values_of("interfaces") { @@ -161,6 +173,7 @@ pub fn parse_args() -> MiniserveConfig { interfaces, auth, path_explicitly_chosen: path.is_some(), + no_symlinks, } } @@ -172,13 +185,19 @@ fn file_handler(req: &HttpRequest) -> Result { fn configure_app(app: App) -> App { let s = { let path = &app.state().path; + let no_symlinks = app.state().no_symlinks; if path.is_file() { None } else { + let res = fs::StaticFiles::new(path) + .expect("Couldn't create path") + .show_files_listing(); Some( - fs::StaticFiles::new(path) - .expect("Couldn't create path") - .show_files_listing(), + if no_symlinks { + res.files_listing_renderer(no_symlink_directory_listing) + } else { + res + } ) } }; @@ -235,6 +254,19 @@ fn main() { } let miniserve_config = parse_args(); + if miniserve_config.no_symlinks && miniserve_config + .path + .symlink_metadata() + .expect("Can't get file metadata") + .file_type() + .is_symlink() + { + println!( + "{error} The no-symlinks option cannot be used with a symlink path", + error = Paint::red("error:").bold(), + ); + return; + } if miniserve_config.verbose { let _ = TermLogger::init(LevelFilter::Info, Config::default()); @@ -324,3 +356,55 @@ fn main() { let _ = sys.run(); } + +// ↓ Adapted from https://docs.rs/actix-web/0.7.13/src/actix_web/fs.rs.html#564 +fn no_symlink_directory_listing( + dir: &fs::Directory, + req: &HttpRequest, +) -> Result { + let index_of = format!("Index of {}", req.path()); + let mut body = String::new(); + let base = Path::new(req.path()); + + for entry in dir.path.read_dir()? { + if dir.is_visible(&entry) { + let entry = entry.unwrap(); + let p = match entry.path().strip_prefix(&dir.path) { + Ok(p) => base.join(p), + Err(_) => continue, + }; + // show file url as relative to static path + let file_url = + utf8_percent_encode(&p.to_string_lossy(), DEFAULT_ENCODE_SET).to_string(); + // " -- " & -- & ' -- ' < -- < > -- > + let file_name = escape_html_entity(&entry.file_name().to_string_lossy()); + + // if file is a directory, add '/' to the end of the name + if let Ok(metadata) = entry.metadata() { + if metadata.file_type().is_symlink() { + continue; + } + if metadata.is_dir() { + let _ = write!(body, "
  • {}/
  • ", file_url, file_name); + } else { + let _ = write!(body, "
  • {}
  • ", file_url, file_name); + } + } else { + continue; + } + } + } + + let html = format!( + "\ + {}\ +

    {}

    \ +
      \ + {}\ +
    \n", + index_of, index_of, body + ); + Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(html)) +} -- cgit v1.2.3 From 8034d4b2604dd3f5dc97c95e7da8e302e0044068 Mon Sep 17 00:00:00 2001 From: pebbleKite Date: Thu, 8 Nov 2018 14:54:04 +0200 Subject: Use same renderer when skipping symlinks and not --- src/main.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 127d364..688958c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -189,15 +189,13 @@ fn configure_app(app: App) -> App { if path.is_file() { None } else { - let res = fs::StaticFiles::new(path) - .expect("Couldn't create path") - .show_files_listing(); Some( - if no_symlinks { - res.files_listing_renderer(no_symlink_directory_listing) - } else { - res - } + fs::StaticFiles::new(path) + .expect("Couldn't create path") + .show_files_listing() + .files_listing_renderer(move |dir, req| { + directory_listing(dir, req, no_symlinks) + }), ) } }; @@ -358,9 +356,10 @@ fn main() { } // ↓ Adapted from https://docs.rs/actix-web/0.7.13/src/actix_web/fs.rs.html#564 -fn no_symlink_directory_listing( +fn directory_listing( dir: &fs::Directory, req: &HttpRequest, + skip_symlinks: bool, ) -> Result { let index_of = format!("Index of {}", req.path()); let mut body = String::new(); @@ -381,7 +380,7 @@ fn no_symlink_directory_listing( // if file is a directory, add '/' to the end of the name if let Ok(metadata) = entry.metadata() { - if metadata.file_type().is_symlink() { + if skip_symlinks && metadata.file_type().is_symlink() { continue; } if metadata.is_dir() { -- cgit v1.2.3 From fc466891148b7340d9588ccbd2bb74db957e3068 Mon Sep 17 00:00:00 2001 From: pebbleKite Date: Sun, 11 Nov 2018 15:53:37 +0200 Subject: Fix double "symbolic" typo --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 688958c..5806cab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -137,7 +137,7 @@ pub fn parse_args() -> MiniserveConfig { Arg::with_name("no-symlinks") .short("P") .long("no-symlinks") - .help("Do not follow symbolic symbolic links"), + .help("Do not follow symbolic links"), ).get_matches(); let verbose = matches.is_present("verbose"); -- cgit v1.2.3