import { fileTypeFromBuffer } from "file-type"; import hljs from "highlight.js"; import { decrypt } from "./crypto"; import { getMode, humanFileSize, isValidUTF8, setupModeRadios } from "./shared"; async function showContent(content: Uint8Array, filetype: string) { const filesize = document.getElementById("filesize") as HTMLDivElement | null; if (filesize) { filesize.textContent = `size: ${humanFileSize(content.byteLength)}`; } const mediaBox = document.getElementById("mediabox") as HTMLDivElement | null; if (!mediaBox) { console.error("Missing mediaBox element."); return; } let preview: HTMLElement = document.createElement("div"); preview.textContent = "This file can't be previewed"; if (isValidUTF8(content) && filetype !== "blob") { preview = document.createElement("pre"); if (filetype === "none") { preview.textContent = new TextDecoder().decode(content); } else { preview.innerHTML = hljs.highlight( new TextDecoder("utf-8").decode(content), { language: filetype }, ).value; } } else { const filetype = await fileTypeFromBuffer(content); if (filetype) { const blob = new Blob([content as BlobPart], { type: filetype.mime }); const url = URL.createObjectURL(blob); if (filetype.mime.startsWith("audio/")) { preview = document.createElement("audio"); preview.setAttribute("controls", ""); preview.setAttribute("src", url); } else if (filetype.mime.startsWith("video/")) { preview = document.createElement("video"); preview.setAttribute("controls", ""); preview.setAttribute("src", url); } else if (filetype.mime.startsWith("image/")) { preview = document.createElement("img"); preview.setAttribute("src", url); } } } mediaBox.innerHTML = ""; mediaBox.appendChild(preview); } let encrypted: Uint8Array | undefined; let content: Uint8Array; let filetype: string; let filename: string; const uuid = window.location.pathname .split("/") .filter((x) => x !== "") .reverse()[0]; // Remembers the client-side password for this file for the rest of the browser // session, mirroring the server-side password cookie so the prompt only appears // once per session. const PASSWORD_KEY = `zbin-pw-${uuid}`; // Called from the overlay form's onsubmit. In server mode we let the form POST // the password to /set-cookie (native submit). In client mode we intercept, // fetch the still-encrypted bytes, and decrypt locally so the password never // leaves the browser. function onPasswordSubmit(): boolean { if (getMode("decrypt_mode") === "server") { return true; } const passwordInput = document.getElementById( "password", ) as HTMLInputElement | null; const passwordLabel = document.getElementById( "password-label", ) as HTMLLabelElement | null; if (passwordInput) { void decryptClientSide(passwordInput.value, passwordLabel); } return false; } async function decryptClientSide( password: string, passwordLabel: HTMLLabelElement | null, ): Promise { if (!encrypted) { const response = await fetch(`/raw/${uuid}?ignore_password=true`); encrypted = new Uint8Array(await response.arrayBuffer()); filetype = response.headers.get("filetype") || "none"; filename = response.headers.get("filename") || ""; } try { content = await decrypt(encrypted, password); } catch (_e) { if (passwordLabel) passwordLabel.textContent = "Incorrect password"; return false; } sessionStorage.setItem(PASSWORD_KEY, password); document.getElementById("decrypt-overlay")?.remove(); const downloadForm = document.getElementById( "download-form", ) as HTMLFormElement | null; downloadForm?.setAttribute("action", ""); downloadForm?.addEventListener("submit", (e) => { e.preventDefault(); const blob = new Blob([content as BlobPart]); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.download = filename; link.href = url; link.click(); }); showContent(content, filetype); return true; } setupModeRadios("decrypt_mode"); // If we already decrypted this file this session, auto-decrypt with the stored // password instead of prompting again. Drop a stale password if it no longer works. const savedPassword = sessionStorage.getItem(PASSWORD_KEY); if (savedPassword) { decryptClientSide(savedPassword, null).then((ok) => { if (!ok) sessionStorage.removeItem(PASSWORD_KEY); }); } Object.assign(window, { onPasswordSubmit });