diff --git a/src/main.rs b/src/main.rs index da58653..7aef76b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,6 @@ use actix_web::{ }; use actix_web_actors::ws; use log::error; -use serde::Deserialize; use store::FileStore; use tokio::sync::RwLock; @@ -18,25 +17,20 @@ const APP_NAME: &str = "transbeam"; type AppData = web::Data>; -#[derive(Deserialize)] -struct DownloadRequest { - code: String, -} - -#[get("/download")] +#[get("/download/{file_code}")] async fn handle_download( req: HttpRequest, - download: web::Query, + path: web::Path, data: AppData, ) -> actix_web::Result { - let code = &download.code; - if !store::is_valid_storage_code(code) { + let file_code = path.into_inner(); + if !store::is_valid_storage_code(&file_code) { return Ok(HttpResponse::NotFound().finish()); } let data = data.read().await; - let info = data.lookup_file(code); + let info = data.lookup_file(&file_code); if let Some(info) = info { - let storage_path = store::storage_dir().join(code); + let storage_path = store::storage_dir().join(file_code); let file = File::open(&storage_path)?; Ok(download::DownloadingFile { file, @@ -61,9 +55,8 @@ async fn main() -> std::io::Result<()> { let data: AppData = web::Data::new(RwLock::new(FileStore::load().await?)); start_reaper(data.clone()); - let static_dir = PathBuf::from( - std::env::var("TRANSBEAM_STATIC_DIR").unwrap_or_else(|_| String::from("static")), - ); + let static_dir = + PathBuf::from(std::env::var("TRANSBEAM_STATIC_DIR").unwrap_or_else(|_| String::from("static"))); let port = std::env::var("TRANSBEAM_PORT") .ok() .and_then(|p| p.parse::().ok()) diff --git a/src/store.rs b/src/store.rs index 319f988..6685d31 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,10 +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 rand::{distributions::{Alphanumeric, DistString}, thread_rng, Rng}; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use tokio::{ @@ -15,9 +12,9 @@ use tokio::{ const STATE_FILE_NAME: &str = "files.json"; const DEFAULT_STORAGE_DIR: &str = "storage"; const DEFAULT_MAX_LIFETIME: u32 = 30; -const GIGA: u64 = 1024 * 1024 * 1024; -const DEFAULT_MAX_UPLOAD_SIZE: u64 = 16 * GIGA; -const DEFAULT_MAX_STORAGE_SIZE: u64 = 64 * GIGA; +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") { @@ -28,23 +25,15 @@ pub fn gen_storage_code() -> String { } pub fn is_valid_storage_code(s: &str) -> bool { - s.as_bytes() - .iter() - .all(|c| c.is_ascii_alphanumeric() || c == &b'-') + 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)), - ) + PathBuf::from(std::env::var("TRANSBEAM_STORAGE_DIR").unwrap_or_else(|_| String::from(DEFAULT_STORAGE_DIR))) } fn parse_env_var(var: &str, default: T) -> T { - std::env::var(var) - .ok() - .and_then(|val| val.parse::().ok()) - .unwrap_or(default) + std::env::var(var).ok().and_then(|val| val.parse::().ok()).unwrap_or(default) } pub(crate) fn max_lifetime() -> u32 { @@ -205,9 +194,7 @@ impl FileStore { ) -> std::io::Result> { let remaining_size = max_total_size().saturating_sub(self.total_size()); let allowed_size = std::cmp::min(remaining_size, max_single_size()); - if file.size > allowed_size { - return Ok(Err(allowed_size)); - } + if file.size > allowed_size { return Ok(Err(allowed_size)); } self.0.insert(key, file); self.save().await.map(Ok) } @@ -225,7 +212,7 @@ impl FileStore { pub(crate) async fn remove_expired_files(&mut self) -> std::io::Result<()> { info!("Checking for expired files"); let now = OffsetDateTime::now_utc(); - for (key, file) in std::mem::take(&mut self.0).into_iter() { + for (key, file) in std::mem::replace(&mut self.0, HashMap::new()).into_iter() { if file.expiry > now { self.0.insert(key, file); } else { diff --git a/src/upload.rs b/src/upload.rs index 2083807..16bbda1 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use unicode_normalization::UnicodeNormalization; -use crate::store::{self, storage_dir, StoredFile}; +use crate::store::{storage_dir, StoredFile, self}; const MAX_FILES: usize = 256; const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] = @@ -44,14 +44,14 @@ impl Error { match self { Self::Storage(_) => CloseCode::Error, Self::Parse(_) - | Self::UnexpectedMessageType - | Self::ClosedEarly(_) - | Self::UnexpectedExtraData => CloseCode::Invalid, + | Self::UnexpectedMessageType + | Self::ClosedEarly(_) + | Self::UnexpectedExtraData => CloseCode::Invalid, Self::DuplicateFilename - | Self::NoFiles - | Self::TooManyFiles - | Self::TooLong - | Self::TooBig(_) => CloseCode::Policy, + | Self::NoFiles + | Self::TooManyFiles + | Self::TooLong + | Self::TooBig(_) => CloseCode::Policy, } } } @@ -132,15 +132,9 @@ enum ServerMessage { impl From<&Error> for ServerMessage { fn from(e: &Error) -> Self { match e { - Error::TooBig(max_size) => ServerMessage::TooBig { - max_size: *max_size, - }, - Error::TooLong => ServerMessage::TooLong { - max_days: store::max_lifetime(), - }, - _ => ServerMessage::Error { - details: e.to_string(), - }, + Error::TooBig(max_size) => ServerMessage::TooBig { max_size: *max_size }, + Error::TooLong => ServerMessage::TooLong { max_days: store::max_lifetime() }, + _ => ServerMessage::Error { details: e.to_string() }, } } } @@ -197,17 +191,18 @@ impl Uploader { self.cleanup_after_error(ctx); } - fn handle_message(&mut self, msg: ws::Message, ctx: &mut Context) -> Result { + fn handle_message( + &mut self, + msg: ws::Message, + ctx: &mut Context, + ) -> Result { trace!("Websocket message: {:?}", msg); match msg { ws::Message::Text(text) => { if self.writer.is_some() { return Err(Error::UnexpectedMessageType); } - let UploadManifest { - files: raw_files, - lifetime, - } = serde_json::from_slice(text.as_bytes())?; + let UploadManifest { files: raw_files, lifetime, } = serde_json::from_slice(text.as_bytes())?; if lifetime > store::max_lifetime() { return Err(Error::TooLong); } @@ -240,15 +235,19 @@ impl Uploader { .write(true) .create_new(true) .open(&storage_path)?; - let (writer, name, size, modtime): (Box, _, _, _) = if files.len() > 1 { + let (writer, name, size, modtime): (Box,_,_,_) = if files.len() > 1 { info!("Wrapping in zipfile generator"); let now = OffsetDateTime::now_utc(); let zip_writer = super::zip::ZipGenerator::new(files, writer); let size = zip_writer.total_size(); - let download_filename = super::APP_NAME.to_owned() - + &now.format(FILENAME_DATE_FORMAT).unwrap() - + ".zip"; - (Box::new(zip_writer), download_filename, size, now) + let download_filename = + super::APP_NAME.to_owned() + &now.format(FILENAME_DATE_FORMAT).unwrap() + ".zip"; + ( + Box::new(zip_writer), + download_filename, + size, + now, + ) } else { ( Box::new(writer), @@ -262,29 +261,23 @@ impl Uploader { name, size, modtime, - expiry: OffsetDateTime::now_utc() + lifetime * time::Duration::DAY, + expiry: OffsetDateTime::now_utc() + lifetime*time::Duration::DAY, }; let data = self.app_data.clone(); let storage_filename = self.storage_filename.clone(); - ctx.spawn( - actix::fut::wrap_future(async move { - debug!("Spawned future to add entry {} to state", storage_filename); - data.write() - .await - .add_file(storage_filename, stored_file) - .await - }) - .map(|res, u: &mut Self, ctx: &mut Context| match res { - Ok(Ok(())) => ctx.text( - serde_json::to_string(&ServerMessage::Ready { - code: u.storage_filename.clone(), - }) - .unwrap(), - ), + ctx.spawn(actix::fut::wrap_future(async move { + debug!("Spawned future to add entry {} to state", storage_filename); + data.write() + .await + .add_file(storage_filename, stored_file) + .await + }).map(|res, u: &mut Self, ctx: &mut Context| { + match res { + Ok(Ok(())) => ctx.text(serde_json::to_string(&ServerMessage::Ready { code: u.storage_filename.clone() }).unwrap()), Ok(Err(size)) => u.notify_error_and_cleanup(Error::TooBig(size), ctx), - Err(e) => u.notify_error_and_cleanup(Error::from(e), ctx), - }), - ); + Err(e) => u.notify_error_and_cleanup(Error::from(e), ctx) + } + })); } ws::Message::Binary(data) | ws::Message::Continuation(Item::Last(data)) => { let result = self.handle_data(data)?; diff --git a/static/index.html b/static/index.html index f800d1c..d792bb1 100644 --- a/static/index.html +++ b/static/index.html @@ -16,6 +16,9 @@

transbeam

+ + +
@@ -33,11 +36,11 @@
-
-
-
Download code:
+
@@ -49,17 +52,6 @@ Select files to upload... -
-
-
- -
- -
-