From 022512884a950afa0af136c6157fa65669357d58 Mon Sep 17 00:00:00 2001 From: minish Date: Mon, 6 Jan 2025 19:58:24 -0500 Subject: [PATCH] v0.2.8 file extension update --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/engine.rs | 6 +++--- src/new.rs | 52 +++++++++++++++++++++++++++++++++++++-------------- src/view.rs | 1 - 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b90fd6..e77e7e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ dependencies = [ [[package]] name = "breeze" -version = "0.2.7" +version = "0.2.8" dependencies = [ "anyhow", "argh", diff --git a/Cargo.toml b/Cargo.toml index 4f2f606..953ad85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breeze" -version = "0.2.7" +version = "0.2.8" edition = "2021" [dependencies] diff --git a/src/engine.rs b/src/engine.rs index 7461744..57eea64 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -240,13 +240,13 @@ impl Engine { /// Generate a new saved name for an upload. /// /// If it picks a name that already exists, it will try again. - pub async fn gen_saved_name(&self, ext: &str) -> String { + pub async fn gen_saved_name(&self, ext: Option) -> String { loop { // generate a 6-character alphanumeric string let mut saved_name: String = Alphanumeric.sample_string(&mut rand::thread_rng(), 6); // if we have an extension, add it now - if !ext.is_empty() { + if let Some(ref ext) = ext { saved_name.push('.'); saved_name.push_str(ext); } @@ -389,7 +389,7 @@ impl Engine { pub async fn process( &self, - ext: &str, + ext: Option, provided_len: u64, stream: BodyDataStream, lifetime: Option, diff --git a/src/new.rs b/src/new.rs index 10b59c9..bfb1561 100644 --- a/src/new.rs +++ b/src/new.rs @@ -1,4 +1,9 @@ -use std::{ffi::OsStr, path::PathBuf, sync::Arc, time::Duration}; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, + sync::Arc, + time::Duration, +}; use axum::{ body::Body, @@ -32,7 +37,6 @@ pub struct NewRequest { /// The request handler for the /new path. /// This handles all new uploads. -#[axum::debug_handler] pub async fn new( State(engine): State>, Query(req): Query, @@ -49,11 +53,37 @@ pub async fn new( return Err(StatusCode::BAD_REQUEST); } - let extension = PathBuf::from(req.name) - .extension() - .and_then(OsStr::to_str) - .unwrap_or_default() - .to_string(); + // -- try to figure out a file extension.. + + fn extension(pb: &Path) -> Option { + pb.extension().and_then(OsStr::to_str).map(str::to_string) + } + + let pb = PathBuf::from(req.name); + let mut ext = extension(&pb); + + // common extensions that usually have a second extension before themselves + const ADDITIVE: &[&str] = &["gz", "xz", "bz2", "lz4", "zst"]; + + // if the extension is one of those, try to find that second extension + if ext + .as_ref() + .is_some_and(|ext| ADDITIVE.contains(&ext.as_str())) + { + // try to parse out another extension + let stem = pb.file_stem().unwrap(); // SAFETY: if extension is Some(), this will also be + + if let Some(second_ext) = extension(&PathBuf::from(stem)) { + // there is another extension, + // try to make sure it's one we want + // 4 is enough for most common file extensions + // and not many false positives, hopefully + if second_ext.len() <= 4 { + // seems ok so combine them + ext = ext.as_ref().map(|first_ext| second_ext + "." + first_ext); + } + } + } // turn body into stream let stream = Body::into_data_stream(body); @@ -64,13 +94,7 @@ pub async fn new( // they don't expect the connection to close before they're done uploading, i think // so it will just present the user with a "connection closed" error match engine - .process( - &extension, - content_length, - stream, - req.last_for, - req.keep_exif, - ) + .process(ext, content_length, stream, req.last_for, req.keep_exif) .await { Ok(outcome) => match outcome { diff --git a/src/view.rs b/src/view.rs index db7a7e6..413ee50 100644 --- a/src/view.rs +++ b/src/view.rs @@ -86,7 +86,6 @@ impl IntoResponse for UploadResponse { /// GET request handler for /p/* path. /// All file views are handled here. -#[axum::debug_handler] pub async fn view( State(engine): State>, Path(original_path): Path,