diff --git a/web/src/decrypt.rs b/web/src/decrypt.rs index dddb0d1..aeb9afd 100644 --- a/web/src/decrypt.rs +++ b/web/src/decrypt.rs @@ -49,11 +49,13 @@ fn now() -> f64 { .now() } +pub struct MimeType(pub String); + pub fn decrypt( mut container: Vec, key: &Secret, maybe_password: Option>, -) -> Result { +) -> Result<(DecryptedData, MimeType), Error> { open_in_place(&mut container, key, maybe_password)?; let mime_type = tree_magic_mini::from_u8(&container); @@ -76,14 +78,14 @@ pub fn decrypt( log!(format!("Blob conversion completed in {}ms", now() - start)); - match container.content_type() { - ContentType::Text => Ok(DecryptedData::String(Arc::new( + let data = match container.content_type() { + ContentType::Text => DecryptedData::String(Arc::new( // SAFETY: ContentType::Text is guaranteed to be valid UTF-8. unsafe { String::from_utf8_unchecked(container) }, - ))), - ContentType::Image => Ok(DecryptedData::Image(blob, container.len())), - ContentType::Audio => Ok(DecryptedData::Audio(blob)), - ContentType::Video => Ok(DecryptedData::Video(blob)), + )), + ContentType::Image => DecryptedData::Image(blob, container.len()), + ContentType::Audio => DecryptedData::Audio(blob), + ContentType::Video => DecryptedData::Video(blob), ContentType::ZipArchive => { let mut entries = vec![]; let cursor = Cursor::new(container); @@ -107,11 +109,13 @@ pub fn decrypt( } entries.sort_by(|a, b| a.name.cmp(&b.name)); - Ok(DecryptedData::Archive(blob, entries)) + DecryptedData::Archive(blob, entries) } - ContentType::GzipArchive => Ok(DecryptedData::Archive(blob, vec![])), - ContentType::Unknown => Ok(DecryptedData::Blob(blob)), - } + ContentType::GzipArchive => DecryptedData::Archive(blob, vec![]), + ContentType::Unknown => DecryptedData::Blob(blob), + }; + + Ok((data, MimeType(mime_type.to_owned()))) } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] diff --git a/web/src/main.rs b/web/src/main.rs index 9b73110..f6c7d0c 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -20,7 +20,7 @@ use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; use byte_unit::{n_mib_bytes, Byte}; -use decrypt::DecryptedData; +use decrypt::{DecryptedData, MimeType}; use gloo_console::{error, log}; use http::uri::PathAndQuery; use http::{StatusCode, Uri}; @@ -47,7 +47,7 @@ const DOWNLOAD_SIZE_LIMIT: u128 = n_mib_bytes!(500); #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_name = loadFromDb)] - pub fn load_from_db(); + pub fn load_from_db(mimetype: JsString); #[wasm_bindgen(js_name = renderMessage)] pub fn render_message(message: JsString); } @@ -175,7 +175,7 @@ async fn fetch_resources( return Ok(()); } - let decrypted = match decrypt(data, &key, password) { + let (decrypted, mimetype) = match decrypt(data, &key, password) { Ok(data) => data, Err(e) => { let msg = match e { @@ -194,7 +194,7 @@ async fn fetch_resources( let db_open_req = open_idb()?; let on_success = Closure::once(Box::new(move |event| { - on_success(&event, &decrypted, &expires); + on_success(&event, &decrypted, mimetype, &expires); })); db_open_req.set_onsuccess(Some(on_success.into_js_value().unchecked_ref())); @@ -226,7 +226,7 @@ async fn fetch_resources( Ok(()) } -fn on_success(event: &Event, decrypted: &DecryptedData, expires: &str) { +fn on_success(event: &Event, decrypted: &DecryptedData, mimetype: MimeType, expires: &str) { let transaction: IdbObjectStore = as_idb_db(event) .transaction_with_str_and_mode("decrypted data", IdbTransactionMode::Readwrite) .unwrap() @@ -275,7 +275,7 @@ fn on_success(event: &Event, decrypted: &DecryptedData, expires: &str) { put_action.set_onsuccess(Some( Closure::once(Box::new(|| { log!("success"); - load_from_db(); + load_from_db(JsString::from(mimetype.0)); })) .into_js_value() .unchecked_ref(), diff --git a/web/src/main.ts b/web/src/main.ts index c51e3d3..9463e54 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -15,7 +15,8 @@ // along with this program. If not, see . // Exported to main.rs -function loadFromDb() { +function loadFromDb(mimetype: string) { + console.log("Got mime type:", mimetype); const dbReq = window.indexedDB.open("omegaupload", 1); dbReq.onsuccess = (evt) => { const db = (evt.target as IDBRequest).result; @@ -27,7 +28,7 @@ function loadFromDb() { const data = (evt.target as IDBRequest).result; switch (data.type) { case "string": - createStringPasteUi(data); + createStringPasteUi(data, mimetype); break; case "blob": createBlobPasteUi(data); @@ -70,7 +71,7 @@ function loadFromDb() { }; } -function createStringPasteUi(data) { +function createStringPasteUi(data, type: string) { let bodyEle = document.getElementsByTagName("body")[0]; bodyEle.textContent = ''; @@ -84,6 +85,14 @@ function createStringPasteUi(data) { headerEle.textContent = data.expiration; preEle.appendChild(headerEle); + let downloadEle = document.createElement("a"); + downloadEle.href = URL.createObjectURL(new Blob([data.data], { type })); + downloadEle.download = window.location.pathname; + downloadEle.classList.add("hljs-meta"); + downloadEle.classList.add("centered"); + downloadEle.textContent = "Download file."; + preEle.appendChild(downloadEle); + preEle.appendChild(document.createElement("hr")); let codeEle = document.createElement("code"); @@ -120,7 +129,6 @@ function createBlobPasteUi(data) { downloadEle.textContent = "Download binary file."; divEle.appendChild(downloadEle); - mainEle.appendChild(divEle); let displayAnywayEle = document.createElement("p"); @@ -130,7 +138,7 @@ function createBlobPasteUi(data) { displayAnywayEle.onclick = () => { data.data.text().then(text => { data.data = text; - createStringPasteUi(data); + createStringPasteUi(data, "application/octet-stream"); }) }; mainEle.appendChild(displayAnywayEle);