diff --git a/CHANGELOG.md b/CHANGELOG.md index ae352128..af2026dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ * ADDED: Translations for Swedish & Persian * CHANGED: Deduplicate JSON error message translations * CHANGED: Refactored translation of exception messages -* CHANGED: Upgrading libraries to: DOMpurify 3.3.2, ip-lib 1.22.0 & polyfill-php80 1.33.0 +* CHANGED: Upgrading libraries to: DOMpurify 3.3.2, ip-lib 1.22.0, polyfill-php80 1.33.0 & zlib 1.3.2 * FIXED: Some exceptions not getting translated * FIXED: Attachment disappears after a "paste" in the message area (#1731) * FIXED: The content format is not reset when creating a new document (#1707) diff --git a/js/common.js b/js/common.js index 36b5cdaa..c360eef4 100644 --- a/js/common.js +++ b/js/common.js @@ -10,7 +10,7 @@ global.WebCrypto = require('@peculiar/webcrypto').Crypto; // application libraries to test global.$ = global.jQuery = require('./jquery-3.7.1'); -global.zlib = require('./zlib-1.3.1-2').zlib; +global.zlib = require('./zlib').zlib; require('./prettify'); global.prettyPrint = window.PR.prettyPrint; global.prettyPrintOne = window.PR.prettyPrintOne; diff --git a/js/zlib-1.3.1.wasm b/js/zlib-1.3.1.wasm deleted file mode 100644 index 2fbc9169..00000000 Binary files a/js/zlib-1.3.1.wasm and /dev/null differ diff --git a/js/zlib-1.3.2.mjs b/js/zlib-1.3.2.mjs new file mode 100644 index 00000000..b4997f84 --- /dev/null +++ b/js/zlib-1.3.2.mjs @@ -0,0 +1,2 @@ +async function Module(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["d"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("zlib.wasm")}return new URL("zlib.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var abortOnCannotGrowMemory=requestedSize=>{abort("OOM")};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;abortOnCannotGrowMemory(requestedSize)};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}function writeToJs(ptr,dst,size){const o=Module.map[ptr];if(o&&typeof o.onData==="function"){o.onData(Module.HEAPU8.subarray(dst,dst+size))}else{console.error("No handler found for pointer:",ptr)}}var __malloc,__free,__createDeflateContext,__deflate,__freeDeflateContext,__createInflateContext,__inflate,__freeInflateContext,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){__malloc=Module["__malloc"]=wasmExports["e"];__free=Module["__free"]=wasmExports["f"];__createDeflateContext=Module["__createDeflateContext"]=wasmExports["g"];__deflate=Module["__deflate"]=wasmExports["h"];__freeDeflateContext=Module["__freeDeflateContext"]=wasmExports["i"];__createInflateContext=Module["__createInflateContext"]=wasmExports["j"];__inflate=Module["__inflate"]=wasmExports["k"];__freeInflateContext=Module["__freeInflateContext"]=wasmExports["l"];memory=wasmMemory=wasmExports["c"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={b:_emscripten_resize_heap,a:writeToJs};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} +;return moduleRtn}export default Module; diff --git a/js/zlib-1.3.2.wasm b/js/zlib-1.3.2.wasm new file mode 100644 index 00000000..7c5b0674 Binary files /dev/null and b/js/zlib-1.3.2.wasm differ diff --git a/js/zlib-1.3.1-2.js b/js/zlib.js similarity index 58% rename from js/zlib-1.3.1-2.js rename to js/zlib.js index 3e38c3ea..c59b620b 100644 --- a/js/zlib-1.3.1-2.js +++ b/js/zlib.js @@ -9,37 +9,22 @@ const COMPRESSION_LEVEL = 7; const NO_ZLIB_HEADER = -1; const CHUNK_SIZE = 32 * 1024; - const map = {}; - const memory = new WebAssembly.Memory({ - initial: 1, - maximum: 1024, // 64MB - }); - const env = { - memory, - writeToJs(ptr, size) { - const o = map[ptr]; - o.onData(new Uint8Array(memory.buffer, dstPtr, size)); - }, - _abort: errno => { console.error(`Error: ${errno}`) }, - _grow: () => { }, - }; - const ins = (await WebAssembly.instantiateStreaming(fetch('js/zlib-1.3.1.wasm'), { env })).instance; - - const srcPtr = ins.exports._malloc(CHUNK_SIZE); - const dstPtr = ins.exports._malloc(CHUNK_SIZE); + const Module = await import('zlib-1.3.2.mjs'); + Module.map = {}; + const srcPtr = Module.__malloc(CHUNK_SIZE); + const dstPtr = Module.__malloc(CHUNK_SIZE); class RawDef { constructor() { - this.zstreamPtr = ins.exports._createDeflateContext(COMPRESSION_LEVEL, NO_ZLIB_HEADER); - map[this.zstreamPtr] = this; + this.zstreamPtr = Module.__createDeflateContext(COMPRESSION_LEVEL, NO_ZLIB_HEADER); + Module.map[this.zstreamPtr] = this; this.offset = 0; this.buff = new Uint8Array(CHUNK_SIZE); } deflate(chunk, flush) { - const src = new Uint8Array(memory.buffer, srcPtr, chunk.length); - src.set(chunk); - ins.exports._deflate(this.zstreamPtr, srcPtr, dstPtr, chunk.length, CHUNK_SIZE, flush); + Module.HEAPU8.set(chunk, srcPtr); + Module.__deflate(this.zstreamPtr, srcPtr, dstPtr, chunk.length, CHUNK_SIZE, flush); } onData(chunk) { @@ -53,32 +38,27 @@ } destroy() { - ins.exports._freeDeflateContext(this.zstreamPtr); - delete map[this.zstreamPtr]; + Module.__freeDeflateContext(this.zstreamPtr); + delete Module.map[this.zstreamPtr]; this.buff = null; } getBuffer() { - const res = new Uint8Array(this.offset); - for (let i = 0; i < this.offset; ++i) { - res[i] = this.buff[i]; - } - return res; + return Buffer.from(this.buff.buffer, 0, this.offset); } } class RawInf { constructor() { - this.zstreamPtr = ins.exports._createInflateContext(NO_ZLIB_HEADER); - map[this.zstreamPtr] = this; + this.zstreamPtr = Module.__createInflateContext(NO_ZLIB_HEADER); + Module.map[this.zstreamPtr] = this; this.offset = 0; this.buff = new Uint8Array(CHUNK_SIZE); } inflate(chunk) { - const src = new Uint8Array(memory.buffer, srcPtr, chunk.length); - src.set(chunk); - ins.exports._inflate(this.zstreamPtr, srcPtr, dstPtr, chunk.length, CHUNK_SIZE); + Module.HEAPU8.set(chunk, srcPtr); + Module.__inflate(this.zstreamPtr, srcPtr, dstPtr, chunk.length, CHUNK_SIZE); } onData(chunk) { @@ -92,17 +72,13 @@ } destroy() { - ins.exports._freeInflateContext(this.zstreamPtr); - delete map[this.zstreamPtr]; + Module.__freeInflateContext(this.zstreamPtr); + delete Module.map[this.zstreamPtr]; this.buff = null; } getBuffer() { - const res = new Uint8Array(this.offset); - for (let i = 0; i < this.offset; ++i) { - res[i] = this.buff[i]; - } - return res; + return Buffer.from(this.buff.buffer, 0, this.offset); } } diff --git a/lib/Configuration.php b/lib/Configuration.php index 950cf069..1395048b 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -125,7 +125,8 @@ class Configuration 'js/privatebin.js' => 'sha512-6SwOJniNN8RBmAK7yCt4ly2qYyH8OALxB74/K1AJgw+YnZgRCfTDVq1qY1K5Y2QCxCODGGTpAjTqQRExzCqV7g==', 'js/purify-3.3.2.js' => 'sha512-I6igPVpf3xNghG92mujwqB6Zi3LpUTsni4bRuLnMThEGH6BDbsumv7373+AXHzA4OUlxGsym8ZxKFHy4xjYvkQ==', 'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==', - 'js/zlib-1.3.1-2.js' => 'sha512-4gT+v+BkBqdVBbKOO4qKGOAzuay+v1FmOLksS+bMgQ08Oo4xEb3X48Xq1Kv2b4HtiCQA7xq9dFRzxal7jmQI7w==', + 'js/zlib-1.3.2.mjs' => 'sha512-rv8vIHS886FB1BwNSDK2ePuAlQ2kjuCKyKfu6R6xrUaBW74XJwUkgBkYwb2Qo48JGcfenzJuJWnj7aunxXkyIw==', + 'js/zlib.js' => 'sha512-WrCFRc7+7FdFDhN86LQ0g8GHzwhGIqPJ29NBGu2xM3cqkCC9GSMZf/24dhg6+iQs1xjcnWjandBFIOP7xCBUpg==', ), ); diff --git a/lib/View.php b/lib/View.php index 514ac2b1..14b3d852 100644 --- a/lib/View.php +++ b/lib/View.php @@ -62,6 +62,49 @@ class View include $path; } + /** + * get cache buster query string + * + * if the file isn't versioned (ends in a digit), adds our own version as a query string + * + * @access private + * @param string $file + */ + private function _getCacheBuster($file) + { + // + if ((bool) preg_match('#[0-9]\.m?js$#', (string) $file)) { + return ''; + } + return '?' . rawurlencode($this->_variables['VERSION']); + } + + /** + * get SRI hash for given file + * + * @access private + * @param string $file + */ + private function _getSri($file) + { + if (array_key_exists($file, $this->_variables['SRI'])) { + return ' integrity="' . $this->_variables['SRI'][$file] . '"'; + } + return ''; + } + + /** + * echo module preload link tag incl. SRI hash for given script file + * + * @access private + * @param string $file + */ + private function _linkTag($file) + { + echo '_getSri($file), ' />', PHP_EOL; + } + /** * echo script tag incl. SRI hash for given script file * @@ -71,13 +114,9 @@ class View */ private function _scriptTag($file, $attributes = '') { - $sri = array_key_exists($file, $this->_variables['SRI']) ? - ' integrity="' . $this->_variables['SRI'][$file] . '"' : ''; - // if the file isn't versioned (ends in a digit), add our own version - $cacheBuster = (bool) preg_match('#[0-9]\.js$#', (string) $file) ? - '' : '?' . rawurlencode($this->_variables['VERSION']); echo '', PHP_EOL; + ' type="text/javascript" data-cfasync="false" src="', $file, + $this->_getCacheBuster($file), '"', $this->_getSri($file), + ' crossorigin="anonymous">', PHP_EOL; } } diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 283c84ab..64b1eae9 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -42,6 +42,7 @@ if ($SYNTAXHIGHLIGHTING) : endif; ?> + _linkTag('js/zlib-1.3.2.mjs'); ?> _scriptTag('js/jquery-3.7.1.js', 'defer'); ?> - _scriptTag('js/zlib-1.3.1-2.js', 'defer'); ?> + _scriptTag('js/zlib.js', 'defer'); ?> _scriptTag('js/base-x-5.0.1.js', 'defer'); ?> _scriptTag('js/bootstrap-3.4.1.js', 'defer'); ?> + _linkTag('js/zlib-1.3.2.mjs'); ?> _scriptTag('js/jquery-3.7.1.js', 'defer'); ?> - _scriptTag('js/zlib-1.3.1-2.js', 'defer'); ?> + _scriptTag('js/zlib.js', 'defer'); ?> _scriptTag('js/base-x-5.0.1.js', 'defer'); ?> _scriptTag('js/bootstrap-5.3.8.js', 'defer'); ?> _scriptTag('js/dark-mode-switch.js', 'defer'); ?> diff --git a/tst/Bootstrap.php b/tst/Bootstrap.php index ea83e791..0c24da89 100644 --- a/tst/Bootstrap.php +++ b/tst/Bootstrap.php @@ -341,7 +341,7 @@ class Helper */ public static function updateSubresourceIntegrity(): void { - foreach (new GlobIterator(PATH . 'js' . DIRECTORY_SEPARATOR . '*.js') as $file) { + foreach (new GlobIterator(PATH . 'js' . DIRECTORY_SEPARATOR . '*.*js') as $file) { if ($file->getBasename() === 'common.js' || $file->getBasename() === 'eslint.config.js') { continue; // ignore JS unit test bootstrap }