diff --git a/Cargo.lock b/Cargo.lock index 923982c..58a8d94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,6 +899,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "mnemonic" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fae0e4c0b155d3b019a7cbc27abe4a90e15c06814d27889ce9f5f44e2faf77" +dependencies = [ + "byteorder", + "lazy_static", +] + [[package]] name = "ntapi" version = "0.3.7" @@ -1408,6 +1418,7 @@ dependencies = [ "inotify", "log", "mime", + "mnemonic", "pin-project-lite", "rand", "sanitise-file-name", diff --git a/Cargo.toml b/Cargo.toml index fa6bb37..a2379a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ futures-core = "0.3" inotify = "0.10" log = "0.4" mime = "0.3.16" +mnemonic = "1.0.1" pin-project-lite = "0.2.9" rand = "0.8.5" sanitise-file-name = "1.0.0" diff --git a/README.md b/README.md index f63798e..560edbc 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - One-to-many transfer of large files - Simple web UI for sender and receivers +- Word-based download codes that are easy to remember and communicate - Receivers can begin downloading *immediately*, without needing to wait for the upload to finish - Receivers can also download after the upload is complete and the @@ -29,6 +30,9 @@ transbeam is configured with the following environment variables: being uploaded (default: 16GiB) - `TRANSBEAM_MAX_STORAGE_SIZE`: maximum total size, in bytes, of all files being stored by transbeam (default: 64GiB) +- `TRANSBEAM_MNEMONIC_CODES`: generate memorable download codes using + English words, rather than random alphanumeric strings (default: + true) - `RUST_LOG`: log levels, for the app as a whole and/or for specific submodules/libraries. See [`env_logger`](https://docs.rs/env_logger/latest/env_logger/)'s @@ -58,5 +62,3 @@ cargo run --release - uploader auth - downloader auth -- more readable download codes? -- proper error display in the web ui diff --git a/src/main.rs b/src/main.rs index a9d27ac..7aef76b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ mod download; mod store; mod upload; -mod util; mod zip; use std::{fs::File, path::PathBuf}; @@ -25,7 +24,7 @@ async fn handle_download( data: AppData, ) -> actix_web::Result { let file_code = path.into_inner(); - if !util::is_ascii_alphanumeric(&file_code) { + if !store::is_valid_storage_code(&file_code) { return Ok(HttpResponse::NotFound().finish()); } let data = data.read().await; diff --git a/src/store.rs b/src/store.rs index 5966e01..6685d31 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, io::ErrorKind, path::PathBuf, str::FromStr}; use log::{debug, error, info, warn}; +use rand::{distributions::{Alphanumeric, DistString}, thread_rng, Rng}; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use tokio::{ @@ -15,6 +16,17 @@ const GIGA: u64 = 1024*1024*1024; const DEFAULT_MAX_UPLOAD_SIZE: u64 = 16*GIGA; const DEFAULT_MAX_STORAGE_SIZE: u64 = 64*GIGA; +pub fn gen_storage_code() -> String { + if std::env::var("TRANSBEAM_MNEMONIC_CODES").as_deref() == Ok("false") { + Alphanumeric.sample_string(&mut thread_rng(), 8) + } else { + mnemonic::to_string(thread_rng().gen::<[u8; 4]>()) + } +} + +pub fn is_valid_storage_code(s: &str) -> bool { + s.as_bytes().iter().all(|c| c.is_ascii_alphanumeric() || c == &b'-') +} pub(crate) fn storage_dir() -> PathBuf { PathBuf::from(std::env::var("TRANSBEAM_STORAGE_DIR").unwrap_or_else(|_| String::from(DEFAULT_STORAGE_DIR))) @@ -133,7 +145,7 @@ impl FileStore { // Handle this case separately, because we don't // want to try to delete it if it's not the sort // of path we're expecting - if !crate::util::is_ascii_alphanumeric(&key) { + if !is_valid_storage_code(&key) { error!("Invalid key in persistent storage: {}", key); continue; } diff --git a/src/upload.rs b/src/upload.rs index 5b1bd59..16bbda1 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -5,7 +5,6 @@ use actix_http::ws::{CloseReason, Item}; use actix_web_actors::ws::{self, CloseCode}; use bytes::Bytes; use log::{debug, error, info, trace}; -use rand::distributions::{Alphanumeric, DistString}; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use unicode_normalization::UnicodeNormalization; @@ -68,7 +67,7 @@ impl Uploader { pub(crate) fn new(app_data: super::AppData) -> Self { Self { writer: None, - storage_filename: Alphanumeric.sample_string(&mut rand::thread_rng(), 8), + storage_filename: store::gen_storage_code(), app_data, bytes_remaining: 0, } diff --git a/src/util.rs b/src/util.rs deleted file mode 100644 index bcc5caf..0000000 --- a/src/util.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) fn is_ascii_alphanumeric(s: &str) -> bool { - s.as_bytes().iter().all(|c| c.is_ascii_alphanumeric()) -}