diff --git a/.gitignore b/.gitignore index 1cdef6c..87d710a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -/target +**/target **/database -/web/dist/ -**/node_modules \ No newline at end of file +**/dist/ +**/node_modules +test.* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 0553ed9..d373e3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,6 +197,12 @@ dependencies = [ "utf8-width", ] +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" + [[package]] name = "bytemuck" version = "1.7.2" @@ -450,6 +456,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "fixedbitset" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" + [[package]] name = "fnv" version = "1.0.7" @@ -989,6 +1001,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minimal-lexical" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" + [[package]] name = "miniz_oxide" version = "0.3.7" @@ -1053,6 +1071,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "nom" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -1174,6 +1203,7 @@ dependencies = [ "js-sys", "omegaupload-common", "reqwasm", + "tree_magic_mini", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1212,6 +1242,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project" version = "1.0.8" @@ -1969,6 +2009,27 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "tree_magic_db" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73fc24a5427b3b15e2b0bcad8ef61b5affb1da8ac89c8bf3f196c8692d57f02" + +[[package]] +name = "tree_magic_mini" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7581560dc616314f7d73e81419c783d93a92e7fc7331b3041ff57bab240ea6" +dependencies = [ + "bytecount", + "fnv", + "lazy_static", + "nom 7.0.0", + "once_cell", + "petgraph", + "tree_magic_db", +] + [[package]] name = "try-lock" version = "0.2.3" diff --git a/web/Cargo.toml b/web/Cargo.toml index 41b7ca5..19de948 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -17,6 +17,7 @@ http = "0.2" image = "0.23" js-sys = "0.3" reqwasm = "0.2" +tree_magic_mini = { version = "3", features = ["with-gpl-data"] } wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" yew = { version = "0.18", features = ["wasm-bindgen-futures"] } diff --git a/web/src/decrypt.rs b/web/src/decrypt.rs index 179abdb..a62a445 100644 --- a/web/src/decrypt.rs +++ b/web/src/decrypt.rs @@ -8,7 +8,14 @@ use omegaupload_common::crypto::{open_in_place, Key, Nonce}; use wasm_bindgen::JsCast; use web_sys::Blob; -use crate::DecryptedData; +#[derive(Clone)] +pub enum DecryptedData { + String(Arc), + Blob(Arc), + Image(Arc, (u32, u32), usize), + Audio(Arc), + Video(Arc), +} pub fn decrypt( mut container: Vec, @@ -49,7 +56,15 @@ pub fn decrypt( container.len(), )) } else { - Ok(DecryptedData::Blob(blob)) + let mime_type = tree_magic_mini::from_u8(&container); + log!(mime_type); + if mime_type.starts_with("audio/") { + Ok(DecryptedData::Audio(blob)) + } else if mime_type.starts_with("video/") || mime_type.ends_with("x-matroska") { + Ok(DecryptedData::Video(blob)) + } else { + Ok(DecryptedData::Blob(blob)) + } } } } diff --git a/web/src/main.rs b/web/src/main.rs index 5b7516a..927b1d7 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use std::sync::Arc; use byte_unit::Byte; +use decrypt::DecryptedData; use gloo_console::log; use http::header::EXPIRES; use http::uri::PathAndQuery; @@ -229,6 +230,38 @@ impl Component for Paste { ))); decrypted_object.push(&entry); + let entry = Array::new(); + entry.push(&JsString::from("expiration")); + entry.push(&JsString::from(expires.to_string())); + decrypted_object.push(&entry); + } + DecryptedData::Audio(blob) => { + let entry = Array::new(); + entry.push(&JsString::from("data")); + entry.push(blob); + decrypted_object.push(&entry); + + let entry = Array::new(); + entry.push(&JsString::from("type")); + entry.push(&JsString::from("audio")); + decrypted_object.push(&entry); + + let entry = Array::new(); + entry.push(&JsString::from("expiration")); + entry.push(&JsString::from(expires.to_string())); + decrypted_object.push(&entry); + } + DecryptedData::Video(blob) => { + let entry = Array::new(); + entry.push(&JsString::from("data")); + entry.push(blob); + decrypted_object.push(&entry); + + let entry = Array::new(); + entry.push(&JsString::from("type")); + entry.push(&JsString::from("video")); + decrypted_object.push(&entry); + let entry = Array::new(); entry.push(&JsString::from("expiration")); entry.push(&JsString::from(expires.to_string())); @@ -292,10 +325,3 @@ impl Component for Paste { html! {} } } - -#[derive(Clone)] -pub enum DecryptedData { - String(Arc), - Blob(Arc), - Image(Arc, (u32, u32), usize), -} diff --git a/web/src/main.scss b/web/src/main.scss index d1dacf7..c6dcb65 100644 --- a/web/src/main.scss +++ b/web/src/main.scss @@ -64,11 +64,11 @@ main { cursor: pointer; } -img { - margin-bottom: 4em; +img, audio, video { + border-radius: $padding; + margin-bottom: $padding; max-height: 75vh; max-width: 75vw; - border-radius: $padding; } .primary { diff --git a/web/src/main.ts b/web/src/main.ts index e50dd21..f5f7f89 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -4,10 +4,10 @@ function loadFromDb() { dbReq.onsuccess = (evt) => { const db = (evt.target as IDBRequest).result; const obj_store = db - .transaction("decrypted data", "readonly") - .objectStore("decrypted data") - .get(window.location.pathname); - obj_store.onsuccess = (evt) => { + .transaction("decrypted data") + .objectStore("decrypted data"); + let fetchReq = obj_store.get(window.location.pathname); + fetchReq.onsuccess = (evt) => { const data = (evt.target as IDBRequest).result; switch (data.type) { case "string": @@ -19,13 +19,32 @@ function loadFromDb() { case "image": createImagePasteUi(data); break; + case "audio": + createAudioPasteUi(data); + break; + case "video": + createVideoPasteUi(data); + break; default: createBrokenStateUi(); break; } + + // IDB was only used as a temporary medium; + window.onbeforeunload = (e) => { + // See https://link.eddie.sh/NrIIq on why .commit is necessary. + const transaction = db.transaction("decrypted data", "readwrite"); + transaction + .objectStore("decrypted data") + .delete(window.location.pathname); + transaction.commit(); + transaction.oncomplete = () => { + console.log("Item deleted from cache"); + } + }; }; - obj_store.onerror = (evt) => { + fetchReq.onerror = (evt) => { console.log("err"); console.log(evt); }; @@ -58,37 +77,6 @@ function createStringPasteUi(data) { hljs.initLineNumbersOnLoad(); } -function createImagePasteUi(data) { - let bodyEle = document.getElementsByTagName("body")[0]; - bodyEle.textContent = ''; - - let mainEle = document.createElement("main"); - mainEle.classList.add("hljs"); - mainEle.classList.add("centered"); - mainEle.classList.add("fullscreen"); - - const downloadLink = URL.createObjectURL(data.data); - - let expirationEle = document.createElement("p"); - expirationEle.textContent = data.expiration; - mainEle.appendChild(expirationEle); - - let imgEle = document.createElement("img"); - imgEle.src = downloadLink; - mainEle.appendChild(imgEle); - - - let downloadEle = document.createElement("a"); - downloadEle.href = downloadLink; - downloadEle.download = window.location.pathname; - downloadEle.classList.add("hljs-meta"); - downloadEle.textContent = data.button; - mainEle.appendChild(downloadEle); - - - bodyEle.appendChild(mainEle); -} - function createBlobPasteUi(data) { let bodyEle = document.getElementsByTagName("body")[0]; bodyEle.textContent = ''; @@ -129,6 +117,49 @@ function createBlobPasteUi(data) { bodyEle.appendChild(mainEle); } +function createImagePasteUi({ expiration, data, button }) { + createMultiMediaPasteUi("img", expiration, data, button); +} + +function createAudioPasteUi({ expiration, data }) { + createMultiMediaPasteUi("audio", expiration, data, "Download"); +} + +function createVideoPasteUi({ expiration, data }) { + createMultiMediaPasteUi("video", expiration, data, "Download"); +} + +function createMultiMediaPasteUi(tag, expiration, data, downloadMessage) { + let bodyEle = document.getElementsByTagName("body")[0]; + bodyEle.textContent = ''; + + let mainEle = document.createElement("main"); + mainEle.classList.add("hljs"); + mainEle.classList.add("centered"); + mainEle.classList.add("fullscreen"); + + const downloadLink = URL.createObjectURL(data); + + let expirationEle = document.createElement("p"); + expirationEle.textContent = expiration; + mainEle.appendChild(expirationEle); + + let videoEle = document.createElement(tag); + videoEle.src = downloadLink; + videoEle.controls = true; + mainEle.appendChild(videoEle); + + let downloadEle = document.createElement("a"); + downloadEle.href = downloadLink; + downloadEle.download = window.location.pathname; + downloadEle.classList.add("hljs-meta"); + downloadEle.textContent = downloadMessage; + mainEle.appendChild(downloadEle); + + bodyEle.appendChild(mainEle); + +} + // Exported to main.rs function createNotFoundUi() { let body = document.getElementsByTagName("body")[0];