diff --git a/CHANGELOG.md b/CHANGELOG.md index f2b405b1..1e8e7af2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,15 @@ ## 1.7.7 (not yet released) * ADDED: Switching templates using the web ui (#1501) +* ADDED: Show file name and size on download page (#603) * CHANGED: Passing large data structures by reference to reduce memory consumption (#858) * CHANGED: Removed use of ctype functions and polyfill library for ctype * CHANGED: Upgrading libraries to: DOMpurify 3.2.6, ip-lib 1.20.0 * CHANGED: Support for multiple file uploads (#1060) * CHANGED: Documented CSP change necessary to allow PDF attachment preview (#1552) * FIXED: Hide Reply button in the discussions once clicked to avoid losing the text input (#1508) +* FIXED: Bump zlib library suffix, ensuring cache refresh for WASM streaming change +* FIXED: Handle undefined globals in file based persisted values (#1544) ## 1.7.6 (2025-02-01) * ADDED: Ability to copy the paste by clicking the copy icon button or using the keyboard shortcut ctrl+c/cmd+c (#1390 & #12) diff --git a/CREDITS.md b/CREDITS.md index 308a424a..37026898 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -5,6 +5,7 @@ * Simon Rupf - current developer and maintainer * rugk - security review, doc improvment, JS refactoring & various other stuff * R4SAS - python client, compression, blob URI to support larger attachments +* Mikhail Romanov - UI improvements, theme switching, clipboard support, multi-file upload, bugfixes, code refactoring ## Past contributions @@ -33,7 +34,6 @@ * Mounir Idrassi & J. Mozdzen - secure YOURLS integration * Felipe Nakandakari - enabled AWS SDK to use default credential provider chain in the S3 Storage backend * Aaron Sherber - cache control headers for API calls & use of `shortenviayourls` in query parameters -* Mikhail Romanov - UI improvements, theme switching, clipboard support, multi-file upload, bugfixes, code refactoring ## Translations * Hexalyse - French diff --git a/i18n/en.json b/i18n/en.json index d1b2a46f..6cd88118 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -164,6 +164,14 @@ "EiB": "EiB", "ZiB": "ZiB", "YiB": "YiB", + "kB": "kB", + "MB": "MB", + "GB": "GB", + "TB": "TB", + "PB": "PB", + "EB": "EB", + "ZB": "ZB", + "YB": "YB", "Format": "Format", "Plain Text": "Plain Text", "Source Code": "Source Code", diff --git a/js/common.js b/js/common.js index b161b85d..a7efe670 100644 --- a/js/common.js +++ b/js/common.js @@ -11,7 +11,7 @@ global.WebCrypto = require('@peculiar/webcrypto').Crypto; // application libraries to test global.$ = global.jQuery = require('./jquery-3.7.1'); global.RawDeflate = require('./rawinflate-0.3').RawDeflate; -global.zlib = require('./zlib-1.3.1').zlib; +global.zlib = require('./zlib-1.3.1-1').zlib; require('./prettify'); global.prettyPrint = window.PR.prettyPrint; global.prettyPrintOne = window.PR.prettyPrintOne; diff --git a/js/privatebin.js b/js/privatebin.js index cfe1e4c6..7298db65 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -591,6 +591,32 @@ jQuery.PrivateBin = (function($, RawDeflate) { return expirationDate; }; + /** + * Convert Bytes to KiB/MiB/GiB + * + * @name Helper.formatBytes + * @function + * + * @param {number} bytes + * @return {string} + */ + me.formatBytes = function (bytes) + { + let result = ''; + const kilobyte = 1024; + const decimalPoint = 2; + const sizes = [I18n._('B'), I18n._('KiB'), I18n._('MiB'), I18n._('GiB')]; + const index = Math.floor(Math.log(bytes) / Math.log(kilobyte)); + + if (bytes > 0) { + result = parseFloat((bytes / Math.pow(kilobyte, index)).toFixed(decimalPoint)) + ' ' + sizes[index]; + } else { + result = `0 ${I18n._('B')}`; + } + + return result; + } + /** * resets state, used for unit testing * @@ -3000,7 +3026,9 @@ jQuery.PrivateBin = (function($, RawDeflate) { if (typeof fileName !== 'undefined') { attachmentLink.attr('download', fileName); - template.append(fileName); + + const fileSize = Helper.formatBytes(decodedData.length); + template.append(`(${fileName}, ${fileSize})`); } // sanitize SVG preview diff --git a/js/test/Helper.js b/js/test/Helper.js index ab72a6ad..6308fdc0 100644 --- a/js/test/Helper.js +++ b/js/test/Helper.js @@ -290,5 +290,31 @@ describe('Helper', function () { } ); }); + + describe('formatBytes', function () { + jsc.property('returns 0 B for 0 bytes', function () { + return $.PrivateBin.Helper.formatBytes(0) === '0 B'; + }); + + jsc.property('formats bytes < 1000 as B', function () { + return $.PrivateBin.Helper.formatBytes(500) === '500 B'; + }); + + jsc.property('formats kibibytes correctly', function () { + return $.PrivateBin.Helper.formatBytes(1500) === '1.46 KiB'; + }); + + jsc.property('formats mebibytes correctly', function () { + return $.PrivateBin.Helper.formatBytes(2 * 1000 * 1000) === '1.91 MiB'; + }); + + jsc.property('formats gibibytes correctly', function () { + return $.PrivateBin.Helper.formatBytes(3.45 * 1000 * 1000 * 1000) === '3.21 GiB'; + }); + + jsc.property('rounds to two decimal places', function () { + return $.PrivateBin.Helper.formatBytes(1234567) === '1.18 MiB'; + }); + }); }); diff --git a/js/zlib-1.3.1.js b/js/zlib-1.3.1-1.js similarity index 100% rename from js/zlib-1.3.1.js rename to js/zlib-1.3.1-1.js diff --git a/lib/Configuration.php b/lib/Configuration.php index c39f71f5..d54ebfdc 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -119,11 +119,11 @@ class Configuration 'js/kjua-0.9.0.js' => 'sha512-CVn7af+vTMBd9RjoS4QM5fpLFEOtBCoB0zPtaqIDC7sF4F8qgUSRFQQpIyEDGsr6yrjbuOLzdf20tkHHmpaqwQ==', 'js/legacy.js' => 'sha512-UxW/TOZKon83n6dk/09GsYKIyeO5LeBHokxyIq+r7KFS5KMBeIB/EM7NrkVYIezwZBaovnyNtY2d9tKFicRlXg==', 'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==', - 'js/privatebin.js' => 'sha512-m6RrsOsz4RgIWXDzgRghQDx6aegFCpkpqURwhfXwE/rNWhe/1rPJaLR+FXII82iTWo0n9JCzSbqrDqkYVPI50w==', + 'js/privatebin.js' => 'sha512-zvJ6Feu2NvROB236BBxbP+8eYbUTJ5GCfhOJVL/RI6pJQpR3AS4ps0d1cVDqgUFW8wY0tiwE7JTE13gPWO3lHA==', 'js/purify-3.2.6.js' => 'sha512-zqwL4OoBLFx89QPewkz4Lz5CSA2ktU+f31fuECkF0iK3Id5qd3Zpq5dMby8KwHjIEpsUgOqwF58cnmcaNem0EA==', 'js/rawinflate-0.3.js' => 'sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==', 'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==', - 'js/zlib-1.3.1.js' => 'sha512-5bU9IIP4PgBrOKLZvGWJD4kgfQrkTz8Z3Iqeu058mbQzW3mCumOU6M3UVbVZU9rrVoVwaW4cZK8U8h5xjF88eQ==', + 'js/zlib-1.3.1-1.js' => 'sha512-5bU9IIP4PgBrOKLZvGWJD4kgfQrkTz8Z3Iqeu058mbQzW3mCumOU6M3UVbVZU9rrVoVwaW4cZK8U8h5xjF88eQ==', ), ); diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index ac91db11..85cd2a5e 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -276,7 +276,7 @@ class Filesystem extends AbstractData case 'purge_limiter': return $this->_storeString( $this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php', - '_storeString( @@ -308,7 +308,9 @@ class Filesystem extends AbstractData $file = $this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php'; if (is_readable($file)) { require $file; - return $GLOBALS['purge_limiter']; + if (array_key_exists('purge_limiter', $GLOBALS)) { + return $GLOBALS['purge_limiter']; + } } break; case 'salt': @@ -324,9 +326,11 @@ class Filesystem extends AbstractData $file = $this->_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php'; if (is_readable($file)) { require $file; - $this->_last_cache = $GLOBALS['traffic_limiter']; - if (array_key_exists($key, $this->_last_cache)) { - return $this->_last_cache[$key]; + if (array_key_exists('traffic_limiter', $GLOBALS)) { + $this->_last_cache = $GLOBALS['traffic_limiter']; + if (array_key_exists($key, $this->_last_cache)) { + return $this->_last_cache[$key]; + } } } break; diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 570f9ca5..9cefe0e0 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -55,7 +55,7 @@ if ($ZEROBINCOMPATIBILITY) : - _scriptTag('js/zlib-1.3.1.js', 'async'); ?> + _scriptTag('js/zlib-1.3.1-1.js', 'async'); ?> _scriptTag('js/base-x-4.0.0.js', 'defer'); ?> _scriptTag('js/rawinflate-0.3.js', 'defer'); ?> _scriptTag('js/bootstrap-3.4.1.js', 'defer'); ?> diff --git a/tpl/bootstrap5.php b/tpl/bootstrap5.php index 6aecaf20..c92f942d 100644 --- a/tpl/bootstrap5.php +++ b/tpl/bootstrap5.php @@ -38,7 +38,7 @@ if ($ZEROBINCOMPATIBILITY) : - _scriptTag('js/zlib-1.3.1.js', 'defer'); ?> + _scriptTag('js/zlib-1.3.1-1.js', 'defer'); ?> _scriptTag('js/base-x-4.0.0.js', 'defer'); ?> _scriptTag('js/rawinflate-0.3.js', 'defer'); ?> _scriptTag('js/bootstrap-5.3.3.js', 'async'); ?> diff --git a/tpl/page.php b/tpl/page.php index 38f1292d..ccbdb116 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -34,7 +34,7 @@ if ($ZEROBINCOMPATIBILITY): - _scriptTag('js/zlib-1.3.1.js', 'async'); ?> + _scriptTag('js/zlib-1.3.1-1.js', 'async'); ?> _scriptTag('js/base-x-4.0.0.js', 'async'); ?> _scriptTag('js/rawinflate-0.3.js', 'async'); ?> assertEquals($this->_model->readComments($dataid), array($comment['meta']['created'] => $comment), "comment of $dataid wasn't modified in the conversion"); } } + + public function testValueFileErrorHandling() + { + define('VALID', 'valid content'); + foreach (array('purge_limiter', 'salt', 'traffic_limiter') as $namespace) { + file_put_contents($this->_invalidPath . DIRECTORY_SEPARATOR . $namespace . '.php', 'invalid content'); + $model = new Filesystem(array('dir' => $this->_invalidPath)); + ob_start(); // hide "invalid content", when file gets included + $this->assertEquals($model->getValue($namespace), '', 'empty default value returned, invalid content ignored'); + ob_end_clean(); + $this->assertTrue($model->setValue(VALID, $namespace), 'setting valid value'); + $this->assertEquals($model->getValue($namespace), VALID, 'valid value returned'); + } + } }