diff --git a/Cargo.lock b/Cargo.lock index 281716f..cc2972d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -278,6 +278,8 @@ dependencies = [ "hyper-util", "lazy_static", "log", + "mime-sniffer", + "new_mime_guess", "parking_lot", "pocket-db", "pocket-types", @@ -1094,6 +1096,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime-sniffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8b2a64cd735f1d5f17ff6701ced3cc3c54851f9448caf454cd9c923d812408" +dependencies = [ + "mime", + "url", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1137,6 +1155,16 @@ dependencies = [ "memmap2", ] +[[package]] +name = "new_mime_guess" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a2dfb3559d53e90b709376af1c379462f7fb3085a0177deb73e6ea0d99eff4" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "nom" version = "7.1.3" @@ -1931,6 +1959,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + [[package]] name = "unicode-ident" version = "1.0.13" diff --git a/Cargo.toml b/Cargo.toml index 39281ff..6f284a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ hyper-tungstenite = "0.15" hyper-util = "0.1" lazy_static = "1.5" log = "0.4" +mime-sniffer = "0.1" +new_mime_guess = "4.0" pocket-types = { git = "https://github.com/mikedilger/pocket", branch = "master" } pocket-db = { git = "https://github.com/mikedilger/pocket", branch = "master" } parking_lot = "0.12" diff --git a/src/filestore/mod.rs b/src/filestore/mod.rs index 7d85a48..1d97844 100644 --- a/src/filestore/mod.rs +++ b/src/filestore/mod.rs @@ -48,7 +48,7 @@ impl FileStore { &self, data: BoxBody, expected_hash: Option, - ) -> Result<(u64, HashOutput), Error> { + ) -> Result<(u64, HashOutput, Option), Error> { use bitcoin_hashes::sha256; use std::io::Write; // for hash_engine.write_all() @@ -78,6 +78,7 @@ impl FileStore { // Copy the data into the tempfile (hashing and counting as we go) let count = tokio::io::copy(&mut inspect_reader, &mut tempfile).await?; + drop(tempfile); // Verify our code was correct if count != size { @@ -101,6 +102,16 @@ impl FileStore { } } + // Sniff the mime-type + let maybe_mime_string = { + use mime_sniffer::MimeTypeSniffer; + use tokio::io::AsyncReadExt; + let mut readtempfile = File::open(&temppathbuf).await?; + let mut buffer: Vec = vec![0; 128]; + let _ = readtempfile.read(&mut buffer).await?; + buffer.sniff_mime_type().map(|s| s.to_string()) + }; + // Compute the proper path let pathbuf = hash.to_pathbuf(&self.base); @@ -109,7 +120,7 @@ impl FileStore { // Just clean up fs::remove_file(&temppathbuf).await?; - return Ok((size, hash)); + return Ok((size, hash, maybe_mime_string)); } // Make the parent directory @@ -118,7 +129,7 @@ impl FileStore { // Move the file fs::rename(&temppathbuf, &pathbuf).await?; - Ok((size, hash)) + Ok((size, hash, maybe_mime_string)) } /// Retrieve a file from storage by its HashOutput, streamed to a hyper BoxBoxy diff --git a/src/web/blossom/mod.rs b/src/web/blossom/mod.rs index 7a376a9..b68ab8a 100644 --- a/src/web/blossom/mod.rs +++ b/src/web/blossom/mod.rs @@ -229,7 +229,15 @@ pub async fn handle_upload( let uri = request.uri().to_owned(); - let (size, hash) = GLOBALS + let maybe_content_type = match request.headers().get(http::header::CONTENT_TYPE) { + Some(s) => match s.to_str() { + Ok(s) => Some(s.to_owned()), + Err(_) => None, + }, + None => None, + }; + + let (size, hash, maybe_sniffed_mime_string) = GLOBALS .filestore .get() .unwrap() @@ -239,11 +247,26 @@ pub async fn handle_upload( ) .await?; + let extension = { + let mut mime_string: String = "".to_owned(); + if let Some(ms) = maybe_content_type { + mime_string = ms.to_owned(); + } else if let Some(ms) = maybe_sniffed_mime_string { + mime_string = ms.to_owned(); + } + + if let Some(exts) = new_mime_guess::get_mime_extensions_str(&mime_string) { + exts[0] + } else { + "blob" + } + }; + let uri = { let mut parts = GLOBALS.config.read().uri_parts(uri, true)?; parts.path_and_query = Some(http::uri::PathAndQuery::from_maybe_shared(format!( - "/{}", - hash + "/{}.{}", + hash, extension ))?); http::Uri::from_parts(parts)? };