Add support for language and name hints

This commit is contained in:
Edward Shen 2022-01-15 22:47:56 -08:00
parent 9b2f53bddf
commit b7d5425a73
Signed by: edward
GPG key ID: 19182661E818369F
5 changed files with 147 additions and 72 deletions

View file

@ -52,6 +52,8 @@ pub struct ParsedUrl {
pub struct PartialParsedUrl { pub struct PartialParsedUrl {
pub decryption_key: Option<Secret<Key>>, pub decryption_key: Option<Secret<Key>>,
pub needs_password: bool, pub needs_password: bool,
pub name: Option<String>,
pub language: Option<String>,
} }
#[cfg(test)] #[cfg(test)]
@ -91,7 +93,7 @@ impl TryFrom<&str> for PartialParsedUrl {
return Ok(Self { return Ok(Self {
decryption_key, decryption_key,
needs_password: false, ..Self::default()
}); });
} }
@ -106,6 +108,8 @@ impl TryFrom<&str> for PartialParsedUrl {
let mut decryption_key = None; let mut decryption_key = None;
let mut needs_password = false; let mut needs_password = false;
let mut name = None;
let mut language = None;
for (key, value) in args { for (key, value) in args {
match (key, value) { match (key, value) {
@ -117,6 +121,8 @@ impl TryFrom<&str> for PartialParsedUrl {
("pw", _) => { ("pw", _) => {
needs_password = true; needs_password = true;
} }
("name", Some(provided_name)) => name = Some(provided_name.to_owned()),
("lang", Some(provided_lang)) => language = Some(provided_lang.to_owned()),
_ => (), _ => (),
} }
} }
@ -124,6 +130,8 @@ impl TryFrom<&str> for PartialParsedUrl {
Ok(Self { Ok(Self {
decryption_key, decryption_key,
needs_password, needs_password,
name,
language,
}) })
} }
} }
@ -159,6 +167,7 @@ impl FromStr for ParsedUrl {
let PartialParsedUrl { let PartialParsedUrl {
mut decryption_key, mut decryption_key,
needs_password, needs_password,
..
} = PartialParsedUrl::try_from(fragment)?; } = PartialParsedUrl::try_from(fragment)?;
url.set_fragment(None); url.set_fragment(None);
@ -338,7 +347,7 @@ mod partial_parsed_url_parsing {
DECRYPTION_KEY_STRING.parse(), DECRYPTION_KEY_STRING.parse(),
Ok(PartialParsedUrl { Ok(PartialParsedUrl {
decryption_key: decryption_key(), decryption_key: decryption_key(),
needs_password: false ..Default::default()
}) })
); );
} }
@ -350,7 +359,7 @@ mod partial_parsed_url_parsing {
input.parse(), input.parse(),
Ok(PartialParsedUrl { Ok(PartialParsedUrl {
decryption_key: decryption_key(), decryption_key: decryption_key(),
needs_password: false ..Default::default()
}) })
); );
} }
@ -362,7 +371,34 @@ mod partial_parsed_url_parsing {
input.parse(), input.parse(),
Ok(PartialParsedUrl { Ok(PartialParsedUrl {
decryption_key: decryption_key(), decryption_key: decryption_key(),
needs_password: true needs_password: true,
..Default::default()
})
);
}
#[test]
fn with_name() {
let input = "key:ddLod7sGy_EjFDjWqZoH4i5n_XU8bIpEuEo3-pjfAIE=!name:test_file.rs";
assert_eq!(
input.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
name: Some("test_file.rs".to_owned()),
..Default::default()
})
);
}
#[test]
fn with_lang() {
let input = "key:ddLod7sGy_EjFDjWqZoH4i5n_XU8bIpEuEo3-pjfAIE=!lang:rust";
assert_eq!(
input.parse(),
Ok(PartialParsedUrl {
decryption_key: decryption_key(),
language: Some("rust".to_owned()),
..Default::default()
}) })
); );
} }
@ -374,7 +410,8 @@ mod partial_parsed_url_parsing {
input.parse(), input.parse(),
Ok(PartialParsedUrl { Ok(PartialParsedUrl {
decryption_key: decryption_key(), decryption_key: decryption_key(),
needs_password: true needs_password: true,
..Default::default()
}) })
); );
} }
@ -386,7 +423,7 @@ mod partial_parsed_url_parsing {
input.parse(), input.parse(),
Ok(PartialParsedUrl { Ok(PartialParsedUrl {
decryption_key: decryption_key(), decryption_key: decryption_key(),
needs_password: false ..Default::default()
}) })
); );
} }

View file

@ -12,7 +12,7 @@
<script src="static/highlightjs-line-numbers.min.js" defer></script> <script src="static/highlightjs-line-numbers.min.js" defer></script>
<link data-trunk rel="rust" data-wasm-opt="0" data-keep-debug="true" data-no-mangle="true" /> <link data-trunk rel="rust" data-wasm-opt="0" data-keep-debug="true" data-no-mangle="true" />
<link data-trunk rel="copy-file" href="vendor/MPLUS_FONTS/fonts/ttf/MplusCodeLatin[wdth,wght].ttf" dest="/" /> <link data-trunk rel="copy-file" href="vendor/MPLUS_FONTS/fonts/ttf/MPLUSCodeLatin[wdth,wght].ttf" dest="/" />
<link data-trunk rel="copy-file" href="vendor/highlight.min.js" dest="/" /> <link data-trunk rel="copy-file" href="vendor/highlight.min.js" dest="/" />
<link data-trunk rel="copy-file" href="vendor/highlightjs-line-numbers.js/dist/highlightjs-line-numbers.min.js" <link data-trunk rel="copy-file" href="vendor/highlightjs-line-numbers.js/dist/highlightjs-line-numbers.min.js"
dest="/" /> dest="/" />

View file

@ -47,7 +47,7 @@ const DOWNLOAD_SIZE_LIMIT: u128 = n_mib_bytes!(500);
#[wasm_bindgen] #[wasm_bindgen]
extern "C" { extern "C" {
#[wasm_bindgen(js_name = loadFromDb)] #[wasm_bindgen(js_name = loadFromDb)]
pub fn load_from_db(mimetype: JsString); pub fn load_from_db(mime_type: JsString, name: Option<JsString>, language: Option<JsString>);
#[wasm_bindgen(js_name = renderMessage)] #[wasm_bindgen(js_name = renderMessage)]
pub fn render_message(message: JsString); pub fn render_message(message: JsString);
} }
@ -88,7 +88,15 @@ fn main() {
Uri::from_parts(uri_parts).unwrap() Uri::from_parts(uri_parts).unwrap()
}; };
let (key, needs_pw) = { let (
key,
PartialParsedUrl {
needs_password,
name,
language,
..
},
) = {
let fragment = if let Some(fragment) = url.split_once('#').map(|(_, fragment)| fragment) { let fragment = if let Some(fragment) = url.split_once('#').map(|(_, fragment)| fragment) {
if fragment.is_empty() { if fragment.is_empty() {
error!("Key is missing in url; bailing."); error!("Key is missing in url; bailing.");
@ -102,7 +110,7 @@ fn main() {
return; return;
}; };
let partial_parsed_url = match PartialParsedUrl::try_from(fragment) { let mut partial_parsed_url = match PartialParsedUrl::try_from(fragment) {
Ok(partial_parsed_url) => partial_parsed_url, Ok(partial_parsed_url) => partial_parsed_url,
Err(e) => { Err(e) => {
error!("Failed to parse text fragment; bailing."); error!("Failed to parse text fragment; bailing.");
@ -111,7 +119,7 @@ fn main() {
} }
}; };
let key = if let Some(key) = partial_parsed_url.decryption_key { let key = if let Some(key) = partial_parsed_url.decryption_key.take() {
key key
} else { } else {
error!("Key is missing in url; bailing."); error!("Key is missing in url; bailing.");
@ -119,10 +127,10 @@ fn main() {
return; return;
}; };
(key, partial_parsed_url.needs_password) (key, partial_parsed_url)
}; };
let password = if needs_pw { let password = if needs_password {
loop { loop {
let pw = window().prompt_with_message("A password is required to decrypt this paste:"); let pw = window().prompt_with_message("A password is required to decrypt this paste:");
@ -150,7 +158,7 @@ fn main() {
}; };
spawn_local(async move { spawn_local(async move {
if let Err(e) = fetch_resources(request_uri, key, password).await { if let Err(e) = fetch_resources(request_uri, key, password, name, language).await {
log!(e.to_string()); log!(e.to_string());
} }
}); });
@ -161,6 +169,8 @@ async fn fetch_resources(
request_uri: Uri, request_uri: Uri,
key: Secret<Key>, key: Secret<Key>,
password: Option<SecretVec<u8>>, password: Option<SecretVec<u8>>,
name: Option<String>,
language: Option<String>,
) -> Result<()> { ) -> Result<()> {
match Request::get(&request_uri.to_string()).send().await { match Request::get(&request_uri.to_string()).send().await {
Ok(resp) if resp.status() == StatusCode::OK => { Ok(resp) if resp.status() == StatusCode::OK => {
@ -213,7 +223,7 @@ async fn fetch_resources(
let db_open_req = open_idb()?; let db_open_req = open_idb()?;
let on_success = Closure::once(Box::new(move |event| { let on_success = Closure::once(Box::new(move |event| {
on_success(&event, &decrypted, mimetype, &expires); on_success(&event, &decrypted, mimetype, &expires, name, language);
})); }));
db_open_req.set_onsuccess(Some(on_success.into_js_value().unchecked_ref())); db_open_req.set_onsuccess(Some(on_success.into_js_value().unchecked_ref()));
@ -245,7 +255,14 @@ async fn fetch_resources(
Ok(()) Ok(())
} }
fn on_success(event: &Event, decrypted: &DecryptedData, mimetype: MimeType, expires: &str) { fn on_success(
event: &Event,
decrypted: &DecryptedData,
mimetype: MimeType,
expires: &str,
name: Option<String>,
language: Option<String>,
) {
let transaction: IdbObjectStore = as_idb_db(event) let transaction: IdbObjectStore = as_idb_db(event)
.transaction_with_str_and_mode("decrypted data", IdbTransactionMode::Readwrite) .transaction_with_str_and_mode("decrypted data", IdbTransactionMode::Readwrite)
.unwrap() .unwrap()
@ -294,7 +311,9 @@ fn on_success(event: &Event, decrypted: &DecryptedData, mimetype: MimeType, expi
put_action.set_onsuccess(Some( put_action.set_onsuccess(Some(
Closure::once(Box::new(|| { Closure::once(Box::new(|| {
log!("success"); log!("success");
load_from_db(JsString::from(mimetype.0)); let name = name.map(JsString::from);
let language = language.map(JsString::from);
load_from_db(JsString::from(mimetype.0), name, language);
})) }))
.into_js_value() .into_js_value()
.unchecked_ref(), .unchecked_ref(),

View file

@ -20,7 +20,7 @@ $padding: 1em;
@font-face { @font-face {
font-family: "Mplus Code"; font-family: "Mplus Code";
src: url("./MplusCodeLatin[wdth,wght].ttf") format("truetype"); src: url("./MPLUSCodeLatin[wdth,wght].ttf") format("truetype");
} }
body { body {

View file

@ -15,35 +15,38 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// Exported to main.rs // Exported to main.rs
function loadFromDb(mimetype: string) { function loadFromDb(mimeType: string, name?: string, language?: string) {
console.log("Got mime type:", mimetype); console.log("[js] Got name:", name);
console.log("[js] Got language:", language);
console.log("[js] Got mime type:", mimeType);
const dbReq = window.indexedDB.open("omegaupload", 1); const dbReq = window.indexedDB.open("omegaupload", 1);
dbReq.onsuccess = (evt) => { dbReq.onsuccess = (evt) => {
const db = (evt.target as IDBRequest).result; const db = (evt.target as IDBRequest).result;
const obj_store = db const obj_store = db
.transaction("decrypted data") .transaction("decrypted data")
.objectStore("decrypted data"); .objectStore("decrypted data");
let fetchReq = obj_store.get(window.location.pathname); const fetchReq = obj_store.get(window.location.pathname);
fetchReq.onsuccess = (evt) => { fetchReq.onsuccess = (evt) => {
const data = (evt.target as IDBRequest).result; const data = (evt.target as IDBRequest).result;
switch (data.type) { switch (data.type) {
case "string": case "string":
createStringPasteUi(data, mimetype); createStringPasteUi(data, mimeType, name, language);
break; break;
case "blob": case "blob":
createBlobPasteUi(data); createBlobPasteUi(data, name);
break; break;
case "image": case "image":
createImagePasteUi(data); createImagePasteUi(data, name);
break; break;
case "audio": case "audio":
createAudioPasteUi(data); createAudioPasteUi(data, name);
break; break;
case "video": case "video":
createVideoPasteUi(data); createVideoPasteUi(data, name);
break; break;
case "archive": case "archive":
createArchivePasteUi(data); createArchivePasteUi(data, name);
break; break;
default: default:
renderMessage("Something went wrong. Try clearing local data."); renderMessage("Something went wrong. Try clearing local data.");
@ -71,23 +74,24 @@ function loadFromDb(mimetype: string) {
}; };
} }
function createStringPasteUi(data, type: string) { function createStringPasteUi(data, mimeType: string, name?: string, lang?: string) {
let bodyEle = document.getElementsByTagName("body")[0]; const bodyEle = document.getElementsByTagName("body")[0];
bodyEle.textContent = ''; bodyEle.textContent = '';
let mainEle = document.createElement("main"); const mainEle = document.createElement("main");
let preEle = document.createElement("pre"); const preEle = document.createElement("pre");
preEle.classList.add("paste"); preEle.classList.add("paste");
let headerEle = document.createElement("p"); const headerEle = document.createElement("p");
headerEle.classList.add("unselectable"); headerEle.classList.add("unselectable");
headerEle.classList.add("centered"); headerEle.classList.add("centered");
headerEle.textContent = data.expiration; headerEle.textContent = data.expiration;
preEle.appendChild(headerEle); preEle.appendChild(headerEle);
let downloadEle = document.createElement("a"); const downloadEle = document.createElement("a");
downloadEle.href = URL.createObjectURL(new Blob([data.data], { type })); downloadEle.href = getObjectUrl([data.data], mimeType);
downloadEle.download = window.location.pathname; downloadEle.download = name;
downloadEle.classList.add("hljs-meta"); downloadEle.classList.add("hljs-meta");
downloadEle.classList.add("centered"); downloadEle.classList.add("centered");
downloadEle.textContent = "Download file."; downloadEle.textContent = "Download file.";
@ -95,43 +99,52 @@ function createStringPasteUi(data, type: string) {
preEle.appendChild(document.createElement("hr")); preEle.appendChild(document.createElement("hr"));
let codeEle = document.createElement("code"); const codeEle = document.createElement("code");
codeEle.textContent = data.data; codeEle.textContent = data.data;
preEle.appendChild(codeEle); preEle.appendChild(codeEle);
mainEle.appendChild(preEle); mainEle.appendChild(preEle);
bodyEle.appendChild(mainEle); bodyEle.appendChild(mainEle);
if (!hljs.getLanguage(lang)) {
console.warn(`[js] User provided language (${lang}) is not known. Ignoring.`);
} else {
console.log(`[js] Selecting user provided language ${lang} for highlighting.`);
hljs.configure({
languages: [lang],
});
}
hljs.highlightAll(); hljs.highlightAll();
hljs.initLineNumbersOnLoad(); hljs.initLineNumbersOnLoad();
} }
function createBlobPasteUi(data) { function createBlobPasteUi(data, name?: string) {
let bodyEle = document.getElementsByTagName("body")[0]; const bodyEle = document.getElementsByTagName("body")[0];
bodyEle.textContent = ''; bodyEle.textContent = '';
let mainEle = document.createElement("main"); const mainEle = document.createElement("main");
mainEle.classList.add("hljs"); mainEle.classList.add("hljs");
mainEle.classList.add("centered"); mainEle.classList.add("centered");
mainEle.classList.add("fullscreen"); mainEle.classList.add("fullscreen");
let divEle = document.createElement("div"); const divEle = document.createElement("div");
divEle.classList.add("centered"); divEle.classList.add("centered");
let expirationEle = document.createElement("p"); const expirationEle = document.createElement("p");
expirationEle.textContent = data.expiration; expirationEle.textContent = data.expiration;
divEle.appendChild(expirationEle); divEle.appendChild(expirationEle);
let downloadEle = document.createElement("a"); const downloadEle = document.createElement("a");
downloadEle.href = URL.createObjectURL(data.data); downloadEle.href = getObjectUrl(data.data, name);
downloadEle.download = window.location.pathname; downloadEle.download = name;
downloadEle.classList.add("hljs-meta"); downloadEle.classList.add("hljs-meta");
downloadEle.textContent = "Download binary file."; downloadEle.textContent = "Download binary file.";
divEle.appendChild(downloadEle); divEle.appendChild(downloadEle);
mainEle.appendChild(divEle); mainEle.appendChild(divEle);
let displayAnywayEle = document.createElement("p"); const displayAnywayEle = document.createElement("p");
displayAnywayEle.classList.add("display-anyways"); displayAnywayEle.classList.add("display-anyways");
displayAnywayEle.classList.add("hljs-comment"); displayAnywayEle.classList.add("hljs-comment");
displayAnywayEle.textContent = "Display anyways?"; displayAnywayEle.textContent = "Display anyways?";
@ -145,41 +158,41 @@ function createBlobPasteUi(data) {
bodyEle.appendChild(mainEle); bodyEle.appendChild(mainEle);
} }
function createImagePasteUi({ expiration, data, file_size }) { function createImagePasteUi({ expiration, data, file_size }, name?: string) {
createMultiMediaPasteUi("img", expiration, data, (downloadEle, imgEle) => { createMultiMediaPasteUi("img", expiration, data, name, (downloadEle, imgEle) => {
imgEle.onload = () => { imgEle.onload = () => {
let width = imgEle.naturalWidth || imgEle.width; const width = imgEle.naturalWidth || imgEle.width;
let height = imgEle.naturalHeight || imgEle.height; const height = imgEle.naturalHeight || imgEle.height;
downloadEle.textContent = "Download " + file_size + " \u2014 " + width + " by " + height; downloadEle.textContent = "Download " + file_size + " \u2014 " + width + " by " + height;
} }
}); });
} }
function createAudioPasteUi({ expiration, data }) { function createAudioPasteUi({ expiration, data }, name?: string) {
createMultiMediaPasteUi("audio", expiration, data, "Download"); createMultiMediaPasteUi("audio", expiration, data, name, "Download");
} }
function createVideoPasteUi({ expiration, data }) { function createVideoPasteUi({ expiration, data }, name?: string) {
createMultiMediaPasteUi("video", expiration, data, "Download"); createMultiMediaPasteUi("video", expiration, data, name, "Download");
} }
function createArchivePasteUi({ expiration, data, entries }) { function createArchivePasteUi({ expiration, data, entries }, name?: string) {
let bodyEle = document.getElementsByTagName("body")[0]; const bodyEle = document.getElementsByTagName("body")[0];
bodyEle.textContent = ''; bodyEle.textContent = '';
let mainEle = document.createElement("main"); const mainEle = document.createElement("main");
let sectionEle = document.createElement("section"); const sectionEle = document.createElement("section");
sectionEle.classList.add("paste"); sectionEle.classList.add("paste");
let expirationEle = document.createElement("p"); const expirationEle = document.createElement("p");
expirationEle.textContent = expiration; expirationEle.textContent = expiration;
expirationEle.classList.add("centered"); expirationEle.classList.add("centered");
sectionEle.appendChild(expirationEle); sectionEle.appendChild(expirationEle);
let downloadEle = document.createElement("a"); const downloadEle = document.createElement("a");
downloadEle.href = URL.createObjectURL(data); downloadEle.href = getObjectUrl(data);
downloadEle.download = window.location.pathname; downloadEle.download = name;
downloadEle.textContent = "Download"; downloadEle.textContent = "Download";
downloadEle.classList.add("hljs-meta"); downloadEle.classList.add("hljs-meta");
downloadEle.classList.add("centered"); downloadEle.classList.add("centered");
@ -187,7 +200,7 @@ function createArchivePasteUi({ expiration, data, entries }) {
sectionEle.appendChild(document.createElement("hr")); sectionEle.appendChild(document.createElement("hr"));
let mediaEle = document.createElement("table"); const mediaEle = document.createElement("table");
mediaEle.classList.add("archive-table"); mediaEle.classList.add("archive-table");
const tr = mediaEle.insertRow(); const tr = mediaEle.insertRow();
tr.classList.add("hljs-title"); tr.classList.add("hljs-title");
@ -224,30 +237,30 @@ function createArchivePasteUi({ expiration, data, entries }) {
bodyEle.appendChild(mainEle); bodyEle.appendChild(mainEle);
} }
function createMultiMediaPasteUi(tag, expiration, data, on_create?) { function createMultiMediaPasteUi(tag, expiration, data, name?: string, on_create?) {
let bodyEle = document.getElementsByTagName("body")[0]; const bodyEle = document.getElementsByTagName("body")[0];
bodyEle.textContent = ''; bodyEle.textContent = '';
let mainEle = document.createElement("main"); const mainEle = document.createElement("main");
mainEle.classList.add("hljs"); mainEle.classList.add("hljs");
mainEle.classList.add("centered"); mainEle.classList.add("centered");
mainEle.classList.add("fullscreen"); mainEle.classList.add("fullscreen");
const downloadLink = URL.createObjectURL(data); const downloadLink = getObjectUrl(data, name);
let expirationEle = document.createElement("p"); const expirationEle = document.createElement("p");
expirationEle.textContent = expiration; expirationEle.textContent = expiration;
mainEle.appendChild(expirationEle); mainEle.appendChild(expirationEle);
let mediaEle = document.createElement(tag); const mediaEle = document.createElement(tag);
mediaEle.src = downloadLink; mediaEle.src = downloadLink;
mediaEle.controls = true; mediaEle.controls = true;
mainEle.appendChild(mediaEle); mainEle.appendChild(mediaEle);
let downloadEle = document.createElement("a"); const downloadEle = document.createElement("a");
downloadEle.href = downloadLink; downloadEle.href = downloadLink;
downloadEle.download = window.location.pathname; downloadEle.download = name;
downloadEle.classList.add("hljs-meta"); downloadEle.classList.add("hljs-meta");
mainEle.appendChild(downloadEle); mainEle.appendChild(downloadEle);
@ -261,9 +274,9 @@ function createMultiMediaPasteUi(tag, expiration, data, on_create?) {
} }
function renderMessage(message) { function renderMessage(message) {
let body = document.getElementsByTagName("body")[0]; const body = document.getElementsByTagName("body")[0];
body.textContent = ''; body.textContent = '';
let mainEle = document.createElement("main"); const mainEle = document.createElement("main");
mainEle.classList.add("hljs"); mainEle.classList.add("hljs");
mainEle.classList.add("centered"); mainEle.classList.add("centered");
mainEle.classList.add("fullscreen"); mainEle.classList.add("fullscreen");
@ -271,4 +284,10 @@ function renderMessage(message) {
body.appendChild(mainEle); body.appendChild(mainEle);
} }
function getObjectUrl(data, mimeType?: string) {
return URL.createObjectURL(new Blob(data, {
type: mimeType,
}));
}
window.addEventListener("hashchange", () => location.reload()); window.addEventListener("hashchange", () => location.reload());