diff --git a/Cargo.lock b/Cargo.lock index 7fcff0c..c678a51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -563,28 +563,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - [[package]] name = "flate2" version = "1.0.30" @@ -795,7 +773,6 @@ dependencies = [ "futures", "log", "percent-encoding", - "tar", "v_htmlescape", ] @@ -876,15 +853,9 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "local-channel" @@ -1007,7 +978,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall", "smallvec", "windows-targets 0.52.5", ] @@ -1102,15 +1073,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.1" @@ -1164,19 +1126,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "ryu" version = "1.0.18" @@ -1311,17 +1260,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tar" -version = "0.4.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" -dependencies = [ - "filetime", - "libc", - "xattr", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -1674,17 +1612,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" -[[package]] -name = "xattr" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" -dependencies = [ - "libc", - "linux-raw-sys", - "rustix", -] - [[package]] name = "zerocopy" version = "0.7.34" diff --git a/Cargo.toml b/Cargo.toml index 7262061..23e0de9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ clap = { version = "3.2.20", features = ["cargo"] } env_logger = "*" log = "*" futures = "0.3.24" -tar = "0.4.38" percent-encoding = "2.2" v_htmlescape = "0.15" diff --git a/src/directory_listing.rs b/src/directory_listing.rs index 4b8c916..7f849b8 100644 --- a/src/directory_listing.rs +++ b/src/directory_listing.rs @@ -27,46 +27,56 @@ pub fn directory_listing( 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) if cfg!(windows) => base.join(p).to_string_lossy().replace('\\', "/"), - Ok(p) => base.join(p).to_string_lossy().into_owned(), - Err(_) => continue, - }; + let mut paths: Vec<_> = dir.path.read_dir()?.map(|r| r.unwrap()).collect(); + paths.sort_by(|a, b| { + let (oan, obn) = (a.file_name(), b.file_name()); + let (an, bn) = (oan.to_str().unwrap(), obn.to_str().unwrap()); - // if file is a directory, add '/' to the end of the name - if let Ok(metadata) = entry.metadata() { - if metadata.is_dir() { - let _ = write!( - body, - "📂 {}/ [.tar]", - encode_file_url!(p), - encode_file_name!(entry), - encode_file_url!(p), - ); - } else { - let _ = write!( - body, - "🗎 {} {}", - encode_file_url!(p), - encode_file_name!(entry), - metadata.len(), - ); - } + if let (Ok(am), Ok(bm)) = (a.metadata(), b.metadata()) { + return bm.is_dir().cmp(&am.is_dir()).then(an.cmp(bn)); + } else { + return an.cmp(bn); + } + }); + + for entry in paths { + let p = match entry.path().strip_prefix(&dir.path) { + Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace('\\', "/"), + Ok(p) => base.join(p).to_string_lossy().into_owned(), + Err(_) => continue, + }; + + if !base.has_root() { + let _ = write!( + body, + "../Size\n" + ); + } + + // if file is a directory, add '/' to the end of the name + if let Ok(metadata) = entry.metadata() { + if metadata.is_dir() { + let _ = write!( + body, + "{}/ dir", + encode_file_url!(p), + encode_file_name!(entry), + ); } else { - continue; + let _ = write!( + body, + "{} {}", + encode_file_url!(p), + encode_file_name!(entry), + metadata.len(), + ); } + } else { + continue; } } - let header = format!( - "

Index of {}/

\n\ - [.tar of whole directory]", - index_of, - if index_of.is_empty() { "_" } else { index_of } - ); + let header = format!("

Index of {}/

\n", index_of); let footer = format!( r#""#, @@ -81,11 +91,10 @@ pub fn directory_listing( "\n\ \n\ \n\ - Index of {}\n\ + Index of {}/\n\ \n\ \n{}\n\ \n\ - \n\ {}\
📁 ../Size
\n\ {}\ diff --git a/src/favicon.png b/src/favicon.png index edb8b85..3ef7f6b 100644 Binary files a/src/favicon.png and b/src/favicon.png differ diff --git a/src/main.rs b/src/main.rs index ab10c49..5ac9876 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ mod directory_listing; -mod threaded_archiver; mod web; #[actix_web::main] diff --git a/src/style.css b/src/style.css index cc50398..ed5b852 100644 --- a/src/style.css +++ b/src/style.css @@ -1,14 +1,35 @@ -body {background-color: #282f34; color: #aeb9a6; font-family:monospace; font-size:10.5pt;} -a { color: #adad9d; } -footer a {color: darkgray; text-decoration:none; font-size:smaller} -h1 {margin-bottom: 0} -table td:nth-child(2) {text-align:right} +body { + background-color: #282f34; + color: #aeb9a6; + font-family: monospace; + font-size: 10.5pt; +} + +a { + color: #adad9d; +} + +footer a { + color: darkgray; + text-decoration: none; + font-size: smaller +} + +h1 { + margin-bottom: 0 +} + +table td:nth-child(2) { + text-align: right +} + table { - width:100%; - margin: 1em auto; + width: 100%; + max-width: 35em; + margin: 1em 0; padding: 0.5em 0; border-top: 1px; border-bottom: 1px; border-color: #444; border-style: solid none; -} +} \ No newline at end of file diff --git a/src/threaded_archiver.rs b/src/threaded_archiver.rs deleted file mode 100644 index 89bdf05..0000000 --- a/src/threaded_archiver.rs +++ /dev/null @@ -1,57 +0,0 @@ -use futures::prelude::*; - -use std::io; -use std::path::Path; -use std::thread; - -/* - * TODO: - * don't tar hidden files - */ - -type Stream = futures::channel::mpsc::Receiver; -type Sender = futures::channel::mpsc::Sender; - -pub fn stream_tar_in_thread

(path: P) -> Stream -where - P: AsRef + Send + 'static, -{ - let (writer, stream) = StreamWriter::new(64); - - thread::spawn(move || { - let mut a = tar::Builder::new(writer); - let last_path_component = path.as_ref().file_name().unwrap(); - a.mode(tar::HeaderMode::Deterministic); - a.append_dir_all(last_path_component, &path) - .unwrap_or_else(|e| println!("{}", e)); - a.finish().unwrap_or_else(|e| println!("{}", e)); - }); - stream -} - -struct StreamWriter { - tx: Sender, -} - -impl StreamWriter { - fn new(size: usize) -> (Self, Stream) { - let (tx, rx) = futures::channel::mpsc::channel(size); - (StreamWriter { tx }, rx) - } -} - -impl io::Write for StreamWriter { - fn write(&mut self, data: &[u8]) -> io::Result { - let len = data.len(); - futures::executor::block_on(async move { - let buf = bytes::Bytes::copy_from_slice(data); - self.tx.send(buf).await.ok(); // maybe propagate any errors back - }); - Ok(len) - } - - fn flush(&mut self) -> io::Result<()> { - futures::executor::block_on(async move { self.tx.flush().await.ok() }); - Ok(()) - } -} diff --git a/src/web.rs b/src/web.rs index 593cbab..92c5fd6 100644 --- a/src/web.rs +++ b/src/web.rs @@ -1,8 +1,8 @@ -use actix_files::{Files, NamedFile}; +use actix_files::Files; use actix_web::{ - get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder, + dev::{fn_service, ServiceRequest, ServiceResponse}, + get, middleware, App, HttpResponse, HttpServer, Responder, }; -use futures::StreamExt; use std::path::PathBuf; @@ -10,15 +10,41 @@ pub async fn run(bind_addr: &str, root: &PathBuf) -> std::io::Result<()> { let root_ = root.clone(); let s = HttpServer::new(move || { let static_files = Files::new("/", &root_) + .prefer_utf8(true) + .index_file("index.html") + .use_hidden_files() .show_files_listing() .redirect_to_slash_directory() - .files_listing_renderer(crate::directory_listing::directory_listing); + .files_listing_renderer(crate::directory_listing::directory_listing) + .default_handler(fn_service(|req: ServiceRequest| async { + let (req, _) = req.into_parts(); + let style = include_str!("style.css"); + + let html = format!( + "\n\ + \n\ + \n\ + Error\n\ + \n\ + \n\ +

Error

\ +

File not found

\ + \n", + style + ); + + Ok(ServiceResponse::new( + req, + HttpResponse::NotFound() + .content_type("text/html; charset=utf-8") + .body(html), + )) + })); App::new() .app_data(root_.clone()) .wrap(middleware::Logger::default()) .service(favicon_ico) - .service(handle_tar) .service(static_files) }) .bind(bind_addr)? @@ -28,37 +54,6 @@ pub async fn run(bind_addr: &str, root: &PathBuf) -> std::io::Result<()> { s.await } -#[get("/{tail:.*}.tar")] -async fn handle_tar( - req: HttpRequest, - root: web::Data, - tail: web::Path, -) -> impl Responder { - let relpath = PathBuf::from(tail.trim_end_matches('/')); - let fullpath = root.join(&relpath).canonicalize().unwrap(); - - // if a .tar already exists, just return it as-is - let mut fullpath_tar = fullpath.clone(); - fullpath_tar.set_extension("tar"); - if fullpath_tar.is_file() { - return NamedFile::open_async(fullpath_tar) - .await - .unwrap() - .into_response(&req); - } - - if !(fullpath.is_dir()) { - return HttpResponse::NotFound().body("Directory not found\n"); - } - - let stream = crate::threaded_archiver::stream_tar_in_thread(fullpath).map(Ok::<_, Error>); - let response = HttpResponse::Ok() - .content_type("application/x-tar") - .streaming(stream); - - response -} - const FAVICON_ICO: &[u8] = include_bytes!("favicon.png"); #[get("/favicon.ico")]