Compare commits

..

No commits in common. "master" and "1.7.6" have entirely different histories.

133 changed files with 3348 additions and 2918 deletions

View file

@ -43,7 +43,7 @@ jobs:
actions: read actions: read
id-token: write id-token: write
contents: write contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
with: with:
base64-subjects: "${{ needs.release.outputs.hashes }}" base64-subjects: "${{ needs.release.outputs.hashes }}"
draft-release: true draft-release: true

View file

@ -24,7 +24,7 @@ jobs:
steps: steps:
- name: Download and Extract Artifacts - name: Download and Extract Artifacts
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc
with: with:
run_id: ${{ github.event.workflow_run.id }} run_id: ${{ github.event.workflow_run.id }}
path: artifacts path: artifacts

View file

@ -121,7 +121,7 @@ jobs:
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18' node-version: '20'
cache: 'npm' cache: 'npm'
cache-dependency-path: 'js/package-lock.json' cache-dependency-path: 'js/package-lock.json'

View file

@ -2,7 +2,6 @@ RewriteEngine on
RewriteCond !%{HTTP_USER_AGENT} "Let's Encrypt validation server" [NC] RewriteCond !%{HTTP_USER_AGENT} "Let's Encrypt validation server" [NC]
RewriteCond %{HTTP_USER_AGENT} ^.*(bot|spider|crawl|https?://|WhatsApp|SkypeUriPreview|facebookexternalhit) [NC] RewriteCond %{HTTP_USER_AGENT} ^.*(bot|spider|crawl|https?://|WhatsApp|SkypeUriPreview|facebookexternalhit) [NC]
RewriteRule .* - [R=403,L] RewriteRule .* - [R=403,L]
AddType application/wasm .wasm
<IfModule mod_php7.c> <IfModule mod_php7.c>
php_value max_execution_time 30 php_value max_execution_time 30

View file

@ -1,27 +1,5 @@
# PrivateBin version history # PrivateBin version history
## 2.0.0 (not yet released)
* CHANGED: Remove page template (#265)
* FIXED: Name mismatches in attached files (#1584)
* FIXED: Unable to paste attachments from clipboard (#1589)
## 1.7.8 (2025-06-30)
* FIXED: Duplicate attachment for every comment (#1577)
* FIXED: Attachments with empty file names (#1577)
* FIXED: Page template scripts loading order (#1579)
## 1.7.7 (2025-06-28)
* 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) ## 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) * ADDED: Ability to copy the paste by clicking the copy icon button or using the keyboard shortcut ctrl+c/cmd+c (#1390 & #12)
* CHANGED: Allow toggling tab-key-support using `[Ctrl]+[m]` or `[Esc]` in textarea for keyboard navigation (#1386) * CHANGED: Allow toggling tab-key-support using `[Ctrl]+[m]` or `[Esc]` in textarea for keyboard navigation (#1386)

View file

@ -5,7 +5,6 @@
* Simon Rupf - current developer and maintainer * Simon Rupf - current developer and maintainer
* rugk - security review, doc improvment, JS refactoring & various other stuff * rugk - security review, doc improvment, JS refactoring & various other stuff
* R4SAS - python client, compression, blob URI to support larger attachments * 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 ## Past contributions
@ -34,6 +33,7 @@
* Mounir Idrassi & J. Mozdzen - secure YOURLS integration * Mounir Idrassi & J. Mozdzen - secure YOURLS integration
* Felipe Nakandakari - enabled AWS SDK to use default credential provider chain in the S3 Storage backend * 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 * Aaron Sherber - cache control headers for API calls & use of `shortenviayourls` in query parameters
* Mikhail Romanov - copying to clipboard, UI/UX improvements
## Translations ## Translations
* Hexalyse - French * Hexalyse - French

View file

@ -1,7 +1,7 @@
.PHONY: all coverage coverage-js coverage-php doc doc-js doc-php increment sign test test-js test-php help .PHONY: all coverage coverage-js coverage-php doc doc-js doc-php increment sign test test-js test-php help
CURRENT_VERSION = 1.7.8 CURRENT_VERSION = 1.7.6
VERSION ?= 2.0.0 VERSION ?= 1.7.7
VERSION_FILES = README.md SECURITY.md doc/Installation.md js/package*.json lib/Controller.php Makefile VERSION_FILES = README.md SECURITY.md doc/Installation.md js/package*.json lib/Controller.php Makefile
REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g") REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g")
REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g") REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g")

View file

@ -1,6 +1,6 @@
# [![PrivateBin](https://cdn.rawgit.com/PrivateBin/assets/master/images/preview/logoSmall.png)](https://privatebin.info/) # [![PrivateBin](https://cdn.rawgit.com/PrivateBin/assets/master/images/preview/logoSmall.png)](https://privatebin.info/)
*Current version: 1.7.8* *Current version: 1.7.6*
**PrivateBin** is a minimalist, open source online **PrivateBin** is a minimalist, open source online
[pastebin](https://en.wikipedia.org/wiki/Pastebin) [pastebin](https://en.wikipedia.org/wiki/Pastebin)

View file

@ -4,8 +4,8 @@
| Version | Supported | | Version | Supported |
| ------- | ------------------ | | ------- | ------------------ |
| 1.7.8 | :heavy_check_mark: | | 1.7.6 | :heavy_check_mark: |
| < 1.7.8 | :x: | | < 1.7.6 | :x: |
## Reporting a Vulnerability ## Reporting a Vulnerability
@ -14,8 +14,5 @@ a response within a week (usually during the next weekend). The respondee will
reply from their personal address and can offer you their GPG public key to reply from their personal address and can offer you their GPG public key to
support end-to-end encrypted communication on sensitive topics or attachments. support end-to-end encrypted communication on sensitive topics or attachments.
You can also [use the corresponding GitHub form](https://github.com/PrivateBin/PrivateBin/security/advisories/new)
to report a new vulnerability directly on GitHub.
You can also contact us via the regular issue tracker if the risk of early You can also contact us via the regular issue tracker if the risk of early
publication is low or you would request input from other PrivateBin users. publication is low or you would request input from other PrivateBin users.

View file

@ -72,35 +72,6 @@ class Administration
exit("paste $pasteId successfully deleted" . PHP_EOL); exit("paste $pasteId successfully deleted" . PHP_EOL);
} }
/**
* lists all stored paste IDs
*
* @access private
*/
private function _list_ids()
{
$ids = $this->_store->getAllPastes();
foreach ($ids as $pasteid) {
echo $pasteid, PHP_EOL;
}
exit;
}
/**
* deletes all stored pastes (regardless of expiration)
*
* @access private
*/
private function _delete_all()
{
$ids = $this->_store->getAllPastes();
foreach ($ids as $pasteid) {
echo "Deleting paste ID: $pasteid" . PHP_EOL;
$this->_store->delete($pasteid);
}
exit("All pastes successfully deleted" . PHP_EOL);
}
/** /**
* removes empty directories, if current storage model uses Filesystem * removes empty directories, if current storage model uses Filesystem
* *
@ -153,15 +124,13 @@ class Administration
{ {
echo <<<'EOT' echo <<<'EOT'
Usage: Usage:
administration [--delete <paste id> | --delete-all | --empty-dirs | --help | --list-ids | --purge | --statistics] administration [--delete <paste id> | --empty-dirs | --help | --purge | --statistics]
Options: Options:
-d, --delete deletes the requested paste ID -d, --delete deletes the requested paste ID
--delete-all deletes all paste IDs
-e, --empty-dirs removes empty directories (only if Filesystem storage is -e, --empty-dirs removes empty directories (only if Filesystem storage is
configured) configured)
-h, --help displays this help message -h, --help displays this help message
-l, --list-ids lists all paste IDs
-p, --purge purge all expired pastes -p, --purge purge all expired pastes
-s, --statistics reads all stored pastes and comments and reports statistics -s, --statistics reads all stored pastes and comments and reports statistics
EOT, PHP_EOL; EOT, PHP_EOL;
@ -208,8 +177,7 @@ EOT, PHP_EOL;
self::_help(2); self::_help(2);
} }
$this->_opts = getopt('hd:epsl', array('help', 'delete:', 'empty-dirs', 'purge', 'statistics', 'list-ids', 'delete-all')); $this->_opts = getopt('hd:eps', array('help', 'delete:', 'empty-dirs', 'purge', 'statistics'));
if (!$this->_opts) { if (!$this->_opts) {
self::_error_echo('unsupported arguments given'); self::_error_echo('unsupported arguments given');
echo PHP_EOL; echo PHP_EOL;
@ -340,12 +308,6 @@ EOT, PHP_EOL;
$class = 'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model'); $class = 'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model');
$this->_store = new $class($this->_conf->getSection('model_options')); $this->_store = new $class($this->_conf->getSection('model_options'));
if ($this->_option('l', 'list-ids') !== null) {
$this->_list_ids();
}
if ($this->_option(null, 'delete-all') !== null) {
$this->_delete_all();
}
if (($pasteId = $this->_option('d', 'delete')) !== null) { if (($pasteId = $this->_option('d', 'delete')) !== null) {
$this->_delete($pasteId); $this->_delete($pasteId);
} }

View file

@ -164,14 +164,14 @@ new ConfigurationTestGenerator(array(
), ),
'main/template' => array( 'main/template' => array(
array( array(
'setting' => 'bootstrap5', 'setting' => 'page',
'tests' => array( 'tests' => array(
array( array(
'type' => 'MatchesRegularExpression', 'type' => 'MatchesRegularExpression',
'args' => array( 'args' => array(
'#<link[^>]+type="text/css"[^>]+rel="stylesheet"[^>]+href="css/bootstrap5/privatebin\.css\\?\d[\d\.]+\d+"[^>]*/>#', '#<link[^>]+type="text/css"[^>]+rel="stylesheet"[^>]+href="css/privatebin\.css\\?\d[\d\.]+\d+"[^>]*/>#',
'$content', '$content',
'outputs "bootstrap5" stylesheet correctly', 'outputs "page" stylesheet correctly',
), ),
), array( ), array(
'type' => 'DoesNotMatchRegularExpression', 'type' => 'DoesNotMatchRegularExpression',
@ -189,9 +189,9 @@ new ConfigurationTestGenerator(array(
array( array(
'type' => 'DoesNotMatchRegularExpression', 'type' => 'DoesNotMatchRegularExpression',
'args' => array( 'args' => array(
'#<link[^>]+type="text/css"[^>]+rel="stylesheet"[^>]+href="css/bootstrap5/privatebin\.css\\?\d[\d\.]+\d+"[^>]*/>#', '#<link[^>]+type="text/css"[^>]+rel="stylesheet"[^>]+href="css/privatebin\.css\\?\d[\d\.]+\d+"[^>]*/>#',
'$content', '$content',
'removes "bootstrap5" stylesheet correctly', 'removes "page" stylesheet correctly',
), ),
), array( ), array(
'type' => 'MatchesRegularExpression', 'type' => 'MatchesRegularExpression',

View file

@ -42,26 +42,13 @@ defaultformatter = "plaintext"
; size limit per paste or comment in bytes, defaults to 10 Mebibytes ; size limit per paste or comment in bytes, defaults to 10 Mebibytes
sizelimit = 10485760 sizelimit = 10485760
; by default PrivateBin use "bootstrap" template (tpl/bootstrap.php). ; template to include, default is "bootstrap" (tpl/bootstrap.php), also
; Optionally you can enable the template selection menu, which uses ; available are "page" (tpl/page.php), the classic ZeroBin style and several
; a session cookie to store the choice until the browser is closed.
templateselection = false
; List of available for selection templates when "templateselection" option is enabled
availabletemplates[] = "bootstrap5"
availabletemplates[] = "bootstrap"
availabletemplates[] = "bootstrap-page"
availabletemplates[] = "bootstrap-dark"
availabletemplates[] = "bootstrap-dark-page"
availabletemplates[] = "bootstrap-compact"
availabletemplates[] = "bootstrap-compact-page"
; set the template your installs defaults to, defaults to "bootstrap" (tpl/bootstrap.php), also
; bootstrap variants: "bootstrap-dark", "bootstrap-compact", "bootstrap-page", ; bootstrap variants: "bootstrap-dark", "bootstrap-compact", "bootstrap-page",
; which can be combined with "-dark" and "-compact" for "bootstrap-dark-page", ; which can be combined with "-dark" and "-compact" for "bootstrap-dark-page"
; "bootstrap-compact-page" and finally "bootstrap5" (tpl/bootstrap5.php) - previews at: ; and finally "bootstrap-compact-page" - previews at:
; https://privatebin.info/screenshots.html ; https://privatebin.info/screenshots.html
; template = "bootstrap" template = "bootstrap"
; (optional) info text to display ; (optional) info text to display
; use single, instead of double quotes for HTML attributes ; use single, instead of double quotes for HTML attributes
@ -106,6 +93,8 @@ languageselection = false
; scripts or run your site behind certain DDoS-protection services. ; scripts or run your site behind certain DDoS-protection services.
; Check the documentation at https://content-security-policy.com/ ; Check the documentation at https://content-security-policy.com/
; Notes: ; Notes:
; - If you use any bootstrap theme, you can remove the allow-popups from the
; sandbox restrictions.
; - If you use the bootstrap5 theme, you must change default-src to 'self' to ; - If you use the bootstrap5 theme, you must change default-src to 'self' to
; enable display of the svg icons ; enable display of the svg icons
; - By default this disallows to load images from third-party servers, e.g. when ; - By default this disallows to load images from third-party servers, e.g. when
@ -114,15 +103,7 @@ languageselection = false
; for details. ; for details.
; - The 'wasm-unsafe-eval' is used to enable webassembly support (used for zlib ; - The 'wasm-unsafe-eval' is used to enable webassembly support (used for zlib
; compression). You can remove it if compression doesn't need to be supported. ; compression). You can remove it if compression doesn't need to be supported.
; - The 'unsafe-inline' style-src is used by Chrome when displaying PDF previews ; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; font-src 'self'; frame-ancestors 'none'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads"
; and can be omitted if attachment upload is disabled (which is the default).
; See https://issues.chromium.org/issues/343754409
; - To allow displaying PDF previews in Firefox or Chrome, sandboxing must also
; get turned off. The following CSP allows PDF previews:
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-ancestors 'none'; frame-src blob:; img-src 'self' data: blob:; media-src blob:; object-src blob:"
;
; The recommended and default used CSP is:
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; font-src 'self'; frame-ancestors 'none'; frame-src blob:; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-modals allow-downloads"
; stay compatible with PrivateBin Alpha 0.19, less secure ; stay compatible with PrivateBin Alpha 0.19, less secure
; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of ; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of

View file

@ -26,8 +26,9 @@
"require" : { "require" : {
"php": "^7.3 || ^8.0", "php": "^7.3 || ^8.0",
"jdenticon/jdenticon": "1.0.2", "jdenticon/jdenticon": "1.0.2",
"mlocati/ip-lib": "1.20.0", "mlocati/ip-lib": "1.18.1",
"symfony/polyfill-php80": "1.31.0", "symfony/polyfill-ctype": "^1.31",
"symfony/polyfill-php80": "^1.31",
"yzalis/identicon": "2.0.0" "yzalis/identicon": "2.0.0"
}, },
"suggest" : { "suggest" : {

105
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "6c7e6dea19e8bfd5641b220cb68c4b65", "content-hash": "0fdf0f08646fa2a4cf9c076131f529f5",
"packages": [ "packages": [
{ {
"name": "jdenticon/jdenticon", "name": "jdenticon/jdenticon",
@ -57,16 +57,16 @@
}, },
{ {
"name": "mlocati/ip-lib", "name": "mlocati/ip-lib",
"version": "1.20.0", "version": "1.18.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/mlocati/ip-lib.git", "url": "https://github.com/mlocati/ip-lib.git",
"reference": "fd45fc3bf08ed6c7e665e2e70562082ac954afd4" "reference": "08bb43b4949069c543ebdf099a6b2c322d0172ab"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/mlocati/ip-lib/zipball/fd45fc3bf08ed6c7e665e2e70562082ac954afd4", "url": "https://api.github.com/repos/mlocati/ip-lib/zipball/08bb43b4949069c543ebdf099a6b2c322d0172ab",
"reference": "fd45fc3bf08ed6c7e665e2e70562082ac954afd4", "reference": "08bb43b4949069c543ebdf099a6b2c322d0172ab",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -112,7 +112,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/mlocati/ip-lib/issues", "issues": "https://github.com/mlocati/ip-lib/issues",
"source": "https://github.com/mlocati/ip-lib/tree/1.20.0" "source": "https://github.com/mlocati/ip-lib/tree/1.18.1"
}, },
"funding": [ "funding": [
{ {
@ -124,7 +124,86 @@
"type": "other" "type": "other"
} }
], ],
"time": "2025-02-04T17:30:58+00:00" "time": "2024-10-29T15:44:19+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
@ -337,16 +416,16 @@
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
"version": "1.13.0", "version": "1.12.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/myclabs/DeepCopy.git", "url": "https://github.com/myclabs/DeepCopy.git",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414" "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414", "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -385,7 +464,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/myclabs/DeepCopy/issues", "issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
}, },
"funding": [ "funding": [
{ {
@ -393,7 +472,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-02-12T12:17:51+00:00" "time": "2024-11-08T17:47:46+00:00"
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",

View file

@ -8,12 +8,6 @@
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License * @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
*/ */
#attachmentPreview {
display: flex;
flex-direction: column;
align-items: center;
}
#attachmentPreview img { #attachmentPreview img {
max-width: 100%; max-width: 100%;
height: auto; height: auto;

460
css/privatebin.css Normal file
View file

@ -0,0 +1,460 @@
/**
* PrivateBin
*
* Cascading style sheets for page template.
*
* @link https://github.com/PrivateBin/PrivateBin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
*/
@import url("common.css");
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.
Licensed under the BSD License. - http://yuilibrary.com/license/ */
html{color:#000;background:#fff}body,div,dl,dt,dd,ul,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{font-size:100%;}legend{color:#000}
html {
background-color: #455463;
color: #fff;
min-height: 100%;
background-image: linear-gradient(bottom, #0f1823 0, #455463 100%);
background-image: -o-linear-gradient(bottom, #0f1823 0, #455463 100%);
background-image: -moz-linear-gradient(bottom, #0f1823 0, #455463 100%);
background-image: -webkit-linear-gradient(bottom, #0f1823 0, #455463 100%);
background-image: -ms-linear-gradient(bottom, #0f1823 0, #455463 100%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #0f1823), color-stop(1, #455463));
}
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 0.9em;
margin-bottom: 15px;
padding-left: 60px;
padding-right: 60px;
}
a { color: #0f388f; cursor:pointer; }
h1.title {
font-size: 3.5em;
font-weight: bold;
color: #000;
position: relative;
display: inline;
cursor: pointer;
}
h1.title:before {
content: attr(title);
position: absolute;
color: rgba(255,255,255,0.15);
top: 1px;
left: 1px;
cursor: pointer;
}
h2.title {
color: #000;
font-size: 1em;
display: inline;
font-style: italic;
font-weight: bold;
position: relative;
bottom: 8px;
}
h3.title {
color: #94a3b4;
font-size: 0.7em;
display: inline;
margin-top: 10px;
position: relative;
bottom: 8px;
}
#aboutbox {
color: #94a3b4;
padding: 4px 8px 4px 16px;
position: relative;
top: 10px;
border-left: 2px solid #94a3b4;
float: right;
width: 60%;
}
#aboutbox a { color: #94a3b4; }
#message, #cleartext, #prettymessage, #attachment, .replymessage {
position: relative;
clear: both;
color: #000;
background-color: #fff;
font-size: 9pt;
border: 1px solid #28343F;
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
width: 100%;
}
#message, .replymessage {
padding: 5px;
white-space: pre-wrap;
font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, monospace;
resize: vertical;
}
#status {
clear: both;
padding: 5px 10px;
}
#pasteresult {
background-color: #1F2833;
color: #fff;
padding: 4px 12px;
clear: both;
-moz-box-shadow: inset 0 2px 2px #000;
-webkit-box-shadow: inset 0 2px 2px #000;
box-shadow: inset 0 2px 2px #000;
}
#pasteresult a { color: #fff; }
#pasteresult button { margin-left: 11px; }
#message, #plaintext, #prettymessage, #toolbar, #status { margin-bottom: 5px; }
#copyhint { color: #666; font-size: 0.85em }
button, .button {
color: #fff;
background-color: #323b47;
background-repeat: no-repeat;
background-position: center left;
padding: 4px 8px;
font-size: 1em;
margin-right: 5px;
display: inline-block;
background-image: linear-gradient(bottom, #323b47 0, #51606e 100%);
background-image: -o-linear-gradient(bottom, #323b47 0, #51606e 100%);
background-image: -moz-linear-gradient(bottom, #323b47 0, #51606e 100%);
background-image: -webkit-linear-gradient(bottom, #323b47 0, #51606e 100%);
background-image: -ms-linear-gradient(bottom, #323b47 0, #51606e 100%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #323b47), color-stop(1, #51606e));
border: 1px solid #28343F;
-moz-box-shadow: inset 0 1px 2px #647384;
-webkit-box-shadow: inset 0 1px 2px #647384;
box-shadow: inset 0 1px 2px #647384;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-moz-background-clip: padding;
-webkit-background-clip: padding-box;
background-clip: padding-box;
}
button:hover {
background-image: linear-gradient(bottom, #424b57 0%, #61707e 100%);
background-image: -o-linear-gradient(bottom, #424b57 0%, #61707e 100%);
background-image: -moz-linear-gradient(bottom, #424b57 0%, #61707e 100%);
background-image: -webkit-linear-gradient(bottom, #424b57 0%, #61707e 100%);
background-image: -ms-linear-gradient(bottom, #424b57 0%, #61707e 100%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #424b57), color-stop(1, #61707e));
}
button:active {
background-image: linear-gradient(bottom, #51606e 0, #323b47 100%);
background-image: -o-linear-gradient(bottom, #51606e 0, #323b47 100%);
background-image: -moz-linear-gradient(bottom, #51606e 0, #323b47 100%);
background-image: -webkit-linear-gradient(bottom, #51606e 0, #323b47 100%);
background-image: -ms-linear-gradient(bottom, #51606e 0, #323b47 100%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #51606e), color-stop(1, #323b47));
position:relative;
top:1px;
}
button:disabled, .buttondisabled {
background: #ccc;
color: #888;
top: 0;
}
button img {
margin-right: 8px;
position: relative;
top: 2px;
}
.button {
background-color: #414d5a;
padding: 6px 8px;
margin: 0 5px 0 0;
position: relative;
bottom: 1px; /* WTF ? Why is this shifted by 1 pixel ? */
}
.button select {
color: #eee;
background: transparent;
border: none;
}
.button select option {
color:#eee;
background: #414d5a;
}
#rawtextbutton img {
padding: 1px 0 1px 0;
}
#downloadtextbutton img {
padding: 1px 0 1px 0;
}
#remainingtime, #password {
color: #94a3b4;
display: inline;
font-size: 0.85em;
}
#newbutton {
float: right;
margin-left: 0;
margin-right: 0;
margin-bottom: 5px;
display: inline;
}
input {
color: #777;
font-size: 1em;
padding: 6px;
border: 1px solid #28343f;
}
.blink {
text-decoration: blink;
font-size: 0.8em;
color: #a4b3c4;
}
.foryoureyesonly {
color: #ff0 !important;
font-size: 1em !important;
font-weight: bold !important;
}
#attachmentPreview, .nonworking {
background-color: #fff;
color: #000;
width: 100%;
text-align: center;
font-weight: bold;
font-size: 10pt;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
padding: 5px 0;
}
.hidden { display: none !important; }
#ienotice {
background-color: #7e98af;
color: #000;
font-size: 0.85em;
padding: 3px 5px;
text-align: center;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
display: none;
}
#ienotice a { color: #000; }
#oldnotice, #httpnotice { display: none; }
#errormessage, .errorMessage {
background-color: #f77 !important;
color:#ff0;
}
.small {
font-size: 80%;
}
/* --- discussion related CSS ------- */
#discussion { /* Discussion container */
margin-top: 20px;
width: 100%;
margin-left: -30px;
min-width: 200px;
}
h4.title {
font-size: 1.2em;
color: #94a3b4;
font-style: italic;
font-weight: bold;
position: relative;
margin-left: 30px;
}
.comment /* One single reply */
{
background-color: #ceced6;
color: #000;
white-space: pre-wrap;
font-family: Consolas,"Lucida Console","DejaVu Sans Mono",Monaco,monospace;
font-size: 9pt;
border-left: 1px solid #859AAE;
border-top: 1px solid #859AAE;
padding: 5px 0px 5px 5px;
margin-left: 30px;
-moz-box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
-webkit-box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
min-width: 200px;
overflow: auto;
}
.reply { margin: 5px 0 0 30px; }
#replystatus {
display: inline;
padding: 1px 7px;
font-family: Arial, Helvetica, sans-serif;
}
.comment button {
color: #446;
background-color: #aab;
background-repeat: no-repeat;
background-position: center left;
padding: 0 2px;
font-size: 0.73em;
margin: 3px 5px 3px 0;
display: inline;
background-image: linear-gradient(bottom, #aab 0, #ccc 100%);
background-image: -o-linear-gradient(bottom, #aab 0, #ccc 100%);
background-image: -moz-linear-gradient(bottom, #aab 0, #ccc 100%);
background-image: -webkit-linear-gradient(bottom, #aab 0, #ccc 100%);
background-image: -ms-linear-gradient(bottom, #aab 0, #ccc 100%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #aab), color-stop(1, #ccc));
border: 1px solid #ccd;
-moz-box-shadow: inset 0 1px 2px #ddd;
-webkit-box-shadow: inset 0 1px 2px #fff;
box-shadow: inset 0 1px 2px #eee;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-moz-background-clip: padding;
-webkit-background-clip: padding-box;
background-clip: padding-box;
}
.comment button:hover {
background-image: linear-gradient(bottom, #ccd 0, #fff 100%);
background-image: -o-linear-gradient(bottom, #ccd 0, #fff 100%);
background-image: -moz-linear-gradient(bottom, #ccd 0, #fff 100%);
background-image: -webkit-linear-gradient(bottom, #ccd 0, #fff 100%);
background-image: -ms-linear-gradient(bottom, #ccd 0, #fff 100%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccd), color-stop(1, #fff));
}
.comment button:active {
background-image: linear-gradient(bottom, #fff 0, #889 100%);
background-image: -o-linear-gradient(bottom, #fff 0, #889 100%);
background-image: -moz-linear-gradient(bottom, #fff 0, #889 100%);
background-image: -webkit-linear-gradient(bottom, #fff 0, #889 100%);
background-image: -ms-linear-gradient(bottom, #fff 0, #889 100%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(1, #889));
position:relative;
top:1px;
}
.comment input { padding: 2px; }
#replymessage { margin-top: 5px; }
.commentmeta {
color: #fff;
background-color: #8ea0b2;
margin-bottom: 3px;
padding: 0 0 0 3px;
}
.commentdate { color: #bfcede; }
img.vizhash {
width: 16px;
height: 16px;
position: relative;
top: 2px;
left: -3px;
}
#prettyprint {
color: #000000;
font-size: 1.2em;
}
#prettyprint.prettyprinted {
overflow: auto;
}
#cleartext {
padding: 10px;
}
#cleartext * {
margin-bottom: 10px;
}
#cleartext ol {
list-style: auto;
margin-left: 15px;
}
#cleartext ul {
list-style: disc;
margin-left: 15px;
}
#cleartext h1, #cleartext h2, #cleartext h3, #cleartext h4, #cleartext h5, #cleartext h6 {
font-weight: bold;
}
#cleartext h1 {
font-size: 2em;
}
#cleartext h2 {
font-size: 1.5em;
}
#cleartext h3 {
font-size: 1.2em;
}
/* right-to-left overrides */
html[dir="rtl"] #aboutbox, html[dir="rtl"] #deletelink, html[dir="rtl"] #newbutton {
float: left;
}
html[dir="rtl"] button, html[dir="rtl"] .button, html[dir="rtl"] button img {
margin-left: 5px;
margin-right: inherit;
}
html[dir="rtl"] button img {
margin-left: 8px;
}

View file

@ -22,6 +22,7 @@ for more information.
### Minimal Requirements ### Minimal Requirements
- PHP version 7.3 or above - PHP version 7.3 or above
- ctype extension
- GD extension (when using identicon or vizhash icons, jdenticon works without it) - GD extension (when using identicon or vizhash icons, jdenticon works without it)
- zlib extension - zlib extension
- some disk space or a database supported by [PDO](https://php.net/manual/book.pdo.php) - some disk space or a database supported by [PDO](https://php.net/manual/book.pdo.php)
@ -200,7 +201,7 @@ CREATE INDEX parent ON prefix_comment(pasteid);
CREATE TABLE prefix_config ( CREATE TABLE prefix_config (
id CHAR(16) NOT NULL, value TEXT, PRIMARY KEY (id) id CHAR(16) NOT NULL, value TEXT, PRIMARY KEY (id)
); );
INSERT INTO prefix_config VALUES('VERSION', '1.7.8'); INSERT INTO prefix_config VALUES('VERSION', '1.7.6');
``` ```
In **PostgreSQL**, the `data`, `attachment`, `nickname` and `vizhash` columns In **PostgreSQL**, the `data`, `attachment`, `nickname` and `vizhash` columns

View file

@ -18,7 +18,7 @@ The parameters in detail:
an accidentally destructive test case in it. an accidentally destructive test case in it.
- `--read-only` - This image supports running in read-only mode. Only /tmp - `--read-only` - This image supports running in read-only mode. Only /tmp
may be written into. may be written into.
- `--rm` - Remove the container after the run. This saves you doing a cleanup - `-rm` - Remove the container after the run. This saves you doing a cleanup
on your docker environment, if you run the image frequently. on your docker environment, if you run the image frequently.
You can also run just the php and javascript test suites instead of both: You can also run just the php and javascript test suites instead of both:

View file

@ -164,25 +164,17 @@
"EiB": "إكسابايت", "EiB": "إكسابايت",
"ZiB": "زيتابايت", "ZiB": "زيتابايت",
"YiB": "يوتابايت", "YiB": "يوتابايت",
"kB": "كيلوبايت",
"MB": "ميجابايت",
"GB": "جيجابايت",
"TB": "تيرابايت",
"PB": "بيتابايت",
"EB": "إكسابايت",
"ZB": "زيتابايت",
"YB": "يوتابايت",
"Format": "التنسيق", "Format": "التنسيق",
"Plain Text": "نص عادي", "Plain Text": "نص عادي",
"Source Code": "كود مصدر", "Source Code": "كود مصدر",
"Markdown": "ماركداون", "Markdown": "ماركداون",
"Download attachment": "نزّل المرفق", "Download attachment": "تنزيل المرفقات",
"Cloned: '%s'": "مستنسخ: '%s'", "Cloned: '%s'": "مستنسخ: '%s'",
"The cloned file '%s' was attached to this paste.": "تم إرفاق الملف المستنسخ '%s' بهذا اللصق.", "The cloned file '%s' was attached to this paste.": "تم إرفاق المِلَفّ المستنسخ '%s' بهذا اللصق.",
"Attach a file": "أرفق ملف", "Attach a file": "إرفاق مِلَفّ",
"alternatively drag & drop a file or paste an image from the clipboard": "بدلاً من ذلك، اسحب ملفًا وأسقطه أو الصق صورة من الحافظة", "alternatively drag & drop a file or paste an image from the clipboard": "بدلاً من ذلك، اسحب ملفًا وأسقطه أو الصق صورة من الحافظة",
"File too large, to display a preview. Please download the attachment.": "الملف كبير جدًا، بحيث لا يمكن عرض معاينة. الرجاء تنزيل المرفق.", "File too large, to display a preview. Please download the attachment.": "المِلَفّ كبير جدًا، بحيث لا يمكن عرض معاينة. الرجاء تنزيل المرفق.",
"Remove attachment": "أزِل المرفق", "Remove attachment": "إزالة المرفق",
"Your browser does not support uploading encrypted files. Please use a newer browser.": "متصفحك لا يدعم رفع الملفات المشفرة. الرجاء استخدام متصفح أحدث.", "Your browser does not support uploading encrypted files. Please use a newer browser.": "متصفحك لا يدعم رفع الملفات المشفرة. الرجاء استخدام متصفح أحدث.",
"Invalid attachment.": "مرفق غير صحيح.", "Invalid attachment.": "مرفق غير صحيح.",
"Options": "الخيارات", "Options": "الخيارات",
@ -223,17 +215,16 @@
"Trying to shorten a URL that isn't pointing at our instance.": "محاولة تقصير عنوان URL لا يشير إلى خادمنا.", "Trying to shorten a URL that isn't pointing at our instance.": "محاولة تقصير عنوان URL لا يشير إلى خادمنا.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "خطأ في الاتصال بـ YOURLS. ربما تكون هناك مشكلة في التضبيط، مثل \"apiurl\" أو \"التوقيع\" الخاطئ أو المفقود.", "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "خطأ في الاتصال بـ YOURLS. ربما تكون هناك مشكلة في التضبيط، مثل \"apiurl\" أو \"التوقيع\" الخاطئ أو المفقود.",
"Error parsing YOURLS response.": "خطأ في تحليل استجابة YOURLS.", "Error parsing YOURLS response.": "خطأ في تحليل استجابة YOURLS.",
"This secret message can only be displayed once. Would you like to see it now?": "يمكن عرض هذه الرسالة السرية مرة واحدة فقط. هل ترغب في رؤيتها الآن؟", "This secret message can only be displayed once. Would you like to see it now?": "لا يمكن عرض اللصق احرقه بعد قراءته إلا مرة واحدة عند تحميله. هل تريد فتحه الآن؟",
"Yes, see it": "نعم، دعني اراها", "Yes, see it": "نعم، حمله",
"Dark Mode": "الوضع الداكن", "Dark Mode": "الوضع الداكن",
"Error compressing paste, due to missing WebAssembly support.": "خطأ في ضغط اللصق، بسبب فقدان دعم WebAssembly.", "Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "خطأ في فك ضغط اللصق، متصفحك لا يدعم WebAssembly. الرجاء استخدام متصفح آخر لعرض هذه اللصقة.", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.",
"Start over": "ابدأ من جديد", "Start over": "Start over",
"Paste copied to clipboard": "نُسخ اللصق إلى الحافظة", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "لنسخ اللصق انقر على زر النسخ أو استخدم اختصار الحافظة <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "نسخ الرابط", "Copy link": "Copy link",
"Link copied to clipboard": "نُسخ الرابط إلى الحافظة", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "لصق النص", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "مفتاح التبويب يعمل كشخصية (انقر <kbd>Ctrl</kbd>+<kbd>m</kbd> أو <kbd>Esc</kbd> للتبديل)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "السمة"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "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": "Изходен код",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Text sense format", "Plain Text": "Text sense format",
"Source Code": "Codi font", "Source Code": "Codi font",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -156,22 +156,14 @@
"Could not create paste: %s": "Ùn si pò micca creà lappiccicu : %s", "Could not create paste: %s": "Ùn si pò micca creà lappiccicu : %s",
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Ùn si pò micca dicifrà lappiccicu : A chjave di dicifratura hè assente in lindirizzu. Averiate impiegatu un orientadore dindirizzu o un riduttore chì ammuzzeghja una parte di lindirizzu ?", "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Ùn si pò micca dicifrà lappiccicu : A chjave di dicifratura hè assente in lindirizzu. Averiate impiegatu un orientadore dindirizzu o un riduttore chì ammuzzeghja una parte di lindirizzu ?",
"B": "o", "B": "o",
"KiB": "Kio", "KiB": "Ko",
"MiB": "Mio", "MiB": "Mo",
"GiB": "Gio", "GiB": "Go",
"TiB": "Tio", "TiB": "To",
"PiB": "Pio", "PiB": "Po",
"EiB": "Eio", "EiB": "Eo",
"ZiB": "Zio", "ZiB": "Zo",
"YiB": "Yio", "YiB": "Yo",
"kB": "Ko",
"MB": "Mo",
"GB": "Go",
"TB": "To",
"PB": "Po",
"EB": "Eo",
"ZB": "Zo",
"YB": "Yo",
"Format": "Furmatu", "Format": "Furmatu",
"Plain Text": "Testu in chjaru", "Plain Text": "Testu in chjaru",
"Source Code": "Codice di fonte", "Source Code": "Codice di fonte",
@ -234,6 +226,5 @@
"Copy link": "Cupià u liame", "Copy link": "Cupià u liame",
"Link copied to clipboard": "U liame hè statu cupiatu in u premepapei", "Link copied to clipboard": "U liame hè statu cupiatu in u premepapei",
"Paste text": "Testu di lappiccicu", "Paste text": "Testu di lappiccicu",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "U tastu di tabulazione ghjova cumè un caratteru (Appughjate nantà <kbd>Ctrl</kbd>+<kbd>m</kbd> o <kbd>Scapp</kbd> per scambià)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "U tastu di tabulazione ghjova cumè un caratteru (Appughjate nantà <kbd>Ctrl</kbd>+<kbd>m</kbd> o <kbd>Scapp</kbd> per scambià)"
"Theme": "Tema"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formát", "Format": "Formát",
"Plain Text": "Prostý text", "Plain Text": "Prostý text",
"Source Code": "Zdrojový kód", "Source Code": "Zdrojový kód",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Nur Text", "Plain Text": "Nur Text",
"Source Code": "Quellcode", "Source Code": "Quellcode",
@ -234,6 +226,5 @@
"Copy link": "Verknüpfung kopieren", "Copy link": "Verknüpfung kopieren",
"Link copied to clipboard": "Verknüpfung wurde in die Zwischenablage kopiert.", "Link copied to clipboard": "Verknüpfung wurde in die Zwischenablage kopiert.",
"Paste text": "Text", "Paste text": "Text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortaste als Zeichen interpretieren (Umschalten durch <kbd>Strg</kbd>+<kbd>m</kbd> oder <kbd>Esc</kbd>)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortaste als Zeichen interpretieren (Umschalten durch <kbd>Strg</kbd>+<kbd>m</kbd> oder <kbd>Esc</kbd>)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "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": "Πηγαίος Κώδικας",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Plain Text", "Plain Text": "Plain Text",
"Source Code": "Source Code", "Source Code": "Source Code",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formato", "Format": "Formato",
"Plain Text": "Texto sin formato", "Plain Text": "Texto sin formato",
"Source Code": "Código fuente", "Source Code": "Código fuente",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formaat", "Format": "Formaat",
"Plain Text": "Lihttekst", "Plain Text": "Lihttekst",
"Source Code": "Lähtekood", "Source Code": "Lähtekood",
@ -223,17 +215,16 @@
"Trying to shorten a URL that isn't pointing at our instance.": "Püüame lühendada URL-i, mis ei viita meie instantsile.", "Trying to shorten a URL that isn't pointing at our instance.": "Püüame lühendada URL-i, mis ei viita meie instantsile.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Viga YOURLS-i kutsumisel. Tõenäoliselt konfiguratsiooniprobleem, näiteks vale või puuduv \"apiurl\" või \"signature\".", "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Viga YOURLS-i kutsumisel. Tõenäoliselt konfiguratsiooniprobleem, näiteks vale või puuduv \"apiurl\" või \"signature\".",
"Error parsing YOURLS response.": "Viga YOURLS vastuse parsimisel.", "Error parsing YOURLS response.": "Viga YOURLS vastuse parsimisel.",
"This secret message can only be displayed once. Would you like to see it now?": "Seda turvalist sõnumit saab kuvada vaid ühe korra. \nKas soovid seda näha nüüd?", "This secret message can only be displayed once. Would you like to see it now?": "This secret message can only be displayed once. Would you like to see it now?",
"Yes, see it": "Jah, vaata seda", "Yes, see it": "Yes, see it",
"Dark Mode": "Tume režiim", "Dark Mode": "Tume režiim",
"Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.", "Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.",
"Start over": "Alusta uuesti", "Start over": "Start over",
"Paste copied to clipboard": "Kleebe kopeeriti lõikelauale", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "Kopeeri link", "Copy link": "Copy link",
"Link copied to clipboard": "Link kopeeriti lõikelauale", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Kleebi tekst", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Teema"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formaatti", "Format": "Formaatti",
"Plain Text": "Perusteksti", "Plain Text": "Perusteksti",
"Source Code": "Lähdekoodi", "Source Code": "Lähdekoodi",
@ -226,14 +218,13 @@
"This secret message can only be displayed once. Would you like to see it now?": "Tämä salainen viesti voidaan näyttää vain kerran. Haluatko nähdä sen nyt?", "This secret message can only be displayed once. Would you like to see it now?": "Tämä salainen viesti voidaan näyttää vain kerran. Haluatko nähdä sen nyt?",
"Yes, see it": "Kyllä, näet sen", "Yes, see it": "Kyllä, näet sen",
"Dark Mode": "Tumma tila", "Dark Mode": "Tumma tila",
"Error compressing paste, due to missing WebAssembly support.": "Virhe pakattaessa pastea, koska WebAssembly-tuki puuttuu.", "Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Virhe pasten purkamisessa, selaimesi ei tue WebAssemblyä. Ole hyvä ja käytä toista selainta nähdäksesi tämä paste.", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.",
"Start over": "Aloita alusta", "Start over": "Start over",
"Paste copied to clipboard": "Paste kopioitu leikepöydälle", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "Voit kopioida pasten painamalla kopioi-painiketta tai käyttämällä leikepöydän oikotietä <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "Kopioi linkki", "Copy link": "Copy link",
"Link copied to clipboard": "Linkki kopioitu leikepöydälle", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Liitä teksti", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulaattori toimii merkkinä (Paina <kbd>Ctrl</kbd>+<kbd>m</kbd> tai <kbd>Esc</kbd> vaihtaaksesi)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Teema"
} }

View file

@ -151,7 +151,7 @@
"server error or not responding": "Le serveur ne répond pas ou a rencontré une erreur", "server error or not responding": "Le serveur ne répond pas ou a rencontré une erreur",
"Could not post comment: %s": "Impossible de poster le commentaire : %s", "Could not post comment: %s": "Impossible de poster le commentaire : %s",
"Sending paste…": "Envoi du paste…", "Sending paste…": "Envoi du paste…",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "Votre paste est disponible à l'adresse <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appuyez sur <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd> pour copier)</span>", "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "Votre paste est disponible à l'adresse <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appuyez sur <kbd>Ctrl</kbd>+<kbd>c</kbd> pour copier)</span>",
"Delete data": "Supprimer les données du paste", "Delete data": "Supprimer les données du paste",
"Could not create paste: %s": "Impossible de créer le paste : %s", "Could not create paste: %s": "Impossible de créer le paste : %s",
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Impossible de déchiffrer le paste : Clé de déchiffrement manquante dans l'URL (Avez-vous utilisé un redirecteur ou un site de réduction d'URL qui supprime une partie de l'URL ?)", "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Impossible de déchiffrer le paste : Clé de déchiffrement manquante dans l'URL (Avez-vous utilisé un redirecteur ou un site de réduction d'URL qui supprime une partie de l'URL ?)",
@ -164,14 +164,6 @@
"EiB": "Eio", "EiB": "Eio",
"ZiB": "Zio", "ZiB": "Zio",
"YiB": "Yio", "YiB": "Yio",
"kB": "ko",
"MB": "Mo",
"GB": "Go",
"TB": "To",
"PB": "Po",
"EB": "Eo",
"ZB": "Zo",
"YB": "Yo",
"Format": "Format", "Format": "Format",
"Plain Text": "Texte brut", "Plain Text": "Texte brut",
"Source Code": "Code source", "Source Code": "Code source",
@ -229,11 +221,10 @@
"Error compressing paste, due to missing WebAssembly support.": "Erreur lors de la compression du paste, en raison du support de WebAssembly manquant.", "Error compressing paste, due to missing WebAssembly support.": "Erreur lors de la compression du paste, en raison du support de WebAssembly manquant.",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Erreur lors de la décompression du paste, votre navigateur ne supporte pas WebAssembly. Veuillez utiliser un autre navigateur pour voir ce paste.", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Erreur lors de la décompression du paste, votre navigateur ne supporte pas WebAssembly. Veuillez utiliser un autre navigateur pour voir ce paste.",
"Start over": "Recommencer", "Start over": "Recommencer",
"Paste copied to clipboard": "Paste copié dans le presse-papier", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "Pour copier-coller appuyer sur le bouton To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "Copier le lien", "Copy link": "Copier le lien",
"Link copied to clipboard": "Lien copié dans le presse-papier", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Texte du paste", "Paste text": "Texte du paste",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "La touche de tabulation sert de caractère (Presser <kbd>Ctrl</kbd>+<kbd>m</kbd> ou <kbd>Esc</kbd> pour basculer)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Thème"
} }

View file

@ -1,7 +1,7 @@
{ {
"PrivateBin": "PrivateBin", "PrivateBin": "PrivateBin",
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s הוא שירות pastebin מקוון, מינימליסטי וקוד פתוח, שבו לשרת אין כל ידע על המידע שהודבק. הנתונים מוצפנים ומפוענחים %sבדפדפן%s באמצעות הצפנת AES ב-256 סיביות.", "%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.",
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "מידע נוסף בדף <a href=\"https://privatebin.info/\">הפרויקט</a>.", "More information on the <a href=\"https://privatebin.info/\">project page</a>.": "More information on the <a href=\"https://privatebin.info/\">project page</a>.",
"Because ignorance is bliss": "כיוון שבורות היא ברכה", "Because ignorance is bliss": "כיוון שבורות היא ברכה",
"Paste does not exist, has expired or has been deleted.": "ההדבקה לא קיימת, פגה או נמחקה.", "Paste does not exist, has expired or has been deleted.": "ההדבקה לא קיימת, פגה או נמחקה.",
"%s requires php %s or above to work. Sorry.": "%s דורש PHP %s כדי לפעול.", "%s requires php %s or above to work. Sorry.": "%s דורש PHP %s כדי לפעול.",
@ -29,7 +29,7 @@
"Create": "צור", "Create": "צור",
"Clone": "שכפול", "Clone": "שכפול",
"Raw text": "טקסט גולמי", "Raw text": "טקסט גולמי",
"Expires": "יפוג ב", "Expires": "Expires",
"Burn after reading": "קוראים-שורפים", "Burn after reading": "קוראים-שורפים",
"Open discussion": "פתיחת דיון", "Open discussion": "פתיחת דיון",
"Password (recommended)": "ססמה (מומלץ)", "Password (recommended)": "ססמה (מומלץ)",
@ -53,11 +53,11 @@
], ],
"%d hours": [ "%d hours": [
"שעה אחת", "שעה אחת",
"%d שעות", "%d hours (1st plural)",
"%d שעות", "%d hours (2nd plural)",
"%d שעות", "%d hours (3rd plural)",
"%d שעות", "%d hours (4th plural)",
"%d שעות" "%d hours (5th plural)"
], ],
"%d days": [ "%d days": [
"יום אחד", "יום אחד",
@ -94,44 +94,44 @@
"Never": "לעולם לא", "Never": "לעולם לא",
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "הערה: זהו שירות בדקה: המידע לא ישמר.", "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "הערה: זהו שירות בדקה: המידע לא ישמר.",
"This document will expire in %d seconds.": [ "This document will expire in %d seconds.": [
"מסמך זה יפוג בעוד שנייה אחת.", "This document will expire in %d second. (singular)",
"מסמך זה יפוג בעוד %d שניות.", "This document will expire in %d seconds. (1st plural)",
"מסמך זה יפוג בעוד %d שניות.", "This document will expire in %d seconds. (2nd plural)",
"מסמך זה יפוג בעוד %d שניות.", "This document will expire in %d seconds. (3rd plural)",
"מסמך זה יפוג בעוד %d שניות.", "This document will expire in %d seconds. (4th plural)",
"מסמך זה יפוג בעוד %d שניות." "This document will expire in %d seconds. (5th plural)"
], ],
"This document will expire in %d minutes.": [ "This document will expire in %d minutes.": [
"מסמך זה יפוג בעוד דקה אחת.", "This document will expire in %d minute. (singular)",
"מסמך זה יפוג בעוד %d דקות.", "This document will expire in %d minutes. (1st plural)",
"מסמך זה יפוג בעוד %d דקות.", "This document will expire in %d minutes. (2nd plural)",
"מסמך זה יפוג בעוד %d דקות.", "This document will expire in %d minutes. (3rd plural)",
"מסמך זה יפוג בעוד %d דקות.", "This document will expire in %d minutes. (4th plural)",
"מסמך זה יפוג בעוד %d דקות." "This document will expire in %d minutes. (5th plural)"
], ],
"This document will expire in %d hours.": [ "This document will expire in %d hours.": [
"מסמך זה יפוג בעוד שעה אחת.", "This document will expire in %d hour. (singular)",
"מסמך זה יפוג בעוד %d שעות.", "This document will expire in %d hours. (1st plural)",
"מסמך זה יפוג בעוד %d שעות.", "This document will expire in %d hours. (2nd plural)",
"מסמך זה יפוג בעוד %d שעות.", "This document will expire in %d hours. (3rd plural)",
"מסמך זה יפוג בעוד %d שעות.", "This document will expire in %d hours. (4th plural)",
"מסמך זה יפוג בעוד %d שעות." "This document will expire in %d hours. (5th plural)"
], ],
"This document will expire in %d days.": [ "This document will expire in %d days.": [
"מסמך זה יפוג בעוד יום אחד.", "This document will expire in %d day. (singular)",
"מסמך זה יפוג בעוד %d ימים.", "This document will expire in %d days. (1st plural)",
"מסמך זה יפוג בעוד %d ימים.", "This document will expire in %d days. (2nd plural)",
"מסמך זה יפוג בעוד %d ימים.", "This document will expire in %d days. (3rd plural)",
"מסמך זה יפוג בעוד %d ימים.", "This document will expire in %d days. (4th plural)",
"מסמך זה יפוג בעוד %d ימים." "This document will expire in %d days. (5th plural)"
], ],
"This document will expire in %d months.": [ "This document will expire in %d months.": [
"מסמך זה יפוג בעוד חודש אחד.", "This document will expire in %d month. (singular)",
"מסמך זה יפוג בעוד %d חודשים.", "This document will expire in %d months. (1st plural)",
"מסמך זה יפוג בעוד %d חודשים.", "This document will expire in %d months. (2nd plural)",
"מסמך זה יפוג בעוד %d חודשים.", "This document will expire in %d months. (3rd plural)",
"מסמך זה יפוג בעוד %d חודשים.", "This document will expire in %d months. (4th plural)",
"מסמך זה יפוג בעוד %d חודשים." "This document will expire in %d months. (5th plural)"
], ],
"Please enter the password for this paste:": "נא למלא את הססמה להדבקה הזו:", "Please enter the password for this paste:": "נא למלא את הססמה להדבקה הזו:",
"Could not decrypt data (Wrong key?)": "לא ניתן לפענח את הנתונים (מפתח שגוי?)", "Could not decrypt data (Wrong key?)": "לא ניתן לפענח את הנתונים (מפתח שגוי?)",
@ -164,43 +164,35 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "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": "קוד מקור",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "הורדת קובץ מצורף", "Download attachment": "הורדת קובץ מצורף",
"Cloned: '%s'": "שוכפל: '%s'", "Cloned: '%s'": "שוכפל: '%s'",
"The cloned file '%s' was attached to this paste.": "הקובץ '%s' שהועתק צורף להדבקה זו.", "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
"Attach a file": "צירוף קובץ", "Attach a file": "צירוף קובץ",
"alternatively drag & drop a file or paste an image from the clipboard": "לחלופין, ניתן לגרור ולשחרר קובץ או להדביק תמונה מהלוח.", "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
"File too large, to display a preview. Please download the attachment.": "הקובץ גדול מדי כדי להציג תצוגה מקדימה. אנא הורד את הקובץ המצורף.", "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
"Remove attachment": "הסר קובץ מצורף", "Remove attachment": "Remove attachment",
"Your browser does not support uploading encrypted files. Please use a newer browser.": "הדפדפן שלך אינו תומך בהעלאת קבצים מוצפנים. אנא השתמש בדפדפן עדכני יותר.", "Your browser does not support uploading encrypted files. Please use a newer browser.": "Your browser does not support uploading encrypted files. Please use a newer browser.",
"Invalid attachment.": "קובץ מצורף שגוי.", "Invalid attachment.": "קובץ מצורף שגוי.",
"Options": "אפשרויות", "Options": "אפשרויות",
"Shorten URL": "קיצור כתובת", "Shorten URL": "קיצור כתובת",
"Editor": "עורך", "Editor": "עורך",
"Preview": "תצוגה מקדימה", "Preview": "תצוגה מקדימה",
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s דורש שה-PATH יסתיים ב-\"%s\". אנא עדכן את ה-PATH בקובץ index.php שלך.", "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.",
"Decrypt": "פענוח", "Decrypt": "פענוח",
"Enter password": "נא למלא ססמה", "Enter password": "נא למלא ססמה",
"Loading…": "בטעינה…", "Loading…": "בטעינה…",
"Decrypting paste…": "ההדבקה מפוענחת…", "Decrypting paste…": "ההדבקה מפוענחת…",
"Preparing new paste…": "ההדבקה החדשה בהכנות…", "Preparing new paste…": "ההדבקה החדשה בהכנות…",
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "אם הודעה זו לא נעלמת, אנא עיין ב- <a href=\"%s\">שאלות נפוצות אלה למידע לפתרון בעיות</a>.", "In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.",
"+++ no paste text +++": "+++ אין טקסט להדבקה +++", "+++ no paste text +++": "+++ אין טקסט להדבקה +++",
"Could not get paste data: %s": "לא ניתן לקבל את נתוני ההדבקה: %s", "Could not get paste data: %s": "לא ניתן לקבל את נתוני ההדבקה: %s",
"QR code": "קוד QR", "QR code": "קוד QR",
"This website is using an insecure HTTP connection! Please use it only for testing.": "האתר הזה משתמש בחיבור HTTP בלתי מאובטח! נא להשתמש בזה לבדיקות בלבד.", "This website is using an insecure HTTP connection! Please use it only for testing.": "האתר הזה משתמש בחיבור HTTP בלתי מאובטח! נא להשתמש בזה לבדיקות בלבד.",
"For more information <a href=\"%s\">see this FAQ entry</a>.": "לפרטים נוספים <a href=\"%s\">עיין ברשומת שאלות נפוצות זו</a>.", "For more information <a href=\"%s\">see this FAQ entry</a>.": "יש מידע נוסף <a href=\"%s\">ברשומה הזאת בשו״ת</a>.",
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "יכול להיות שהדפדפן שלך ידרוש חיבור HTTPS כדי לתמוך ב־API של WebCrypto. כדי לנסות <a href=\"%s\">לעבור ל־HTTPS</a>.", "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "יכול להיות שהדפדפן שלך ידרוש חיבור HTTPS כדי לתמוך ב־API של WebCrypto. כדי לנסות <a href=\"%s\">לעבור ל־HTTPS</a>.",
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "הדפדפן שלך לא תומך ב־WebAssembly שמשמש לדחיסת zlib. אפשר ליצור מסמכים בלתי מוצפנים אך אין אפשרות לקרוא מסמכים מוצפנים.", "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "הדפדפן שלך לא תומך ב־WebAssembly שמשמש לדחיסת zlib. אפשר ליצור מסמכים בלתי מוצפנים אך אין אפשרות לקרוא מסמכים מוצפנים.",
"waiting on user to provide a password": "בהמתנה למילוי הססמה מצד המשתמש", "waiting on user to provide a password": "בהמתנה למילוי הססמה מצד המשתמש",
@ -217,23 +209,22 @@
"Close": "סגירה", "Close": "סגירה",
"Encrypted note on %s": "%sהערה מוצפנת ב־", "Encrypted note on %s": "%sהערה מוצפנת ב־",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "נא לבקר בקישור כדי לצפות בהערה. מסירת הקישור לאנשים כלשהם תאפשר גם להם לגשת להערה.", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "נא לבקר בקישור כדי לצפות בהערה. מסירת הקישור לאנשים כלשהם תאפשר גם להם לגשת להערה.",
"URL shortener may expose your decrypt key in URL.": "שירות קיצור כתובת URL עשוי לחשוף את מפתח הפענוח שלך בכתובת ה-URL.", "URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "שמור הדבקה", "Save paste": "Save paste",
"Your IP is not authorized to create pastes.": "ה-IP שלך אינו מורשה ליצור הדבקות.", "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
"Trying to shorten a URL that isn't pointing at our instance.": "מנסים לקצר כתובת URL שאינה מצביעה על המערכת שלנו.", "Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "שגיאה בעת קריאה ל-YOURLS. כנראה מדובר בבעיה בהגדרות, כמו \"apiurl\" או \"signature\" שגויים או חסרים.", "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
"Error parsing YOURLS response.": "שגיאה בניתוח התגובה מ-YOURLS.", "Error parsing YOURLS response.": "Error parsing YOURLS response.",
"This secret message can only be displayed once. Would you like to see it now?": "ההודעה הסודית הזו יכולה להוצג רק פעם אחת. האם תרצה לראות אותה עכשיו?", "This secret message can only be displayed once. Would you like to see it now?": "This secret message can only be displayed once. Would you like to see it now?",
"Yes, see it": "כן, ראה אותה", "Yes, see it": "Yes, see it",
"Dark Mode": "מצב כהה", "Dark Mode": "Dark Mode",
"Error compressing paste, due to missing WebAssembly support.": "שגיאה בדחיסת ההדבקה, עקב חוסר תמיכה ב-WebAssembly.", "Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "שגיאה בהפקת ההדבקה, הדפדפן שלך לא תומך ב-WebAssembly. אנא השתמש בדפדפן אחר כדי לצפות בהדבקה זו.", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.",
"Start over": "להתחיל מחדש", "Start over": "Start over",
"Paste copied to clipboard": "ההדבקה הועתקה ללוח", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "על מנת להעתיק את ההדבקה, לחץ על כפתור ההעתקה או השתמש בקיצור הדרך ללוח <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "העתק קישור", "Copy link": "Copy link",
"Link copied to clipboard": "הקישור הועתק ללוח", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "הדבק טקסט", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "משטח ה-tab פועל כמקש תו (לחץ על <kbd>Ctrl</kbd>+<kbd>m</kbd> או <kbd>Esc</kbd> להחלפה)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "נושא"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Plain Text", "Plain Text": "Plain Text",
"Source Code": "Source Code", "Source Code": "Source Code",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formátum", "Format": "Formátum",
"Plain Text": "Egyszerű szöveg", "Plain Text": "Egyszerű szöveg",
"Source Code": "Forráskód", "Source Code": "Forráskód",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Teks Biasa", "Plain Text": "Teks Biasa",
"Source Code": "Kode Sumber", "Source Code": "Kode Sumber",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formato", "Format": "Formato",
"Plain Text": "Solo Testo", "Plain Text": "Solo Testo",
"Source Code": "Codice Sorgente", "Source Code": "Codice Sorgente",
@ -234,6 +226,5 @@
"Copy link": "Copia il link", "Copy link": "Copia il link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Testo del messaggio", "Paste text": "Testo del messaggio",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "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": "ソースコード",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Plain Text", "Plain Text": "Plain Text",
"Source Code": "Source Code", "Source Code": "Source Code",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Plain Text", "Plain Text": "Plain Text",
"Source Code": "Source Code", "Source Code": "Source Code",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Plain Text", "Plain Text": "Plain Text",
"Source Code": "Source Code", "Source Code": "Source Code",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Plain Text", "Plain Text": "Plain Text",
"Source Code": "Source Code", "Source Code": "Source Code",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formatas", "Format": "Formatas",
"Plain Text": "Grynasis tekstas", "Plain Text": "Grynasis tekstas",
"Source Code": "Pirminis kodas", "Source Code": "Pirminis kodas",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -143,7 +143,7 @@
"Avatar generated from IP address": "Anonieme avatar (van het IP-adres)", "Avatar generated from IP address": "Anonieme avatar (van het IP-adres)",
"Add comment": "Commentaar toevoegen", "Add comment": "Commentaar toevoegen",
"Optional nickname…": "Optionele bijnaam…", "Optional nickname…": "Optionele bijnaam…",
"Post comment": "Plaats commentaar", "Post comment": "Plaats een commentaar",
"Sending comment…": "Commentaar verzenden…", "Sending comment…": "Commentaar verzenden…",
"Comment posted.": "Commentaar geplaatst.", "Comment posted.": "Commentaar geplaatst.",
"Could not refresh display: %s": "Kon de weergave niet vernieuwen: %s", "Could not refresh display: %s": "Kon de weergave niet vernieuwen: %s",
@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formaat", "Format": "Formaat",
"Plain Text": "Platte tekst", "Plain Text": "Platte tekst",
"Source Code": "Broncode", "Source Code": "Broncode",
@ -228,12 +220,11 @@
"Dark Mode": "Donkere modus", "Dark Mode": "Donkere modus",
"Error compressing paste, due to missing WebAssembly support.": "Fout bij het comprimeren van notitie door ontbrekende ondersteuning voor WebAssembly.", "Error compressing paste, due to missing WebAssembly support.": "Fout bij het comprimeren van notitie door ontbrekende ondersteuning voor WebAssembly.",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Fout bij het decomprimeren van de notitie, uw browser ondersteunt WebAssembly niet. Gebruik een andere browser om deze notitie te bekijken.", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Fout bij het decomprimeren van de notitie, uw browser ondersteunt WebAssembly niet. Gebruik een andere browser om deze notitie te bekijken.",
"Start over": "Opnieuw beginnen", "Start over": "Start over",
"Paste copied to clipboard": "Notitie gekopieerd naar klembord", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "Om te kopiëren en plakken druk je op de knop Kopiëren of gebruik je de sneltoets op het klembord <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "Kopieer link", "Copy link": "Copy link",
"Link copied to clipboard": "Link gekopieerd naar klembord", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Tekst plakken", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortoets dient als teken (gebruik <kbd>Ctrl</kbd>+<kbd>m</kbd> of <kbd>Esc</kbd> om te schakelen)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Ren Tekst", "Plain Text": "Ren Tekst",
"Source Code": "Kildekode", "Source Code": "Kildekode",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "Eio", "EiB": "Eio",
"ZiB": "Zio", "ZiB": "Zio",
"YiB": "Yio", "YiB": "Yio",
"kB": "ko",
"MB": "Mo",
"GB": "Go",
"TB": "To",
"PB": "Po",
"EB": "Eo",
"ZB": "Zo",
"YB": "Yo",
"Format": "Format", "Format": "Format",
"Plain Text": "Tèxte brut", "Plain Text": "Tèxte brut",
"Source Code": "Còdi font", "Source Code": "Còdi font",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Czysty tekst", "Plain Text": "Czysty tekst",
"Source Code": "Kod źródłowy", "Source Code": "Kod źródłowy",
@ -228,12 +220,11 @@
"Dark Mode": "Ciemny motyw", "Dark Mode": "Ciemny motyw",
"Error compressing paste, due to missing WebAssembly support.": "Błąd kompresowania wklejenia przez brak obsługi WebAssembly.", "Error compressing paste, due to missing WebAssembly support.": "Błąd kompresowania wklejenia przez brak obsługi WebAssembly.",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Błąd dekompresowania wklejenia przez brak obsługi WebAssembly przez przeglądarkę. Użyj innej przeglądarki, aby zobaczyć to wklejenie.", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Błąd dekompresowania wklejenia przez brak obsługi WebAssembly przez przeglądarkę. Użyj innej przeglądarki, aby zobaczyć to wklejenie.",
"Start over": "Zacznij od nowa", "Start over": "Start over",
"Paste copied to clipboard": "Wklejka skopiowana do schowka", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "Aby skopiować wklejkę, naciśnij przycisk kopiowania lub użyj skrótu schowka <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "Kopiuj link", "Copy link": "Copy link",
"Link copied to clipboard": "Link został skopiowany do schowka", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Wklej tekst", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Klawisz Tabulatora służy jako znak (przytrzymaj <kbd>Ctrl</kbd>+<kbd>m</kbd> lub <kbd>Esc</kbd> aby przełączać)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Motyw"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formato", "Format": "Formato",
"Plain Text": "Texto sem formato", "Plain Text": "Texto sem formato",
"Source Code": "Código fonte", "Source Code": "Código fonte",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formatare", "Format": "Formatare",
"Plain Text": "Text neformatat", "Plain Text": "Text neformatat",
"Source Code": "Cod sursă", "Source Code": "Cod sursă",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "Эбайт", "EiB": "Эбайт",
"ZiB": "Збайт", "ZiB": "Збайт",
"YiB": "Йбайт", "YiB": "Йбайт",
"kB": "кбайт",
"MB": "Мбайт",
"GB": "Гбайт",
"TB": "Тбайт",
"PB": "Пбайт",
"EB": "Эбайт",
"ZB": "Збайт",
"YB": "Йбайт",
"Format": "Формат", "Format": "Формат",
"Plain Text": "Обычный текст", "Plain Text": "Обычный текст",
"Source Code": "Исходный код", "Source Code": "Исходный код",
@ -234,6 +226,5 @@
"Copy link": "Скопировать ссылку", "Copy link": "Скопировать ссылку",
"Link copied to clipboard": "Ссылка скопирована в буфер обмена", "Link copied to clipboard": "Ссылка скопирована в буфер обмена",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Тема"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Formát", "Format": "Formát",
"Plain Text": "Čistý text", "Plain Text": "Čistý text",
"Source Code": "Zdrojový kód", "Source Code": "Zdrojový kód",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EB", "EiB": "EB",
"ZiB": "ZB", "ZiB": "ZB",
"YiB": "YB", "YiB": "YB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Surov tekst", "Plain Text": "Surov tekst",
"Source Code": "Odprta koda", "Source Code": "Odprta koda",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Plain Text", "Plain Text": "Plain Text",
"Source Code": "Source Code", "Source Code": "Source Code",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "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": "ซอร์สโค้ด",
@ -229,11 +221,10 @@
"Error compressing paste, due to missing WebAssembly support.": "ไม่สามารถบีบอัดข้อมูลที่คุณต้องการฝากโค้ดได้ เนื่องจากอุปกรณ์ของคุณขาดการรองรับ WebAssembly", "Error compressing paste, due to missing WebAssembly support.": "ไม่สามารถบีบอัดข้อมูลที่คุณต้องการฝากโค้ดได้ เนื่องจากอุปกรณ์ของคุณขาดการรองรับ WebAssembly",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "ไม่สามารถอ่านข้อมูลที่คุณได้ฝากโค้ดไว้ เบราว์เซอร์ของคุณไม่รองรับ WebAssembly กรุณาลองเปลี่ยนใช้เบราว์เซอร์ตัวอื่นเพื่อดูการฝากโค้ดนี้อีกครั้ง", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "ไม่สามารถอ่านข้อมูลที่คุณได้ฝากโค้ดไว้ เบราว์เซอร์ของคุณไม่รองรับ WebAssembly กรุณาลองเปลี่ยนใช้เบราว์เซอร์ตัวอื่นเพื่อดูการฝากโค้ดนี้อีกครั้ง",
"Start over": "เริ่มใหม่", "Start over": "เริ่มใหม่",
"Paste copied to clipboard": "คัดลอกการฝากโค้ดไปที่คลิปบอร์ดแล้ว", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "หากต้องการคัดลอกและวาง กรุณากดปุ่มคัดลอก หรือใช้ปุ่มลัด <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd> สำหรับคลิปบอร์ด", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "คัดลอกลิงก์", "Copy link": "Copy link",
"Link copied to clipboard": "คัดลอกลิงก์ไปที่คลิปบอร์ดแล้ว", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "ฝากข้อความ", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "ปุ่ม Tabulator ใช้เป็นอักขระ (กด <kbd>Ctrl</kbd>+<kbd>m</kbd> หรือ <kbd>Esc</kbd> เพื่อสลับ)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "ธีม"
} }

View file

@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "YiB": "YiB",
"kB": "kB",
"MB": "MB",
"GB": "GB",
"TB": "TB",
"PB": "PB",
"EB": "EB",
"ZB": "ZB",
"YB": "YB",
"Format": "Format", "Format": "Format",
"Plain Text": "Düz Yazı", "Plain Text": "Düz Yazı",
"Source Code": "Kaynak Kodu", "Source Code": "Kaynak Kodu",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -164,14 +164,6 @@
"EiB": "Ебайт", "EiB": "Ебайт",
"ZiB": "Збайт", "ZiB": "Збайт",
"YiB": "Йбайт", "YiB": "Йбайт",
"kB": "кбайт",
"MB": "Мбайт",
"GB": "Гбайт",
"TB": "Тбайт",
"PB": "Пбайт",
"EB": "Ебайт",
"ZB": "Збайт",
"YB": "Йбайт",
"Format": "Формат", "Format": "Формат",
"Plain Text": "Звичайний текст", "Plain Text": "Звичайний текст",
"Source Code": "Вихідний код", "Source Code": "Вихідний код",
@ -234,6 +226,5 @@
"Copy link": "Copy link", "Copy link": "Copy link",
"Link copied to clipboard": "Link copied to clipboard", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "Paste text", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "Theme"
} }

View file

@ -7,12 +7,12 @@
"%s requires php %s or above to work. Sorry.": "抱歉,%s 需要 PHP %s 及以上版本才能运行。", "%s requires php %s or above to work. Sorry.": "抱歉,%s 需要 PHP %s 及以上版本才能运行。",
"%s requires configuration section [%s] to be present in configuration file.": "%s 需要设置配置文件中的 [%s] 部分。", "%s requires configuration section [%s] to be present in configuration file.": "%s 需要设置配置文件中的 [%s] 部分。",
"Please wait %d seconds between each post.": [ "Please wait %d seconds between each post.": [
"每 %d 秒只能创建一次粘贴。", "每 %d 秒只能粘贴一次。",
"每 %d 秒只能创建一次粘贴。", "每 %d 秒只能粘贴一次。",
"每 %d 秒只能创建一次粘贴。", "每 %d 秒只能粘贴一次。",
"每 %d 秒只能创建一次粘贴。", "每 %d 秒只能粘贴一次。",
"每 %d 秒只能创建一次粘贴。", "每 %d 秒只能粘贴一次。",
"每 %d 秒只能创建一次粘贴。" "每 %d 秒只能粘贴一次。"
], ],
"Paste is limited to %s of encrypted data.": "对于加密数据,上限为 %s。", "Paste is limited to %s of encrypted data.": "对于加密数据,上限为 %s。",
"Invalid data.": "无效的数据。", "Invalid data.": "无效的数据。",
@ -46,10 +46,10 @@
"%d minutes": [ "%d minutes": [
"%d 分钟", "%d 分钟",
"%d 分钟", "%d 分钟",
"%d 分钟", "%d ",
"%d 分钟", "%d ",
"%d 分钟", "%d ",
"%d 分钟" "%d "
], ],
"%d hours": [ "%d hours": [
"%d 小时", "%d 小时",
@ -94,7 +94,7 @@
"Never": "永不过期", "Never": "永不过期",
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "注意:这是一个测试服务,数据随时可能被删除。如果你滥用这个服务的话,小猫咪会死的。", "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "注意:这是一个测试服务,数据随时可能被删除。如果你滥用这个服务的话,小猫咪会死的。",
"This document will expire in %d seconds.": [ "This document will expire in %d seconds.": [
"这份文档将在 %d 秒后过期。", "这份文档将在秒后过期。",
"这份文档将在 %d 秒后过期。", "这份文档将在 %d 秒后过期。",
"这份文档将在 %d 秒后过期。", "这份文档将在 %d 秒后过期。",
"这份文档将在 %d 秒后过期。", "这份文档将在 %d 秒后过期。",
@ -102,7 +102,7 @@
"这份文档将在 %d 秒后过期。" "这份文档将在 %d 秒后过期。"
], ],
"This document will expire in %d minutes.": [ "This document will expire in %d minutes.": [
"这份文档将在 %d 分钟后过期。", "这份文档将在分钟后过期。",
"这份文档将在 %d 分钟后过期。", "这份文档将在 %d 分钟后过期。",
"这份文档将在 %d 分钟后过期。", "这份文档将在 %d 分钟后过期。",
"这份文档将在 %d 分钟后过期。", "这份文档将在 %d 分钟后过期。",
@ -110,7 +110,7 @@
"这份文档将在 %d 分钟后过期。" "这份文档将在 %d 分钟后过期。"
], ],
"This document will expire in %d hours.": [ "This document will expire in %d hours.": [
"这份文档将在 %d 小时后过期。", "这份文档将在小时后过期。",
"这份文档将在 %d 小时后过期。", "这份文档将在 %d 小时后过期。",
"这份文档将在 %d 小时后过期。", "这份文档将在 %d 小时后过期。",
"这份文档将在 %d 小时后过期。", "这份文档将在 %d 小时后过期。",
@ -118,7 +118,7 @@
"这份文档将在 %d 小时后过期。" "这份文档将在 %d 小时后过期。"
], ],
"This document will expire in %d days.": [ "This document will expire in %d days.": [
"这份文档将在 %d 天后过期。", "这份文档将在天后过期。",
"这份文档将在 %d 天后过期。", "这份文档将在 %d 天后过期。",
"这份文档将在 %d 天后过期。", "这份文档将在 %d 天后过期。",
"这份文档将在 %d 天后过期。", "这份文档将在 %d 天后过期。",
@ -126,7 +126,7 @@
"这份文档将在 %d 天后过期。" "这份文档将在 %d 天后过期。"
], ],
"This document will expire in %d months.": [ "This document will expire in %d months.": [
"这份文档将在 %d 个月后过期。", "这份文档将在个月后过期。",
"这份文档将在 %d 个月后过期。", "这份文档将在 %d 个月后过期。",
"这份文档将在 %d 个月后过期。", "这份文档将在 %d 个月后过期。",
"这份文档将在 %d 个月后过期。", "这份文档将在 %d 个月后过期。",
@ -136,7 +136,7 @@
"Please enter the password for this paste:": "请输入这份粘贴内容的密码:", "Please enter the password for this paste:": "请输入这份粘贴内容的密码:",
"Could not decrypt data (Wrong key?)": "无法解密数据(密钥错误?)", "Could not decrypt data (Wrong key?)": "无法解密数据(密钥错误?)",
"Could not delete the paste, it was not stored in burn after reading mode.": "无法删除此粘贴内容,它没有以阅后即焚模式保存。", "Could not delete the paste, it was not stored in burn after reading mode.": "无法删除此粘贴内容,它没有以阅后即焚模式保存。",
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "睁大眼睛看清楚!不要关闭窗口,否则你再也见不到这条消息了。", "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "看!仔!细!了!不要关闭窗口,否则你再也见不到这条消息了。",
"Could not decrypt comment; Wrong key?": "无法解密评论;密钥错误?", "Could not decrypt comment; Wrong key?": "无法解密评论;密钥错误?",
"Reply": "回复", "Reply": "回复",
"Anonymous": "匿名", "Anonymous": "匿名",
@ -150,7 +150,7 @@
"unknown status": "未知状态", "unknown status": "未知状态",
"server error or not responding": "服务器错误或无回应", "server error or not responding": "服务器错误或无回应",
"Could not post comment: %s": "无法发送评论: %s", "Could not post comment: %s": "无法发送评论: %s",
"Sending paste…": "正在发送粘贴内容…", "Sending paste…": "粘贴内容提交中…",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "您粘贴内容的链接是 <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(按下 <kbd>Ctrl</kbd>+<kbd>c</kbd> 以复制)</span>", "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "您粘贴内容的链接是 <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(按下 <kbd>Ctrl</kbd>+<kbd>c</kbd> 以复制)</span>",
"Delete data": "删除数据", "Delete data": "删除数据",
"Could not create paste: %s": "无法创建粘贴:%s", "Could not create paste: %s": "无法创建粘贴:%s",
@ -164,14 +164,6 @@
"EiB": "EiB", "EiB": "EiB",
"ZiB": "ZiB", "ZiB": "ZiB",
"YiB": "YiB", "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": "源代码",
@ -193,8 +185,8 @@
"Decrypt": "解密", "Decrypt": "解密",
"Enter password": "输入密码", "Enter password": "输入密码",
"Loading…": "载入中…", "Loading…": "载入中…",
"Decrypting paste…": "正在解密", "Decrypting paste…": "正在解密",
"Preparing new paste…": "正在准备新的粘贴内容", "Preparing new paste…": "正在准备新的粘贴内容",
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "如果此消息一直存在,请参考 <a href=\"%s\">这里的 FAQ英文版</a>排除故障。", "In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "如果此消息一直存在,请参考 <a href=\"%s\">这里的 FAQ英文版</a>排除故障。",
"+++ no paste text +++": "+++ 无粘贴内容 +++", "+++ no paste text +++": "+++ 无粘贴内容 +++",
"Could not get paste data: %s": "无法获取粘贴数据:%s", "Could not get paste data: %s": "无法获取粘贴数据:%s",
@ -208,7 +200,7 @@
"Retry": "重试", "Retry": "重试",
"Showing raw text…": "显示原始文字…", "Showing raw text…": "显示原始文字…",
"Notice:": "注意:", "Notice:": "注意:",
"This link will expire after %s.": "链接将会在 %s 过期。", "This link will expire after %s.": "这个链接将会在 %s 过期。",
"This link can only be accessed once, do not use back or refresh button in your browser.": "此链接只能被访问一次,请勿使用浏览器中的返回和刷新按钮。", "This link can only be accessed once, do not use back or refresh button in your browser.": "此链接只能被访问一次,请勿使用浏览器中的返回和刷新按钮。",
"Link:": "链接:", "Link:": "链接:",
"Recipient may become aware of your timezone, convert time to UTC?": "收件人可能会知道您的时区,将时间转换为 UTC", "Recipient may become aware of your timezone, convert time to UTC?": "收件人可能会知道您的时区,将时间转换为 UTC",
@ -225,15 +217,14 @@
"Error parsing YOURLS response.": "解析 YOURLS 响应时出错。", "Error parsing YOURLS response.": "解析 YOURLS 响应时出错。",
"This secret message can only be displayed once. Would you like to see it now?": "读取粘贴后只能在加载时显示一次。您想现在打开吗?", "This secret message can only be displayed once. Would you like to see it now?": "读取粘贴后只能在加载时显示一次。您想现在打开吗?",
"Yes, see it": "是的,加载它", "Yes, see it": "是的,加载它",
"Dark Mode": "夜间模式", "Dark Mode": "暗黑模式",
"Error compressing paste, due to missing WebAssembly support.": "由于缺少 WebAssembly 支持,在压缩粘贴时出错。", "Error compressing paste, due to missing WebAssembly support.": "由于缺少 WebAssembly 支持,在压缩粘贴时出错。",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "解压粘贴时出错,您的浏览器不支持 WebAssembly。请使用其他浏览器查看此粘贴。", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "解压粘贴时出错,您的浏览器不支持 WebAssembly。请使用其他浏览器查看此粘贴。",
"Start over": "重新开始", "Start over": "重新开始",
"Paste copied to clipboard": "粘贴内容已复制到剪贴板", "Paste copied to clipboard": "Paste copied to clipboard",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "要复制粘贴内容,请点击复制按钮或使用快捷键 <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>。", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "复制链接", "Copy link": "Copy link",
"Link copied to clipboard": "链接已复制到剪贴板", "Link copied to clipboard": "Link copied to clipboard",
"Paste text": "粘贴文本", "Paste text": "Paste text",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tab 键可作为字符(按 <kbd>Ctrl</kbd>+<kbd>m</kbd> 或 <kbd>Esc</kbd> 切换开关)", "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
"Theme": "主题"
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 749 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 B

After

Width:  |  Height:  |  Size: 266 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 B

After

Width:  |  Height:  |  Size: 253 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 B

After

Width:  |  Height:  |  Size: 157 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 B

After

Width:  |  Height:  |  Size: 299 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 183 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

After

Width:  |  Height:  |  Size: 209 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 196 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

View file

@ -5,18 +5,19 @@ global.assert = require('assert');
global.jsc = require('jsverify'); global.jsc = require('jsverify');
global.jsdom = require('jsdom-global'); global.jsdom = require('jsdom-global');
global.cleanup = global.jsdom(); global.cleanup = global.jsdom();
global.URL = require('jsdom-url').URL;
global.fs = require('fs'); global.fs = require('fs');
global.WebCrypto = require('@peculiar/webcrypto').Crypto; global.WebCrypto = require('@peculiar/webcrypto').Crypto;
// application libraries to test // application libraries to test
global.$ = global.jQuery = require('./jquery-3.7.1'); global.$ = global.jQuery = require('./jquery-3.7.1');
global.RawDeflate = require('./rawinflate-0.3').RawDeflate; global.RawDeflate = require('./rawinflate-0.3').RawDeflate;
global.zlib = require('./zlib-1.3.1-1').zlib; global.zlib = require('./zlib-1.3.1').zlib;
require('./prettify'); require('./prettify');
global.prettyPrint = window.PR.prettyPrint; global.prettyPrint = window.PR.prettyPrint;
global.prettyPrintOne = window.PR.prettyPrintOne; global.prettyPrintOne = window.PR.prettyPrintOne;
global.showdown = require('./showdown-2.1.0'); global.showdown = require('./showdown-2.1.0');
global.DOMPurify = require('./purify-3.2.6'); global.DOMPurify = require('./purify-3.2.4');
global.baseX = require('./base-x-4.0.0').baseX; global.baseX = require('./base-x-4.0.0').baseX;
global.Legacy = require('./legacy').Legacy; global.Legacy = require('./legacy').Legacy;
require('./bootstrap-3.4.1'); require('./bootstrap-3.4.1');
@ -78,16 +79,8 @@ function parseMime(line) {
} }
// common testing helper functions // common testing helper functions
// as of jsDOM 22 the base64 functions provided in the DOM are more restrictive exports.atob = atob;
// than browser implementation and throw when being passed invalid unicode exports.btoa = btoa;
// codepoints - as we use these in the encryption with binary data, we need
// these to be character encoding agnostic
exports.atob = function(encoded) {
return Buffer.from(encoded, 'base64').toString('binary');
}
exports.btoa = function(text) {
return Buffer.from(text, 'binary').toString('base64');
}
// provides random lowercase characters from a to z // provides random lowercase characters from a to z
exports.jscA2zString = function() { exports.jscA2zString = function() {

1992
js/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,18 @@
{ {
"name": "privatebin", "name": "privatebin",
"version": "1.7.8", "version": "1.7.6",
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).", "description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
"main": "privatebin.js", "main": "privatebin.js",
"directories": { "directories": {
"test": "test" "test": "test"
}, },
"dependencies": {},
"devDependencies": { "devDependencies": {
"@peculiar/webcrypto": "^1.5.0", "jsdom": "^9.12.0",
"jsdom": "^26.0.0", "jsdom-global": "^2.1.1",
"jsdom-global": "^3.0.2", "jsdom-url": "^2.2.1",
"jsverify": "^0.8.3" "jsverify": "^0.8.3",
"@peculiar/webcrypto": "^1.1.1"
}, },
"scripts": { "scripts": {
"test": "mocha", "test": "mocha",

View file

@ -58,6 +58,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
*/ */
const purifyHtmlConfig = { const purifyHtmlConfig = {
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i, ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
SAFE_FOR_JQUERY: true,
USE_PROFILES: { USE_PROFILES: {
html: true html: true
} }
@ -591,32 +592,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
return expirationDate; 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 * resets state, used for unit testing
* *
@ -628,17 +603,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
baseUri = null; baseUri = null;
}; };
/**
* check if bootstrap5 object detected
*
* @name Helper.isBootstrap5
* @returns {Boolean}
*/
me.isBootstrap5 = function ()
{
return typeof bootstrap !== 'undefined';
};
return me; return me;
})(); })();
@ -2330,19 +2294,26 @@ jQuery.PrivateBin = (function($, RawDeflate) {
me.requestLoadConfirmation = function() me.requestLoadConfirmation = function()
{ {
const $loadconfirmmodal = $('#loadconfirmmodal'); const $loadconfirmmodal = $('#loadconfirmmodal');
if ($loadconfirmmodal.length > 0) {
const $loadconfirmOpenNow = $loadconfirmmodal.find('#loadconfirm-open-now'); const $loadconfirmOpenNow = $loadconfirmmodal.find('#loadconfirm-open-now');
$loadconfirmOpenNow.off('click.loadPaste'); $loadconfirmOpenNow.off('click.loadPaste');
$loadconfirmOpenNow.on('click.loadPaste', PasteDecrypter.run); $loadconfirmOpenNow.on('click.loadPaste', PasteDecrypter.run);
const $loadconfirmClose = $loadconfirmmodal.find('.close');
const $loadconfirmClose = $loadconfirmmodal.find('.close'); $loadconfirmClose.off('click.close');
$loadconfirmClose.off('click.close'); $loadconfirmClose.on('click.close', Controller.newPaste);
$loadconfirmClose.on('click.close', Controller.newPaste); if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip.VERSION) {
(new bootstrap.Modal($loadconfirmmodal[0])).show();
if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip.VERSION) { } else {
(new bootstrap.Modal($loadconfirmmodal[0])).show(); $loadconfirmmodal.modal('show');
}
} else { } else {
$loadconfirmmodal.modal('show'); if (window.confirm(
I18n._('This secret message can only be displayed once. Would you like to see it now?')
)) {
PasteDecrypter.run();
} else {
Controller.newPaste();
}
} }
} }
@ -2364,6 +2335,15 @@ jQuery.PrivateBin = (function($, RawDeflate) {
return; return;
} }
// fallback to old method for page template
password = prompt(I18n._('Please enter the password for this paste:'), '');
if (password === null) {
throw 'password prompt canceled';
}
if (password.length === 0) {
// recurse…
return me.requestPassword();
}
PasteDecrypter.run(); PasteDecrypter.run();
}; };
@ -2557,18 +2537,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// show preview // show preview
PasteViewer.setText($message.val()); PasteViewer.setText($message.val());
if (AttachmentViewer.hasAttachmentData()) { if (AttachmentViewer.hasAttachmentData()) {
const attachmentsData = AttachmentViewer.getAttachmentsData(); const attachment = AttachmentViewer.getAttachment();
AttachmentViewer.handleBlobAttachmentPreview(
attachmentsData.forEach(attachmentData => { AttachmentViewer.getAttachmentPreview(),
const mimeType = AttachmentViewer.getAttachmentMimeType(attachmentData); attachment[0], attachment[1]
);
AttachmentViewer.handleBlobAttachmentPreview(
AttachmentViewer.getAttachmentPreview(),
attachmentData, mimeType
);
});
AttachmentViewer.showAttachment();
} }
PasteViewer.run(); PasteViewer.run();
@ -2953,12 +2926,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
const AttachmentViewer = (function () { const AttachmentViewer = (function () {
const me = {}; const me = {};
let $attachmentPreview, let $attachmentLink,
$attachmentPreview,
$attachment, $attachment,
attachmentsData = [], attachmentData,
files, file,
$fileInput, $fileInput,
$dragAndDropFileNames, $dragAndDropFileName,
attachmentHasPreview = false,
$dropzone; $dropzone;
/** /**
@ -3000,30 +2975,26 @@ jQuery.PrivateBin = (function($, RawDeflate) {
me.setAttachment = function(attachmentData, fileName) me.setAttachment = function(attachmentData, fileName)
{ {
// skip, if attachments got disabled // skip, if attachments got disabled
if (!$attachment || !$attachmentPreview) return; if (!$attachmentLink || !$attachmentPreview) return;
// data URI format: data:[<mimeType>][;base64],<data> // data URI format: data:[<mimeType>][;base64],<data>
const template = Model.getTemplate('attachment');
const attachmentLink = template.find('a');
// position in data URI string of where data begins // position in data URI string of where data begins
const base64Start = attachmentData.indexOf(',') + 1; const base64Start = attachmentData.indexOf(',') + 1;
// position in data URI string of where mimeType ends
const mimeTypeEnd = attachmentData.indexOf(';');
const mimeType = me.getAttachmentMimeType(attachmentData); // extract mimeType
const mimeType = attachmentData.substring(5, mimeTypeEnd);
// extract data and convert to binary // extract data and convert to binary
const rawData = attachmentData.substring(base64Start); const rawData = attachmentData.substring(base64Start);
const decodedData = rawData.length > 0 ? atob(rawData) : ''; const decodedData = rawData.length > 0 ? atob(rawData) : '';
let blobUrl = getBlobUrl(decodedData, mimeType); let blobUrl = getBlobUrl(decodedData, mimeType);
attachmentLink.attr('href', blobUrl); $attachmentLink.attr('href', blobUrl);
if (typeof fileName !== 'undefined') { if (typeof fileName !== 'undefined') {
attachmentLink.attr('download', fileName); $attachmentLink.attr('download', fileName);
const fileSize = Helper.formatBytes(decodedData.length);
template.append(`(${fileName}, ${fileSize})`);
} }
// sanitize SVG preview // sanitize SVG preview
@ -3038,9 +3009,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
blobUrl = getBlobUrl(sanitizedData, mimeType); blobUrl = getBlobUrl(sanitizedData, mimeType);
} }
template.removeClass('hidden');
$attachment.append(template);
me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mimeType); me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mimeType);
}; };
@ -3057,7 +3025,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
$attachment.removeClass('hidden'); $attachment.removeClass('hidden');
if (me.hasAttachmentPreview()) { if (attachmentHasPreview) {
$attachmentPreview.removeClass('hidden'); $attachmentPreview.removeClass('hidden');
} }
}; };
@ -3078,9 +3046,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
} }
me.hideAttachment(); me.hideAttachment();
me.hideAttachmentPreview(); me.hideAttachmentPreview();
$attachment.html(''); $attachmentLink.removeAttr('href');
$attachmentLink.removeAttr('download');
$attachmentLink.off('click');
$attachmentPreview.html(''); $attachmentPreview.html('');
$dragAndDropFileNames.html(''); $dragAndDropFileName.text('');
AttachmentViewer.removeAttachmentData(); AttachmentViewer.removeAttachmentData();
}; };
@ -3095,8 +3065,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
*/ */
me.removeAttachmentData = function() me.removeAttachmentData = function()
{ {
files = undefined; file = undefined;
attachmentsData = []; attachmentData = undefined;
}; };
/** /**
@ -3107,21 +3077,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
*/ */
me.clearDragAndDrop = function() me.clearDragAndDrop = function()
{ {
$dragAndDropFileNames.html(''); $dragAndDropFileName.text('');
}; };
/**
* Print file names added via drag & drop
*
* @name AttachmentViewer.printDragAndDropFileNames
* @private
* @function
* @param {array} fileNames
*/
function printDragAndDropFileNames(fileNames) {
$dragAndDropFileNames.html(fileNames.join("<br>"));
}
/** /**
* hides the attachment * hides the attachment
* *
@ -3150,18 +3108,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
} }
}; };
/**
* checks if has any attachment preview
*
* @name AttachmentViewer.hasAttachmentPreview
* @function
* @return {JQuery}
*/
me.hasAttachmentPreview = function()
{
return $attachmentPreview.children().length > 0;
}
/** /**
* checks if there is an attachment displayed * checks if there is an attachment displayed
* *
@ -3173,7 +3119,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
if (!$attachment.length) { if (!$attachment.length) {
return false; return false;
} }
return [...$attachment.children()].length > 0; const link = $attachmentLink.prop('href');
return (typeof link !== 'undefined' && link !== '');
}; };
/** /**
@ -3193,38 +3140,20 @@ jQuery.PrivateBin = (function($, RawDeflate) {
}; };
/** /**
* return the attachments * return the attachment
* *
* @name AttachmentViewer.getAttachments * @name AttachmentViewer.getAttachment
* @function * @function
* @returns {array} * @returns {array}
*/ */
me.getAttachments = function() me.getAttachment = function()
{ {
return [...$attachment.find('a')].map(link => ( return [
[ $attachmentLink.prop('href'),
$(link).prop('href'), $attachmentLink.prop('download')
$(link).prop('download') ];
]
));
}; };
/**
* Get attachment mime type
*
* @name AttachmentViewer.getAttachmentMimeType
* @function
* @param {string} attachmentData - Base64 string
*/
me.getAttachmentMimeType = function(attachmentData)
{
// position in data URI string of where mimeType ends
const mimeTypeEnd = attachmentData.indexOf(';');
// extract mimeType
return attachmentData.substring(5, mimeTypeEnd);
}
/** /**
* moves the attachment link to another element * moves the attachment link to another element
* *
@ -3233,36 +3162,27 @@ jQuery.PrivateBin = (function($, RawDeflate) {
* @name AttachmentViewer.moveAttachmentTo * @name AttachmentViewer.moveAttachmentTo
* @function * @function
* @param {jQuery} $element - the wrapper/container element where this should be moved to * @param {jQuery} $element - the wrapper/container element where this should be moved to
* @param {array} attachment - attachment data
* @param {string} label - the text to show (%s will be replaced with the file name), will automatically be translated * @param {string} label - the text to show (%s will be replaced with the file name), will automatically be translated
*/ */
me.moveAttachmentTo = function($element, attachment, label) me.moveAttachmentTo = function($element, label)
{ {
const attachmentLink = $(document.createElement('a'))
.addClass('alert-link')
.prop('href', attachment[0])
.prop('download', attachment[1]);
// move elemement to new place // move elemement to new place
attachmentLink.appendTo($element); $attachmentLink.appendTo($element);
// update text - ensuring no HTML is inserted into the text node // update text - ensuring no HTML is inserted into the text node
I18n._(attachmentLink, label, attachment[1]); I18n._($attachmentLink, label, $attachmentLink.attr('download'));
}; };
/** /**
* read files data as data URL using the FileReader API * read file data as data URL using the FileReader API
* *
* @name AttachmentViewer.readFileData * @name AttachmentViewer.readFileData
* @private * @private
* @function * @function
* @param {FileList[]} loadedFiles (optional) loaded files array * @param {object} loadedFile (optional) loaded file object
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL()} * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL()}
*/ */
function readFileData(loadedFiles) { function readFileData(loadedFile) {
// Clear old cache
me.removeAttachmentData();
if (typeof FileReader === 'undefined') { if (typeof FileReader === 'undefined') {
// revert loading status… // revert loading status…
me.hideAttachment(); me.hideAttachment();
@ -3271,35 +3191,28 @@ jQuery.PrivateBin = (function($, RawDeflate) {
return; return;
} }
if (loadedFiles === undefined) { const fileReader = new FileReader();
loadedFiles = [...$fileInput[0].files]; if (loadedFile === undefined) {
me.clearDragAndDrop(); loadedFile = $fileInput[0].files[0];
$dragAndDropFileName.text('');
} else { } else {
const fileNames = loadedFiles.map((loadedFile => loadedFile.name)); $dragAndDropFileName.text(loadedFile.name);
printDragAndDropFileNames(fileNames);
} }
if (typeof loadedFiles !== 'undefined') { if (typeof loadedFile !== 'undefined') {
files = loadedFiles; file = loadedFile;
loadedFiles.forEach((loadedFile, index) => { fileReader.onload = function (event) {
const fileReader = new FileReader(); const dataURL = event.target.result;
attachmentData = dataURL;
fileReader.onload = function (event) { if (Editor.isPreview()) {
const dataURL = event.target.result; me.handleAttachmentPreview($attachmentPreview, dataURL);
if (dataURL) { $attachmentPreview.removeClass('hidden');
attachmentsData[index] = dataURL; }
}
if (Editor.isPreview()) { TopNav.highlightFileupload();
me.handleAttachmentPreview($attachmentPreview, dataURL); };
$attachmentPreview.removeClass('hidden'); fileReader.readAsDataURL(loadedFile);
}
TopNav.highlightFileupload();
};
fileReader.readAsDataURL(loadedFile);
});
} else { } else {
me.removeAttachmentData(); me.removeAttachmentData();
} }
@ -3315,17 +3228,16 @@ jQuery.PrivateBin = (function($, RawDeflate) {
* @argument {string} mime type * @argument {string} mime type
*/ */
me.handleBlobAttachmentPreview = function ($targetElement, blobUrl, mimeType) { me.handleBlobAttachmentPreview = function ($targetElement, blobUrl, mimeType) {
const alreadyIncludesCurrentAttachment = $targetElement.find(`[src='${blobUrl}']`).length > 0; if (blobUrl) {
attachmentHasPreview = true;
if (blobUrl && !alreadyIncludesCurrentAttachment) {
if (mimeType.match(/^image\//i)) { if (mimeType.match(/^image\//i)) {
$targetElement.append( $targetElement.html(
$(document.createElement('img')) $(document.createElement('img'))
.attr('src', blobUrl) .attr('src', blobUrl)
.attr('class', 'img-thumbnail') .attr('class', 'img-thumbnail')
); );
} else if (mimeType.match(/^video\//i)) { } else if (mimeType.match(/^video\//i)) {
$targetElement.append( $targetElement.html(
$(document.createElement('video')) $(document.createElement('video'))
.attr('controls', 'true') .attr('controls', 'true')
.attr('autoplay', 'true') .attr('autoplay', 'true')
@ -3336,7 +3248,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
.attr('src', blobUrl)) .attr('src', blobUrl))
); );
} else if (mimeType.match(/^audio\//i)) { } else if (mimeType.match(/^audio\//i)) {
$targetElement.append( $targetElement.html(
$(document.createElement('audio')) $(document.createElement('audio'))
.attr('controls', 'true') .attr('controls', 'true')
.attr('autoplay', 'true') .attr('autoplay', 'true')
@ -3349,13 +3261,15 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// Fallback for browsers, that don't support the vh unit // Fallback for browsers, that don't support the vh unit
const clientHeight = $(window).height(); const clientHeight = $(window).height();
$targetElement.append( $targetElement.html(
$(document.createElement('embed')) $(document.createElement('embed'))
.attr('src', blobUrl) .attr('src', blobUrl)
.attr('type', 'application/pdf') .attr('type', 'application/pdf')
.attr('class', 'pdfPreview') .attr('class', 'pdfPreview')
.css('height', clientHeight) .css('height', clientHeight)
); );
} else {
attachmentHasPreview = false;
} }
} }
}; };
@ -3388,14 +3302,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
} }
if ($fileInput) { if ($fileInput) {
const files = [...evt.dataTransfer.files]; const file = evt.dataTransfer.files[0];
//Clear the file input: //Clear the file input:
$fileInput.wrap('<form>').closest('form').get(0).reset(); $fileInput.wrap('<form>').closest('form').get(0).reset();
$fileInput.unwrap(); $fileInput.unwrap();
//Only works in Chrome: //Only works in Chrome:
//fileInput[0].files = e.dataTransfer.files; //fileInput[0].files = e.dataTransfer.files;
readFileData(files); readFileData(file);
} }
}; };
@ -3432,17 +3346,16 @@ jQuery.PrivateBin = (function($, RawDeflate) {
function addClipboardEventHandler() { function addClipboardEventHandler() {
$(document).on('paste', function (event) { $(document).on('paste', function (event) {
const items = (event.clipboardData || event.originalEvent.clipboardData).items; const items = (event.clipboardData || event.originalEvent.clipboardData).items;
const files = [...items] const lastItem = items[items.length - 1];
.filter(item => item.kind === 'file') if (lastItem.kind === 'file') {
.map(item => item.getAsFile()); if (TopNav.isAttachmentReadonly()) {
event.stopPropagation();
if (TopNav.isAttachmentReadonly()) { event.preventDefault();
event.stopPropagation(); return false;
event.preventDefault(); } else {
return false; readFileData(lastItem.getAsFile());
}
} }
readFileData(files);
}); });
} }
@ -3450,12 +3363,23 @@ jQuery.PrivateBin = (function($, RawDeflate) {
/** /**
* getter for attachment data * getter for attachment data
* *
* @name AttachmentViewer.getAttachmentsData * @name AttachmentViewer.getAttachmentData
* @function * @function
* @return {string[]} * @return {jQuery}
*/ */
me.getAttachmentsData = function () { me.getAttachmentData = function () {
return attachmentsData; return attachmentData;
};
/**
* getter for attachment link
*
* @name AttachmentViewer.getAttachmentLink
* @function
* @return {jQuery}
*/
me.getAttachmentLink = function () {
return $attachmentLink;
}; };
/** /**
@ -3470,14 +3394,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
}; };
/** /**
* getter for files data, returns the file list * getter for file data, returns the file contents
* *
* @name AttachmentViewer.getFiles * @name AttachmentViewer.getFile
* @function * @function
* @return {FileList[]} * @return {string}
*/ */
me.getFiles = function () { me.getFile = function () {
return files; return file;
}; };
/** /**
@ -3491,8 +3415,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
me.init = function() me.init = function()
{ {
$attachment = $('#attachment'); $attachment = $('#attachment');
$dragAndDropFileNames = $('#dragAndDropFileName'); $dragAndDropFileName = $('#dragAndDropFileName');
$dropzone = $('#dropzone'); $dropzone = $('#dropzone');
$attachmentLink = $('#attachment a') || $('<a>');
if($attachment.length) { if($attachment.length) {
$attachmentPreview = $('#attachmentPreview'); $attachmentPreview = $('#attachmentPreview');
@ -3553,12 +3478,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
{ {
const $source = $(event.target); const $source = $(event.target);
// show all reply buttons
$commentContainer.find('button').removeClass('hidden');
// hide the current reply button
$source.addClass('hidden');
// clear input // clear input
$replyMessage.val(''); $replyMessage.val('');
$replyNickname.val(''); $replyNickname.val('');
@ -4014,26 +3933,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
*/ */
function setLanguage(event) function setLanguage(event)
{ {
let lang = $(event.target).data('lang') || event.target.value; document.cookie = 'lang=' + $(event.target).data('lang') + '; SameSite=Lax; Secure';
document.cookie = 'lang=' + lang + '; SameSite=Lax; Secure';
window.location.reload();
event.preventDefault();
}
/**
* save the template in a cookie and reloads the page
*
* @name TopNav.setTemplate
* @private
* @function
* @param {Event} event
*/
function setTemplate(event)
{
let template = $(event.target).data('template') || event.target.value;
document.cookie = 'template=' + template + '; SameSite=Lax; Secure';
window.location.reload(); window.location.reload();
event.preventDefault(); event.preventDefault();
} }
@ -4189,42 +4089,59 @@ jQuery.PrivateBin = (function($, RawDeflate) {
expirationDateRoundedToSecond.setUTCSeconds(0); expirationDateRoundedToSecond.setUTCSeconds(0);
const $emailconfirmmodal = $('#emailconfirmmodal'); const $emailconfirmmodal = $('#emailconfirmmodal');
if (expirationDate !== null) { if ($emailconfirmmodal.length > 0) {
const $emailconfirmTimezoneCurrent = $emailconfirmmodal.find('#emailconfirm-timezone-current'); if (expirationDate !== null) {
const $emailconfirmTimezoneUtc = $emailconfirmmodal.find('#emailconfirm-timezone-utc'); const $emailconfirmTimezoneCurrent = $emailconfirmmodal.find('#emailconfirm-timezone-current');
let localeConfiguration = { dateStyle: 'long', timeStyle: 'long' }; const $emailconfirmTimezoneUtc = $emailconfirmmodal.find('#emailconfirm-timezone-utc');
const bootstrap5EmailConfirmModal = typeof bootstrap !== 'undefined' && bootstrap.Tooltip.VERSION ? let localeConfiguration = { dateStyle: 'long', timeStyle: 'long' };
new bootstrap.Modal($emailconfirmmodal[0]) : null; const bootstrap5EmailConfirmModal = typeof bootstrap !== 'undefined' && bootstrap.Tooltip.VERSION ?
new bootstrap.Modal($emailconfirmmodal[0]) : null;
function sendEmailAndHideModal() { function sendEmailAndHideModal() {
const emailBody = templateEmailBody( const emailBody = templateEmailBody(
// we don't use Date.prototype.toUTCString() because we would like to avoid GMT // we don't use Date.prototype.toUTCString() because we would like to avoid GMT
expirationDateRoundedToSecond.toLocaleString( expirationDateRoundedToSecond.toLocaleString(
[], localeConfiguration [], localeConfiguration
), isBurnafterreading ), isBurnafterreading
); );
if (bootstrap5EmailConfirmModal) {
bootstrap5EmailConfirmModal.hide();
} else {
$emailconfirmmodal.modal('hide');
}
triggerEmailSend(emailBody);
};
$emailconfirmTimezoneCurrent.off('click.sendEmailCurrentTimezone');
$emailconfirmTimezoneCurrent.on('click.sendEmailCurrentTimezone', sendEmailAndHideModal);
$emailconfirmTimezoneUtc.off('click.sendEmailUtcTimezone');
$emailconfirmTimezoneUtc.on('click.sendEmailUtcTimezone', () => {
localeConfiguration.timeZone = 'UTC';
sendEmailAndHideModal();
});
if (bootstrap5EmailConfirmModal) { if (bootstrap5EmailConfirmModal) {
bootstrap5EmailConfirmModal.hide(); bootstrap5EmailConfirmModal.show();
} else { } else {
$emailconfirmmodal.modal('hide'); $emailconfirmmodal.modal('show');
} }
triggerEmailSend(emailBody);
};
$emailconfirmTimezoneCurrent.off('click.sendEmailCurrentTimezone');
$emailconfirmTimezoneCurrent.on('click.sendEmailCurrentTimezone', sendEmailAndHideModal);
$emailconfirmTimezoneUtc.off('click.sendEmailUtcTimezone');
$emailconfirmTimezoneUtc.on('click.sendEmailUtcTimezone', () => {
localeConfiguration.timeZone = 'UTC';
sendEmailAndHideModal();
});
if (bootstrap5EmailConfirmModal) {
bootstrap5EmailConfirmModal.show();
} else { } else {
$emailconfirmmodal.modal('show'); triggerEmailSend(templateEmailBody(null, isBurnafterreading));
} }
} else { } else {
triggerEmailSend(templateEmailBody(null, isBurnafterreading)); let emailBody = '';
if (expirationDate !== null) {
const expirationDateString = window.confirm(
I18n._('Recipient may become aware of your timezone, convert time to UTC?')
) ? expirationDateRoundedToSecond.toLocaleString(
undefined,
// we don't use Date.prototype.toUTCString() because we would like to avoid GMT
{ timeZone: 'UTC', dateStyle: 'long', timeStyle: 'long' }
) : expirationDateRoundedToSecond.toLocaleString();
emailBody = templateEmailBody(expirationDateString, isBurnafterreading);
} else {
emailBody = templateEmailBody(null, isBurnafterreading);
}
triggerEmailSend(emailBody);
} }
} }
@ -4643,11 +4560,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// visually indicate file uploaded // visually indicate file uploaded
const $attachDropdownToggle = $attach.children('.dropdown-toggle'); const $attachDropdownToggle = $attach.children('.dropdown-toggle');
if ($attachDropdownToggle.attr('aria-expanded') === 'false') { if ($attachDropdownToggle.attr('aria-expanded') === 'false') {
if (Helper.isBootstrap5()) { $attachDropdownToggle.click();
new bootstrap.Dropdown($attachDropdownToggle).toggle();
} else {
$attachDropdownToggle.click();
}
} }
$fileWrap.addClass('highlight'); $fileWrap.addClass('highlight');
setTimeout(function () { setTimeout(function () {
@ -4711,9 +4624,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// bootstrap template drop down // bootstrap template drop down
$('#language ul.dropdown-menu li a').click(setLanguage); $('#language ul.dropdown-menu li a').click(setLanguage);
// page template drop down
// bootstrap template drop down $('#language select option').click(setLanguage);
$('#template ul.dropdown-menu li a').click(setTemplate);
// bind events // bind events
$burnAfterReading.change(changeBurnAfterReading); $burnAfterReading.change(changeBurnAfterReading);
@ -5194,7 +5106,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
const plainText = Editor.getText(), const plainText = Editor.getText(),
format = PasteViewer.getFormat(), format = PasteViewer.getFormat(),
// the methods may return different values if no files are attached (null, undefined or false) // the methods may return different values if no files are attached (null, undefined or false)
files = TopNav.getFileList() || AttachmentViewer.getFiles() || AttachmentViewer.hasAttachment(); files = TopNav.getFileList() || AttachmentViewer.getFile() || AttachmentViewer.hasAttachment();
// do not send if there is no data // do not send if there is no data
if (plainText.length === 0 && !files) { if (plainText.length === 0 && !files) {
@ -5234,64 +5146,62 @@ jQuery.PrivateBin = (function($, RawDeflate) {
PasteViewer.setFormat(format); PasteViewer.setFormat(format);
// prepare cypher message // prepare cypher message
let attachmentsData = AttachmentViewer.getAttachmentsData(), let file = AttachmentViewer.getAttachmentData(),
cipherMessage = { cipherMessage = {
'paste': plainText 'paste': plainText
}; };
if (attachmentsData.length) { if (typeof file !== 'undefined' && file !== null) {
cipherMessage['attachment'] = attachmentsData; cipherMessage['attachment'] = file;
cipherMessage['attachment_name'] = AttachmentViewer.getFiles().map((fileInfo => fileInfo.name)); cipherMessage['attachment_name'] = AttachmentViewer.getFile().name;
} else if (AttachmentViewer.hasAttachment()) { } else if (AttachmentViewer.hasAttachment()) {
// fall back to cloned part // fall back to cloned part
let attachments = AttachmentViewer.getAttachments(); let attachment = AttachmentViewer.getAttachment();
cipherMessage['attachment'] = attachments.map(attachment => attachment[0]); cipherMessage['attachment'] = attachment[0];
cipherMessage['attachment_name'] = attachments.map(attachment => attachment[1]); cipherMessage['attachment_name'] = attachment[1];
cipherMessage['attachment'] = await Promise.all(cipherMessage['attachment'].map(async (attachment) => { // we need to retrieve data from blob if browser already parsed it in memory
// we need to retrieve data from blob if browser already parsed it in memory if (typeof attachment[0] === 'string' && attachment[0].startsWith('blob:')) {
if (typeof attachment === 'string' && attachment.startsWith('blob:')) { Alert.showStatus(
Alert.showStatus( [
[ 'Retrieving cloned file \'%s\' from memory...',
'Retrieving cloned file \'%s\' from memory...', attachment[1]
attachment[1] ],
], 'copy'
'copy' );
); try {
try { const blobData = await $.ajax({
const blobData = await $.ajax({ type: 'GET',
type: 'GET', url: `${attachment[0]}`,
url: `${attachment}`, processData: false,
processData: false, timeout: 10000,
timeout: 10000, xhrFields: {
xhrFields: { withCredentials: false,
withCredentials: false, responseType: 'blob'
responseType: 'blob'
}
});
if (blobData instanceof window.Blob) {
const fileReading = new Promise(function(resolve, reject) {
const fileReader = new FileReader();
fileReader.onload = function (event) {
resolve(event.target.result);
};
fileReader.onerror = function (error) {
reject(error);
}
fileReader.readAsDataURL(blobData);
});
return await fileReading;
} else {
const error = 'Cannot process attachment data.';
Alert.showError(error);
throw new TypeError(error);
} }
} catch (error) { });
Alert.showError('Cannot retrieve attachment.'); if (blobData instanceof window.Blob) {
throw error; const fileReading = new Promise(function(resolve, reject) {
const fileReader = new FileReader();
fileReader.onload = function (event) {
resolve(event.target.result);
};
fileReader.onerror = function (error) {
reject(error);
}
fileReader.readAsDataURL(blobData);
});
cipherMessage['attachment'] = await fileReading;
} else {
const error = 'Cannot process attachment data.';
Alert.showError(error);
throw new TypeError(error);
} }
} catch (error) {
console.error(error);
Alert.showError('Cannot retrieve attachment.');
throw error;
} }
})); }
} }
// encrypt message // encrypt message
@ -5386,15 +5296,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// version 2 paste // version 2 paste
const pasteMessage = JSON.parse(pastePlain); const pasteMessage = JSON.parse(pastePlain);
if (pasteMessage.hasOwnProperty('attachment') && pasteMessage.hasOwnProperty('attachment_name')) { if (pasteMessage.hasOwnProperty('attachment') && pasteMessage.hasOwnProperty('attachment_name')) {
if (Array.isArray(pasteMessage.attachment) && Array.isArray(pasteMessage.attachment_name)) { AttachmentViewer.setAttachment(pasteMessage.attachment, pasteMessage.attachment_name);
pasteMessage.attachment.forEach((attachment, key) => {
const attachment_name = pasteMessage.attachment_name[key];
AttachmentViewer.setAttachment(attachment, attachment_name);
});
} else {
// Continue to process attachment parameters as strings to ensure backward compatibility
AttachmentViewer.setAttachment(pasteMessage.attachment, pasteMessage.attachment_name);
}
AttachmentViewer.showAttachment(); AttachmentViewer.showAttachment();
} }
pastePlain = pasteMessage.paste; pastePlain = pasteMessage.paste;
@ -5485,7 +5387,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
me.run = function(paste) me.run = function(paste)
{ {
Alert.hideMessages(); Alert.hideMessages();
Alert.setCustomHandler(null);
Alert.showLoading('Decrypting paste…', 'cloud-download'); Alert.showLoading('Decrypting paste…', 'cloud-download');
if (typeof paste === 'undefined' || paste.type === 'click') { if (typeof paste === 'undefined' || paste.type === 'click') {
@ -5503,9 +5404,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
me.run(paste); me.run(paste);
}); });
// Clear attachments to prevent duplicates
AttachmentViewer.removeAttachment();
// decrypt paste & attachments // decrypt paste & attachments
decryptionPromises.push(decryptPaste(paste, key, password)); decryptionPromises.push(decryptPaste(paste, key, password));
@ -5880,14 +5778,10 @@ jQuery.PrivateBin = (function($, RawDeflate) {
history.pushState({type: 'clone'}, document.title, Helper.baseUri()); history.pushState({type: 'clone'}, document.title, Helper.baseUri());
if (AttachmentViewer.hasAttachment()) { if (AttachmentViewer.hasAttachment()) {
const attachments = AttachmentViewer.getAttachments(); AttachmentViewer.moveAttachmentTo(
attachments.forEach(attachment => { TopNav.getCustomAttachment(),
AttachmentViewer.moveAttachmentTo( 'Cloned: \'%s\''
TopNav.getCustomAttachment(), );
attachment,
'Cloned: \'%s\''
);
});
TopNav.hideFileSelector(); TopNav.hideFileSelector();
AttachmentViewer.hideAttachment(); AttachmentViewer.hideAttachment();
// NOTE: it also looks nice without removing the attachment // NOTE: it also looks nice without removing the attachment
@ -5895,12 +5789,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
AttachmentViewer.hideAttachmentPreview(); AttachmentViewer.hideAttachmentPreview();
TopNav.showCustomAttachment(); TopNav.showCustomAttachment();
// show another status messages to make the user aware that the // show another status message to make the user aware that the
// files were cloned too! // file was cloned too!
Alert.showStatus( Alert.showStatus(
[ [
'The cloned file \'%s\' was attached to this paste.', 'The cloned file \'%s\' was attached to this paste.',
attachments.map(attachment => attachment[1]).join(', '), AttachmentViewer.getAttachment()[1]
], ],
'copy' 'copy'
); );

2
js/purify-3.2.4.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
'use strict'; 'use strict';
const common = require('../common'); var common = require('../common');
describe('AttachmentViewer', function () { describe('AttachmentViewer', function () {
describe('setAttachment, showAttachment, removeAttachment, hideAttachment, hideAttachmentPreview, hasAttachment, getAttachment & moveAttachmentTo', function () { describe('setAttachment, showAttachment, removeAttachment, hideAttachment, hideAttachmentPreview, hasAttachment, getAttachment & moveAttachmentTo', function () {
@ -14,12 +14,11 @@ describe('AttachmentViewer', function () {
'string', 'string',
function (mimeType, rawdata, filename, prefix, postfix) { function (mimeType, rawdata, filename, prefix, postfix) {
let clean = jsdom(), let clean = jsdom(),
data = 'data:' + mimeType + ';base64,' + common.btoa(rawdata), data = 'data:' + mimeType + ';base64,' + btoa(rawdata),
mimePrefix = mimeType.substring(0, 6),
previewSupported = ( previewSupported = (
mimePrefix === 'image/' || mimeType.substring(0, 6) === 'image/' ||
mimePrefix === 'audio/' || mimeType.substring(0, 6) === 'audio/' ||
mimePrefix === 'video/' || mimeType.substring(0, 6) === 'video/' ||
mimeType.match(/\/pdf/i) mimeType.match(/\/pdf/i)
), ),
results = [], results = [],
@ -27,14 +26,11 @@ describe('AttachmentViewer', function () {
prefix = prefix.replace(/%(s|d)/g, '%%'); prefix = prefix.replace(/%(s|d)/g, '%%');
postfix = postfix.replace(/%(s|d)/g, '%%'); postfix = postfix.replace(/%(s|d)/g, '%%');
$('body').html( $('body').html(
'<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>' + '<div id="attachment" role="alert" class="hidden alert ' +
'<div id="attachment" class="hidden"></div>' + 'alert-info"><span class="glyphicon glyphicon-download-' +
'<div id="templates">' + 'alt" aria-hidden="true"></span> <a class="alert-link">' +
'<div id="attachmenttemplate" role="alert" class="attachment hidden alert alert-info">' + 'Download attachment</a></div><div id="attachmentPrevie' +
'<span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span>' + 'w" class="hidden"></div>'
'<a class="alert-link">Download attachment</a>' +
'</div>' +
'</div>'
); );
// mock createObjectURL for jsDOM // mock createObjectURL for jsDOM
if (typeof window.URL.createObjectURL === 'undefined') { if (typeof window.URL.createObjectURL === 'undefined') {
@ -47,35 +43,29 @@ describe('AttachmentViewer', function () {
) )
} }
$.PrivateBin.AttachmentViewer.init(); $.PrivateBin.AttachmentViewer.init();
$.PrivateBin.Model.init();
results.push( results.push(
!$.PrivateBin.AttachmentViewer.hasAttachment() && !$.PrivateBin.AttachmentViewer.hasAttachment() &&
$('#attachment').hasClass('hidden') && $('#attachment').hasClass('hidden') &&
$('#attachment').children().length === 0 &&
$('#attachmenttemplate').hasClass('hidden') &&
$('#attachmentPreview').hasClass('hidden') $('#attachmentPreview').hasClass('hidden')
); );
global.atob = common.atob;
if (filename.length) { if (filename.length) {
$.PrivateBin.AttachmentViewer.setAttachment(data, filename); $.PrivateBin.AttachmentViewer.setAttachment(data, filename);
} else { } else {
$.PrivateBin.AttachmentViewer.setAttachment(data); $.PrivateBin.AttachmentViewer.setAttachment(data);
} }
// // beyond this point we will get the blob URL instead of the data // beyond this point we will get the blob URL instead of the data
data = window.URL.createObjectURL(data); data = window.URL.createObjectURL(data);
const attachment = $.PrivateBin.AttachmentViewer.getAttachments(); const attachment = $.PrivateBin.AttachmentViewer.getAttachment();
results.push( results.push(
$.PrivateBin.AttachmentViewer.hasAttachment() && $.PrivateBin.AttachmentViewer.hasAttachment() &&
$('#attachment').hasClass('hidden') && $('#attachment').hasClass('hidden') &&
$('#attachment').children().length > 0 &&
$('#attachmentPreview').hasClass('hidden') && $('#attachmentPreview').hasClass('hidden') &&
attachment[0][0] === data && attachment[0] === data &&
attachment[0][1] === filename attachment[1] === filename
); );
$.PrivateBin.AttachmentViewer.showAttachment(); $.PrivateBin.AttachmentViewer.showAttachment();
results.push( results.push(
!$('#attachment').hasClass('hidden') && !$('#attachment').hasClass('hidden') &&
$('#attachment').children().length > 0 &&
(previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden')) (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
); );
$.PrivateBin.AttachmentViewer.hideAttachment(); $.PrivateBin.AttachmentViewer.hideAttachment();
@ -93,7 +83,7 @@ describe('AttachmentViewer', function () {
(previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden')) (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
); );
let element = $('<div>'); let element = $('<div>');
$.PrivateBin.AttachmentViewer.moveAttachmentTo(element, attachment[0], prefix + '%s' + postfix); $.PrivateBin.AttachmentViewer.moveAttachmentTo(element, prefix + '%s' + postfix);
// messageIDs with links get a relaxed treatment // messageIDs with links get a relaxed treatment
if (prefix.indexOf('<a') === -1 && postfix.indexOf('<a') === -1) { if (prefix.indexOf('<a') === -1 && postfix.indexOf('<a') === -1) {
result = $('<textarea>').text((prefix + filename + postfix)).text(); result = $('<textarea>').text((prefix + filename + postfix)).text();
@ -107,17 +97,16 @@ describe('AttachmentViewer', function () {
} }
if (filename.length) { if (filename.length) {
results.push( results.push(
element.find('a')[0].href === data && element.children()[0].href === data &&
element.find('a')[0].getAttribute('download') === filename && element.children()[0].getAttribute('download') === filename &&
element.find('a')[0].text === result element.children()[0].text === result
); );
} else { } else {
results.push(element.find('a')[0].href === data); results.push(element.children()[0].href === data);
} }
$.PrivateBin.AttachmentViewer.removeAttachment(); $.PrivateBin.AttachmentViewer.removeAttachment();
results.push( results.push(
$('#attachment').hasClass('hidden') && $('#attachment').hasClass('hidden') &&
$('#attachment').children().length === 0 &&
$('#attachmentPreview').hasClass('hidden') $('#attachmentPreview').hasClass('hidden')
); );
clean(); clean();

View file

@ -19,9 +19,10 @@ describe('Check', function () {
} }
); );
Legacy.Check.init(); Legacy.Check.init();
const result = Legacy.Check.getInit() && !Legacy.Check.getStatus(); const result1 = Legacy.Check.getInit() && !Legacy.Check.getStatus(),
result2 = (document.getElementById('errormessage').className !== 'hidden');
clean(); clean();
return result; return result1 && result2;
} }
), ),
{tests: 10}); {tests: 10});
@ -66,10 +67,7 @@ describe('Check', function () {
'url': (secureProtocol ? 'https' : 'http' ) + '://' + domain.join('') + '/' 'url': (secureProtocol ? 'https' : 'http' ) + '://' + domain.join('') + '/'
} }
); );
Object.defineProperty(window, 'crypto', { window.crypto = new WebCrypto();
value: new WebCrypto(),
writeable: false,
});
Legacy.Check.init(); Legacy.Check.init();
const result1 = Legacy.Check.getInit() && Legacy.Check.getStatus(), const result1 = Legacy.Check.getInit() && Legacy.Check.getStatus(),
result2 = secureProtocol === (document.getElementById('httpnotice').className === 'hidden'); result2 = secureProtocol === (document.getElementById('httpnotice').className === 'hidden');

View file

@ -4,136 +4,120 @@ const common = require('../common');
describe('CopyToClipboard', function() { describe('CopyToClipboard', function() {
this.timeout(30000); this.timeout(30000);
describe ('Copy paste to clipboard', function () { describe ('Copy paste co clipboard', function () {
jsc.property('Copy with button click', jsc.property('Copy with button click', common.jscFormats(), 'nestring', async function (format, text) {
common.jscFormats(), var clean = jsdom();
'nestring', common.enableClipboard();
async function (format, text) {
var clean = jsdom(); $('body').html(
common.enableClipboard(); '<div id="placeholder" class="hidden">+++ no paste text ' +
'+++</div><div id="prettymessage" class="hidden">' +
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
'<svg id="copySuccessIcon"></svg></button><pre ' +
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
'</div><div id="plaintext" class="hidden"></div>'
);
$.PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run();
$.PrivateBin.CopyToClipboard.init();
$('#prettyMessageCopyBtn').trigger('click');
$('body').html( const savedToClipboardText = await navigator.clipboard.readText();
'<div id="placeholder" class="hidden">+++ no paste text ' +
'+++</div><div id="prettymessage" class="hidden">' + clean();
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
'<svg id="copySuccessIcon"></svg></button><pre ' + return text === savedToClipboardText;
'id="prettyprint" class="prettyprint linenums:1"></pre>' + });
'</div><div id="plaintext" class="hidden"></div>'
);
$.PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run();
$.PrivateBin.CopyToClipboard.init();
$('#prettyMessageCopyBtn').trigger('click');
const savedToClipboardText = await navigator.clipboard.readText();
clean();
return text === savedToClipboardText;
}
);
/** /**
* Unfortunately in JSVerify impossible to check if copy with shortcut when user selected some text on the page * Unfortunately in JSVerify impossible to check if copy with shortcut when user selected some text on the page
* (the copy paste to clipboard should not work in this case) due to lacking window.getSelection() in jsdom. * (the copy paste to clipboard should not work in this case) due to lucking window.getSelection() in jsdom.
*/ */
jsc.property('Copy with keyboard shortcut', jsc.property('Copy with keyboard shortcut', common.jscFormats(), 'nestring', async function (format, text) {
common.jscFormats(),
'nestring',
async function (format, text) {
var clean = jsdom();
common.enableClipboard();
$('body').html(
'<div id="placeholder">+++ no paste text ' +
'+++</div><div id="prettymessage" class="hidden">' +
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
'<svg id="copySuccessIcon"></svg></button><pre ' +
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
'</div><div id="plaintext" class="hidden"></div>'
);
$.PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run();
$.PrivateBin.CopyToClipboard.init();
$('body').trigger('copy');
const copiedTextWithoutSelectedText = await navigator.clipboard.readText();
clean();
return copiedTextWithoutSelectedText === text;
}
);
});
jsc.property('Copy link to clipboard',
'nestring',
async function (text) {
var clean = jsdom(); var clean = jsdom();
common.enableClipboard(); common.enableClipboard();
$('body').html('<button id="copyLink"></button>'); $('body').html(
'<div id="placeholder">+++ no paste text ' +
'+++</div><div id="prettymessage" class="hidden">' +
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
'<svg id="copySuccessIcon"></svg></button><pre ' +
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
'</div><div id="plaintext" class="hidden"></div>'
);
$.PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run();
$.PrivateBin.CopyToClipboard.init(); $.PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.setUrl(text);
$('#copyLink').trigger('click'); $('body').trigger('copy');
const copiedText = await navigator.clipboard.readText(); const copiedTextWithoutSelectedText = await navigator.clipboard.readText();
clean(); clean();
return text === copiedText; return copiedTextWithoutSelectedText === text;
} });
); });
jsc.property('Copy link to clipboard', 'nestring', async function (text) {
var clean = jsdom();
common.enableClipboard();
$('body').html('<button id="copyLink"></button>');
$.PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.setUrl(text);
$('#copyLink').trigger('click');
const copiedText = await navigator.clipboard.readText();
clean();
return text === copiedText;
});
describe('Keyboard shortcut hint', function () { describe('Keyboard shortcut hint', function () {
jsc.property('Show hint', jsc.property('Show hint', 'nestring', function (text) {
'nestring', var clean = jsdom();
function (text) {
var clean = jsdom(); $('body').html('<small id="copyShortcutHintText"></small>');
$.PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.showKeyboardShortcutHint();
const keyboardShortcutHint = $('#copyShortcutHintText').text();
clean();
return keyboardShortcutHint.length > 0;
});
$('body').html('<small id="copyShortcutHintText"></small>'); jsc.property('Hide hint', 'nestring', function (text) {
var clean = jsdom();
$.PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.showKeyboardShortcutHint(); $('body').html('<small id="copyShortcutHintText">' + text + '</small>');
const keyboardShortcutHint = $('#copyShortcutHintText').text(); $.PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.hideKeyboardShortcutHint();
clean();
const keyboardShortcutHint = $('#copyShortcutHintText').text();
return keyboardShortcutHint.length > 0;
} clean();
);
return keyboardShortcutHint.length === 0;
jsc.property('Hide hint', });
'nestring',
function (text) {
var clean = jsdom();
$('body').html('<small id="copyShortcutHintText">' + text + '</small>');
$.PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.hideKeyboardShortcutHint();
const keyboardShortcutHint = $('#copyShortcutHintText').text();
clean();
return keyboardShortcutHint.length === 0;
}
);
}); });
}); });

View file

@ -1,5 +1,5 @@
'use strict'; 'use strict';
const common = require('../common'); require('../common');
describe('CryptTool', function () { describe('CryptTool', function () {
describe('cipher & decipher', function () { describe('cipher & decipher', function () {
@ -15,26 +15,21 @@ describe('CryptTool', function () {
'string', 'string',
'string', 'string',
async function (key, password, message) { async function (key, password, message) {
const clean = jsdom(); // pause to let async functions conclude
await new Promise(resolve => setTimeout(resolve, 300));
let clean = jsdom();
// ensure zlib is getting loaded // ensure zlib is getting loaded
$.PrivateBin.Controller.initZ(); $.PrivateBin.Controller.initZ();
Object.defineProperty(window, 'crypto', { window.crypto = new WebCrypto();
value: new WebCrypto(),
writeable: false,
});
global.atob = common.atob;
global.btoa = common.btoa;
message = message.trim(); message = message.trim();
const cipherMessage = await $.PrivateBin.CryptTool.cipher( let cipherMessage = await $.PrivateBin.CryptTool.cipher(
key, password, message, [] key, password, message, []
), ),
plaintext = await $.PrivateBin.CryptTool.decipher( plaintext = await $.PrivateBin.CryptTool.decipher(
key, password, cipherMessage key, password, cipherMessage
); );
clean(); clean();
const result = (message === plaintext); return message === plaintext;
if (!result) console.log(plaintext, cipherMessage);
return result;
} }
), ),
{tests: 3}); {tests: 3});
@ -43,19 +38,15 @@ describe('CryptTool', function () {
// The below static unit tests are included to ensure deciphering of "classic" // The below static unit tests are included to ensure deciphering of "classic"
// SJCL based pastes still works // SJCL based pastes still works
it( it(
'supports PrivateBin v1 ciphertext with password (SJCL & browser atob)', 'supports PrivateBin v1 ciphertext (SJCL & browser atob)',
async function () { function () {
delete global.Base64; delete global.Base64;
const clean = jsdom(); let clean = jsdom();
Object.defineProperty(window, 'crypto', { window.crypto = new WebCrypto();
value: new WebCrypto(),
writeable: false,
});
global.atob = common.atob;
// Of course you can easily decipher the following texts, if you like. // Of course you can easily decipher the following texts, if you like.
// Bonus points for finding their sources and hidden meanings. // Bonus points for finding their sources and hidden meanings.
const paste = await $.PrivateBin.CryptTool.decipher( return $.PrivateBin.CryptTool.decipher(
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=', '6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
// -- "That's amazing. I've got the same combination on my luggage." // -- "That's amazing. I've got the same combination on my luggage."
Array.apply(0, Array(6)).map((_,b) => b + 1).join(''), Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
@ -86,76 +77,53 @@ describe('CryptTool', function () {
'QUxMXI5htsn2rf0HxCFu7Po8DNYLxTS+67hYjDIYWYaEIc8LXWMLyDm9' + 'QUxMXI5htsn2rf0HxCFu7Po8DNYLxTS+67hYjDIYWYaEIc8LXWMLyDm9' +
'C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5' + 'C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5' +
'imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}' 'imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}'
); ).then(function (paste1) {
clean(); $.PrivateBin.CryptTool.decipher(
const result = typeof paste === 'string' && paste.includes('securely packed in iron'); 's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
if (!result) console.log(paste); '', // no password
assert.ok(result); '{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
} ':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
); 'lt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzO' +
'lslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8U' +
it( 'yHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov' +
'supports PrivateBin v1 ciphertext no password (SJCL & browser atob)', '/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+' +
async function () { 'r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isP' +
delete global.Base64; 'YxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjb' +
const clean = jsdom(); 'U94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV' +
// ensure zlib is getting loaded '+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOL' +
$.PrivateBin.Controller.initZ(); 'dKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO2' +
Object.defineProperty(window, 'crypto', { '24WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3' +
value: new WebCrypto(), 'X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta' +
writeable: false, '8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC' +
'//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTY' +
'JW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluC' +
'OrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJO' +
'EJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHc' +
'OMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSY' +
'XhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr' +
'99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZ' +
'MZtmnYpGAtAPg7AUG"}'
).then(function (paste2) {
clean();
assert.ok(
paste1.includes('securely packed in iron') &&
paste2.includes('Sol is right')
);
});
}); });
global.atob = common.atob;
// Of course you can easily decipher the following texts, if you like.
// Bonus points for finding their sources and hidden meanings.
const paste = await $.PrivateBin.CryptTool.decipher(
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
'', // no password
'{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
'lt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzO' +
'lslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8U' +
'yHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov' +
'/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+' +
'r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isP' +
'YxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjb' +
'U94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV' +
'+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOL' +
'dKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO2' +
'24WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3' +
'X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta' +
'8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC' +
'//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTY' +
'JW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluC' +
'OrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJO' +
'EJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHc' +
'OMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSY' +
'XhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr' +
'99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZ' +
'MZtmnYpGAtAPg7AUG"}'
);
clean();
const result = typeof paste === 'string' && paste.includes('Sol is right');
if (!result) console.log(paste);
assert.ok(result);
} }
); );
it( it(
'supports ZeroBin ciphertext with password (SJCL & Base64 1.7)', 'supports ZeroBin ciphertext (SJCL & Base64 1.7)',
async function () { function () {
global.Base64 = require('../base64-1.7').Base64; global.Base64 = require('../base64-1.7').Base64;
const clean = jsdom(); var clean = jsdom();
Object.defineProperty(window, 'crypto', { window.crypto = new WebCrypto();
value: new WebCrypto(),
writeable: false,
});
global.atob = common.atob;
// Of course you can easily decipher the following texts, if you like. // Of course you can easily decipher the following texts, if you like.
// Bonus points for finding their sources and hidden meanings. // Bonus points for finding their sources and hidden meanings.
const paste = await $.PrivateBin.CryptTool.decipher( return $.PrivateBin.CryptTool.decipher(
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=', '6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
// -- "That's amazing. I've got the same combination on my luggage." // -- "That's amazing. I've got the same combination on my luggage."
Array.apply(0, Array(6)).map((_,b) => b + 1).join(''), Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
@ -178,74 +146,54 @@ describe('CryptTool', function () {
'7mNNo7xba/YT9KoPDaniqnYqb+q2pX1WNWE7dLS2wfroMAS3kh8P22DA' + '7mNNo7xba/YT9KoPDaniqnYqb+q2pX1WNWE7dLS2wfroMAS3kh8P22DA' +
'V37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAx' + 'V37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAx' +
'SxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}' 'SxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}'
); ).then(function (paste1) {
clean(); $.PrivateBin.CryptTool.decipher(
delete global.Base64; 's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
const result = typeof paste === 'string' && paste.includes('securely packed in iron'); '', // no password
if (!result) console.log(paste); '{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
assert.ok(result); ':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
} 'lt":"jN6CjbQMJCM=","ct":"PuOPWB3i2FPcreSrLYeQf84LdE8RHjs' +
); 'c+MGtiOr4b7doNyWKYtkNorbRadxaPnEee2/Utrp1MIIfY5juJSy8RGw' +
'EPX5ciWcYe6EzsXWznsnvhmpKNj9B7eIIrfSbxfy8E2e/g7xav1nive+' +
it( 'ljToka3WT1DZ8ILQd/NbnJeHWaoSEOfvz8+d8QJPb1tNZvs7zEY95Dum' +
'supports ZeroBin ciphertext no password (SJCL & Base64 1.7)', 'QwbyOsIMKAvcZHJ9OJNpujXzdMyt6DpcFcqlldWBZ/8q5rAUTw0HNx/r' +
async function () { 'CgbhAxRYfNoTLIcMM4L0cXbPSgCjwf5FuO3EdE13mgEDhcClW79m0Qvc' +
global.Base64 = require('../base64-1.7').Base64; 'nIh8xgzYoxLbp0+AwvC/MbZM8savN/0ieWr2EKkZ04ggiOIEyvfCUuNp' +
const clean = jsdom(); 'rQBYO+y8kKduNEN6by0Yf4LRCPfmwN+GezDLuzTnZIMhPbGqUAdgV6Ex' +
Object.defineProperty(window, 'crypto', { 'qK2ULEEIrQEMoOuQIxfoMhqLlzG79vXGt2O+BY+4IiYfvmuRLks4UXfy' +
value: new WebCrypto(), 'HqxPXTJg48IYbGs0j4TtJPUgp3523EyYLwEGyVTAuWhYAmVIwd/hoV7d' +
writeable: false, '7tmfcF73w9dufDFI3LNca2KxzBnWNPYvIZKBwWbq8ncxkb191dP6mjEi' +
'7NnhqVk5A6vIBbu4AC5PZf76l6yep4xsoy/QtdDxCMocCXeAML9MQ9uP' +
'QbuspOKrBvMfN5igA1kBqasnxI472KBNXsdZnaDddSVUuvhTcETM="}'
).then(function (paste2) {
clean();
delete global.Base64;
assert.ok(
paste1.includes('securely packed in iron') &&
paste2.includes('Sol is right')
);
});
}); });
global.atob = common.atob;
const paste = await $.PrivateBin.CryptTool.decipher(
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
'', // no password
'{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
'lt":"jN6CjbQMJCM=","ct":"PuOPWB3i2FPcreSrLYeQf84LdE8RHjs' +
'c+MGtiOr4b7doNyWKYtkNorbRadxaPnEee2/Utrp1MIIfY5juJSy8RGw' +
'EPX5ciWcYe6EzsXWznsnvhmpKNj9B7eIIrfSbxfy8E2e/g7xav1nive+' +
'ljToka3WT1DZ8ILQd/NbnJeHWaoSEOfvz8+d8QJPb1tNZvs7zEY95Dum' +
'QwbyOsIMKAvcZHJ9OJNpujXzdMyt6DpcFcqlldWBZ/8q5rAUTw0HNx/r' +
'CgbhAxRYfNoTLIcMM4L0cXbPSgCjwf5FuO3EdE13mgEDhcClW79m0Qvc' +
'nIh8xgzYoxLbp0+AwvC/MbZM8savN/0ieWr2EKkZ04ggiOIEyvfCUuNp' +
'rQBYO+y8kKduNEN6by0Yf4LRCPfmwN+GezDLuzTnZIMhPbGqUAdgV6Ex' +
'qK2ULEEIrQEMoOuQIxfoMhqLlzG79vXGt2O+BY+4IiYfvmuRLks4UXfy' +
'HqxPXTJg48IYbGs0j4TtJPUgp3523EyYLwEGyVTAuWhYAmVIwd/hoV7d' +
'7tmfcF73w9dufDFI3LNca2KxzBnWNPYvIZKBwWbq8ncxkb191dP6mjEi' +
'7NnhqVk5A6vIBbu4AC5PZf76l6yep4xsoy/QtdDxCMocCXeAML9MQ9uP' +
'QbuspOKrBvMfN5igA1kBqasnxI472KBNXsdZnaDddSVUuvhTcETM="}'
);
clean();
delete global.Base64;
const result = typeof paste === 'string' && paste.includes('Sol is right');
if (!result) console.log(paste);
assert.ok(result);
} }
); );
it('does not truncate messages', async function () { it('does not truncate messages', async function () {
const message = fs.readFileSync('test/compression-sample.txt', 'ascii').trim(), let message = fs.readFileSync('test/compression-sample.txt', 'utf8'),
clean = jsdom(); clean = jsdom();
Object.defineProperty(window, 'crypto', { window.crypto = new WebCrypto();
value: new WebCrypto(),
writeable: false,
});
// ensure zlib is getting loaded // ensure zlib is getting loaded
$.PrivateBin.Controller.initZ(); $.PrivateBin.Controller.initZ();
global.atob = common.atob; let cipherMessage = await $.PrivateBin.CryptTool.cipher(
global.btoa = common.btoa;
const cipherMessage = await $.PrivateBin.CryptTool.cipher(
'foo', 'bar', message, [] 'foo', 'bar', message, []
), ),
plaintext = await $.PrivateBin.CryptTool.decipher( plaintext = await $.PrivateBin.CryptTool.decipher(
'foo', 'bar', cipherMessage 'foo', 'bar', cipherMessage
); );
clean(); clean();
const result = (message === plaintext); assert.strictEqual(
if (!result) console.log(plaintext, cipherMessage); message,
assert.ok(result); plaintext
);
}); });
it('can en- and decrypt a particular message (#260)', function () { it('can en- and decrypt a particular message (#260)', function () {
@ -253,6 +201,8 @@ describe('CryptTool', function () {
'string', 'string',
'string', 'string',
async function (key, password) { async function (key, password) {
// pause to let async functions conclude
await new Promise(resolve => setTimeout(resolve, 300));
const message = ` const message = `
1 subgoal 1 subgoal
@ -275,23 +225,18 @@ isWhile : interp (while expr sBody) (MemElem mem) =
======================== ( 1 / 1 ) ======================== ( 1 / 1 )
conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem)) conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
`; `;
const clean = jsdom(); let clean = jsdom();
// ensure zlib is getting loaded // ensure zlib is getting loaded
$.PrivateBin.Controller.initZ(); $.PrivateBin.Controller.initZ();
Object.defineProperty(window, 'crypto', { window.crypto = new WebCrypto();
value: new WebCrypto(), let cipherMessage = await $.PrivateBin.CryptTool.cipher(
writeable: false,
});
const cipherMessage = await $.PrivateBin.CryptTool.cipher(
key, password, message, [] key, password, message, []
), ),
plaintext = await $.PrivateBin.CryptTool.decipher( plaintext = await $.PrivateBin.CryptTool.decipher(
key, password, cipherMessage key, password, cipherMessage
); );
clean(); clean();
const result = (message === plaintext); return message === plaintext;
if (!result) console.log(plaintext, cipherMessage);
return result;
} }
), ),
{tests: 3}); {tests: 3});
@ -299,27 +244,23 @@ conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
}); });
describe('getSymmetricKey', function () { describe('getSymmetricKey', function () {
this.timeout(10000); this.timeout(30000);
let keys = []; var keys = [];
// the parameter is used to ensure the test is run more then one time // the parameter is used to ensure the test is run more then one time
it('returns random, non-empty keys', function () { jsc.property(
jsc.assert(jsc.forall( 'returns random, non-empty keys',
'integer', 'integer',
function(counter) { function(counter) {
const clean = jsdom(); var clean = jsdom();
Object.defineProperty(window, 'crypto', { window.crypto = new WebCrypto();
value: new WebCrypto(), var key = $.PrivateBin.CryptTool.getSymmetricKey(),
writeable: false, result = (key !== '' && keys.indexOf(key) === -1);
}); keys.push(key);
const key = $.PrivateBin.CryptTool.getSymmetricKey(), clean();
result = (key !== '' && keys.indexOf(key) === -1); return result;
keys.push(key); }
clean(); );
return result;
}
),
{tests: 10});
});
}); });
}); });

View file

@ -229,30 +229,30 @@ describe('Helper', function () {
cleanup(); cleanup();
}); });
/* TODO test fails since jsDOM version 17 - document.cookie remains empty
jsc.property( jsc.property(
'returns the requested cookie', 'returns the requested cookie',
jsc.nearray(jsc.nearray(common.jscAlnumString())), jsc.nearray(jsc.nearray(common.jscAlnumString())),
jsc.nearray(jsc.nearray(common.jscAlnumString())), jsc.nearray(jsc.nearray(common.jscAlnumString())),
function (labels, values) { function (labels, values) {
let selectedKey = '', selectedValue = ''; var selectedKey = '', selectedValue = '',
const clean = jsdom(); cookieArray = [];
labels.forEach(function(item, i) { labels.forEach(function(item, i) {
const key = item.join(''), var key = item.join(''),
value = (values[i] || values[0]).join(''); value = (values[i] || values[0]).join('');
document.cookie = key + '=' + value; cookieArray.push(key + '=' + value);
if (Math.random() < 1 / i || selectedKey === key) if (Math.random() < 1 / i || selectedKey === key)
{ {
selectedKey = key; selectedKey = key;
selectedValue = value; selectedValue = value;
} }
}); });
const result = $.PrivateBin.Helper.getCookie(selectedKey); var clean = jsdom('', {cookie: cookieArray}),
result = $.PrivateBin.Helper.getCookie(selectedKey);
$.PrivateBin.Helper.reset(); $.PrivateBin.Helper.reset();
clean(); clean();
return result === selectedValue; return result === selectedValue;
} }
); */ );
}); });
describe('baseUri', function () { describe('baseUri', function () {
@ -290,44 +290,5 @@ 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';
});
});
describe('isBootstrap5', function () {
jsc.property('Bootstrap 5 has been detected', function () {
global.bootstrap = {};
return $.PrivateBin.Helper.isBootstrap5() === true;
});
jsc.property('Bootstrap 5 has not been detected', function () {
delete global.bootstrap;
return $.PrivateBin.Helper.isBootstrap5() === false;
});
});
}); });

View file

@ -94,6 +94,7 @@ describe('Model', function () {
url.query = queryStart.concat(pasteId, queryEnd); url.query = queryStart.concat(pasteId, queryEnd);
const pasteIdString = pasteId.join(''), const pasteIdString = pasteId.join(''),
clean = jsdom('', {url: common.urlToString(url)}); clean = jsdom('', {url: common.urlToString(url)});
global.URL = require('jsdom-url').URL;
const result = $.PrivateBin.Model.getPasteId(); const result = $.PrivateBin.Model.getPasteId();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
clean(); clean();
@ -106,6 +107,7 @@ describe('Model', function () {
function (url) { function (url) {
let clean = jsdom('', {url: common.urlToString(url)}), let clean = jsdom('', {url: common.urlToString(url)}),
result = false; result = false;
global.URL = require('jsdom-url').URL;
try { try {
$.PrivateBin.Model.getPasteId(); $.PrivateBin.Model.getPasteId();
} }
@ -129,7 +131,7 @@ describe('Model', function () {
'returns the fragment of a v1 URL', 'returns the fragment of a v1 URL',
common.jscUrl(), common.jscUrl(),
function (url) { function (url) {
url.fragment = '0OIl'; // any non-base58 string url.fragment = common.btoa(url.fragment.padStart(32, '\u0000'));
const clean = jsdom('', {url: common.urlToString(url)}), const clean = jsdom('', {url: common.urlToString(url)}),
result = $.PrivateBin.Model.getPasteKey(); result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
@ -138,17 +140,17 @@ describe('Model', function () {
} }
); );
jsc.property( jsc.property(
'returns the fragment stripped of trailing query parts', 'returns the v1 fragment stripped of trailing query parts',
common.jscUrl(), common.jscUrl(),
jsc.array(common.jscHashString()), jsc.array(common.jscHashString()),
function (url, trail) { function (url, trail) {
const fragment = url.fragment.padStart(32, '\u0000'); const fragmentString = common.btoa(url.fragment.padStart(32, '\u0000'));
url.fragment = $.PrivateBin.CryptTool.base58encode(fragment) + '&' + trail.join(''); url.fragment = fragmentString + '&' + trail.join('');
const clean = jsdom('', {url: common.urlToString(url)}), const clean = jsdom('', {url: common.urlToString(url)}),
result = $.PrivateBin.Model.getPasteKey(); result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
clean(); clean();
return fragment === result; return fragmentString === result;
} }
); );
jsc.property( jsc.property(

View file

@ -2,6 +2,8 @@
require('../common'); require('../common');
describe('Prompt', function () { describe('Prompt', function () {
// TODO: this does not test the prompt() fallback, since that isn't available
// in nodejs -> replace the prompt in the "page" template with a modal
describe('requestPassword & getPassword', function () { describe('requestPassword & getPassword', function () {
this.timeout(30000); this.timeout(30000);
@ -9,7 +11,7 @@ describe('Prompt', function () {
'returns the password fed into the dialog', 'returns the password fed into the dialog',
'string', 'string',
function (password) { function (password) {
password = password.replace(/\r+|\n+/g, ''); password = password.replace(/\r+/g, '');
var clean = jsdom('', {url: 'ftp://example.com/?0000000000000000'}); var clean = jsdom('', {url: 'ftp://example.com/?0000000000000000'});
$('body').html( $('body').html(
'<div id="passwordmodal" class="modal fade" role="dialog">' + '<div id="passwordmodal" class="modal fade" role="dialog">' +
@ -30,8 +32,7 @@ describe('Prompt', function () {
//var result = $.PrivateBin.Prompt.getPassword(); //var result = $.PrivateBin.Prompt.getPassword();
var result = $('#passworddecrypt').val(); var result = $('#passworddecrypt').val();
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
// TODO triggers error messages in jsDOM since version 11 clean();
//clean();
return result === password; return result === password;
} }
); );

View file

@ -621,7 +621,7 @@ describe('TopNav', function () {
'returns the contents of the password input', 'returns the contents of the password input',
'string', 'string',
function (password) { function (password) {
password = password.replace(/\r+|\n+/g, ''); password = password.replace(/\r+/g, '');
var results = []; var results = [];
$('body').html( $('body').html(
'<nav><div id="navbar"><ul><li><div id="password" ' + '<nav><div id="navbar"><ul><li><div id="password" ' +
@ -727,11 +727,11 @@ describe('TopNav', function () {
cleanup(); cleanup();
}); });
// TODO triggers error messages in jsDOM since version 12, but passes
it( it(
'displays raw text view correctly', 'displays raw text view correctly',
function () { function () {
const clean = jsdom('', {url: 'https://privatebin.net/?0123456789abcdef#0'}); const clean = jsdom('', {url: 'https://privatebin.net/?0123456789abcdef#0'});
global.URL = require('jsdom-url').URL;
$('body').html('<button id="rawtextbutton"></button>'); $('body').html('<button id="rawtextbutton"></button>');
const sample = 'example'; const sample = 'example';
$.PrivateBin.PasteViewer.setText(sample); $.PrivateBin.PasteViewer.setText(sample);

View file

@ -18,6 +18,11 @@ describe('UiHelper', function () {
const expected = common.urlToString(url), const expected = common.urlToString(url),
clean = jsdom('', {url: expected}); clean = jsdom('', {url: expected});
// make window.location.href writable
Object.defineProperty(window.location, 'href', {
writable: true,
value: window.location.href
});
$.PrivateBin.UiHelper.mockHistoryChange(); $.PrivateBin.UiHelper.mockHistoryChange();
$.PrivateBin.Helper.reset(); $.PrivateBin.Helper.reset();
var result = window.location.href; var result = window.location.href;
@ -35,6 +40,11 @@ describe('UiHelper', function () {
const expected = common.urlToString(url), const expected = common.urlToString(url),
clean = jsdom('', {url: expected}); clean = jsdom('', {url: expected});
// make window.location.href writable
Object.defineProperty(window.location, 'href', {
writable: true,
value: window.location.href
});
$.PrivateBin.UiHelper.mockHistoryChange([ $.PrivateBin.UiHelper.mockHistoryChange([
{type: 'newpaste'}, '', expected {type: 'newpaste'}, '', expected
]); ]);
@ -47,8 +57,6 @@ describe('UiHelper', function () {
}); });
describe('reloadHome', function () { describe('reloadHome', function () {
// TODO triggers error messages in jsDOM since version 11
/*
this.timeout(30000); this.timeout(30000);
before(function () { before(function () {
$.PrivateBin.Helper.reset(); $.PrivateBin.Helper.reset();
@ -63,6 +71,11 @@ describe('UiHelper', function () {
delete(url.fragment); delete(url.fragment);
const expected = common.urlToString(url); const expected = common.urlToString(url);
// make window.location.href writable
Object.defineProperty(window.location, 'href', {
writable: true,
value: window.location.href
});
$.PrivateBin.UiHelper.reloadHome(); $.PrivateBin.UiHelper.reloadHome();
$.PrivateBin.Helper.reset(); $.PrivateBin.Helper.reset();
var result = window.location.href; var result = window.location.href;
@ -70,7 +83,6 @@ describe('UiHelper', function () {
return expected === result; return expected === result;
} }
); );
*/
}); });
describe('isVisible', function () { describe('isVisible', function () {

View file

@ -45,17 +45,7 @@ class Configuration
'defaultformatter' => 'plaintext', 'defaultformatter' => 'plaintext',
'syntaxhighlightingtheme' => '', 'syntaxhighlightingtheme' => '',
'sizelimit' => 10485760, 'sizelimit' => 10485760,
'templateselection' => false,
'template' => 'bootstrap', 'template' => 'bootstrap',
'availabletemplates' => array(
'bootstrap5',
'bootstrap',
'bootstrap-page',
'bootstrap-dark',
'bootstrap-dark-page',
'bootstrap-compact',
'bootstrap-compact-page',
),
'info' => 'More information on the <a href=\'https://privatebin.info/\'>project page</a>.', 'info' => 'More information on the <a href=\'https://privatebin.info/\'>project page</a>.',
'notice' => '', 'notice' => '',
'languageselection' => false, 'languageselection' => false,
@ -64,7 +54,7 @@ class Configuration
'qrcode' => true, 'qrcode' => true,
'email' => true, 'email' => true,
'icon' => 'identicon', 'icon' => 'identicon',
'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'wasm-unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; frame-src blob:; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-modals allow-downloads', 'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'wasm-unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
'zerobincompatibility' => false, 'zerobincompatibility' => false,
'httpwarning' => true, 'httpwarning' => true,
'compression' => 'zlib', 'compression' => 'zlib',
@ -118,11 +108,11 @@ class Configuration
'js/kjua-0.9.0.js' => 'sha512-CVn7af+vTMBd9RjoS4QM5fpLFEOtBCoB0zPtaqIDC7sF4F8qgUSRFQQpIyEDGsr6yrjbuOLzdf20tkHHmpaqwQ==', 'js/kjua-0.9.0.js' => 'sha512-CVn7af+vTMBd9RjoS4QM5fpLFEOtBCoB0zPtaqIDC7sF4F8qgUSRFQQpIyEDGsr6yrjbuOLzdf20tkHHmpaqwQ==',
'js/legacy.js' => 'sha512-UxW/TOZKon83n6dk/09GsYKIyeO5LeBHokxyIq+r7KFS5KMBeIB/EM7NrkVYIezwZBaovnyNtY2d9tKFicRlXg==', 'js/legacy.js' => 'sha512-UxW/TOZKon83n6dk/09GsYKIyeO5LeBHokxyIq+r7KFS5KMBeIB/EM7NrkVYIezwZBaovnyNtY2d9tKFicRlXg==',
'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==', 'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==',
'js/privatebin.js' => 'sha512-9X1Ns+w/WPlWde118Mbl1UFDPsFmBcXK2zr6ny7oDOuZMVP1hFCPzXjKbC+59xDQW0Q+NmyT4NC6/5B6UzkkDw==', 'js/privatebin.js' => 'sha512-POa+8KNXFFwJFsqp7r9APmR5Rc1w2l363y+OScSzLCySrHN7UhOOgt1VH/o8mVddFvvUozj3FZVmdkTxRlrS5g==',
'js/purify-3.2.6.js' => 'sha512-zqwL4OoBLFx89QPewkz4Lz5CSA2ktU+f31fuECkF0iK3Id5qd3Zpq5dMby8KwHjIEpsUgOqwF58cnmcaNem0EA==', 'js/purify-3.2.4.js' => 'sha512-Mu9BqoHURMeycg6AgqTpokUv9guq88pajfaFqz53fx1OxohyROkydXPLEIbdKCQ7EdDs9hgcrYeZ9zTiPQQ4CA==',
'js/rawinflate-0.3.js' => 'sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==', 'js/rawinflate-0.3.js' => 'sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==',
'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==', 'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==',
'js/zlib-1.3.1-1.js' => 'sha512-5bU9IIP4PgBrOKLZvGWJD4kgfQrkTz8Z3Iqeu058mbQzW3mCumOU6M3UVbVZU9rrVoVwaW4cZK8U8h5xjF88eQ==', 'js/zlib-1.3.1.js' => 'sha512-5bU9IIP4PgBrOKLZvGWJD4kgfQrkTz8Z3Iqeu058mbQzW3mCumOU6M3UVbVZU9rrVoVwaW4cZK8U8h5xjF88eQ==',
), ),
); );

View file

@ -27,7 +27,7 @@ class Controller
* *
* @const string * @const string
*/ */
const VERSION = '1.7.8'; const VERSION = '1.7.6';
/** /**
* minimal required PHP version * minimal required PHP version
@ -112,12 +112,10 @@ class Controller
* *
* initializes and runs PrivateBin * initializes and runs PrivateBin
* *
* @param ?Configuration $config
*
* @access public * @access public
* @throws Exception * @throws Exception
*/ */
public function __construct(?Configuration $config = null) public function __construct()
{ {
if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION) < 0) { if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION) < 0) {
error_log(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION)); error_log(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION));
@ -128,8 +126,7 @@ class Controller
return; return;
} }
// load config (using ini file by default) & initialize required classes // load config from ini file, initialize required classes
$this->_conf = $config ?? new Configuration();
$this->_init(); $this->_init();
switch ($this->_request->getOperation()) { switch ($this->_request->getOperation()) {
@ -177,21 +174,12 @@ class Controller
*/ */
private function _init() private function _init()
{ {
$this->_conf = new Configuration;
$this->_model = new Model($this->_conf); $this->_model = new Model($this->_conf);
$this->_request = new Request; $this->_request = new Request;
$this->_urlBase = $this->_request->getRequestUri(); $this->_urlBase = $this->_request->getRequestUri();
$this->_setDefaultLanguage(); // set default language
$this->_setDefaultTemplate();
}
/**
* Set default language
*
* @access private
*/
private function _setDefaultLanguage()
{
$lang = $this->_conf->getKey('languagedefault'); $lang = $this->_conf->getKey('languagedefault');
I18n::setLanguageFallback($lang); I18n::setLanguageFallback($lang);
// force default language, if language selection is disabled and a default is set // force default language, if language selection is disabled and a default is set
@ -201,25 +189,6 @@ class Controller
} }
} }
/**
* Set default template
*
* @access private
*/
private function _setDefaultTemplate()
{
$templates = $this->_conf->getKey('availabletemplates');
$template = $this->_conf->getKey('template');
TemplateSwitcher::setAvailableTemplates($templates);
TemplateSwitcher::setTemplateFallback($template);
// force default template, if template selection is disabled and a default is set
if (!$this->_conf->getKey('templateselection') && !empty($template)) {
$_COOKIE['template'] = $template;
setcookie('template', $template, array('SameSite' => 'Lax', 'Secure' => true));
}
}
/** /**
* Turn off browser caching * Turn off browser caching
* *
@ -431,18 +400,11 @@ class Controller
setcookie('lang', $languageselection, array('SameSite' => 'Lax', 'Secure' => true)); setcookie('lang', $languageselection, array('SameSite' => 'Lax', 'Secure' => true));
} }
// set template cookie if that functionality was enabled
$templateselection = '';
if ($this->_conf->getKey('templateselection')) {
$templateselection = TemplateSwitcher::getTemplate();
setcookie('template', $templateselection, array('SameSite' => 'Lax', 'Secure' => true));
}
// strip policies that are unsupported in meta tag // strip policies that are unsupported in meta tag
$metacspheader = str_replace( $metacspheader = str_replace(
array( array(
'frame-ancestors \'none\'; ', 'frame-ancestors \'none\'; ',
'; sandbox allow-same-origin allow-scripts allow-forms allow-modals allow-downloads', '; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
), ),
'', '',
$this->_conf->getKey('cspheader') $this->_conf->getKey('cspheader')
@ -476,8 +438,6 @@ class Controller
$page->assign('ZEROBINCOMPATIBILITY', $this->_conf->getKey('zerobincompatibility')); $page->assign('ZEROBINCOMPATIBILITY', $this->_conf->getKey('zerobincompatibility'));
$page->assign('LANGUAGESELECTION', $languageselection); $page->assign('LANGUAGESELECTION', $languageselection);
$page->assign('LANGUAGES', I18n::getLanguageLabels(I18n::getAvailableLanguages())); $page->assign('LANGUAGES', I18n::getLanguageLabels(I18n::getAvailableLanguages()));
$page->assign('TEMPLATESELECTION', $templateselection);
$page->assign('TEMPLATES', TemplateSwitcher::getAvailableTemplates());
$page->assign('EXPIRE', $expire); $page->assign('EXPIRE', $expire);
$page->assign('EXPIREDEFAULT', $this->_conf->getKey('default', 'expire')); $page->assign('EXPIREDEFAULT', $this->_conf->getKey('default', 'expire'));
$page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener')); $page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener'));
@ -487,7 +447,7 @@ class Controller
$page->assign('HTTPSLINK', 'https://' . $this->_request->getHost() . $this->_request->getRequestUri()); $page->assign('HTTPSLINK', 'https://' . $this->_request->getHost() . $this->_request->getRequestUri());
$page->assign('COMPRESSION', $this->_conf->getKey('compression')); $page->assign('COMPRESSION', $this->_conf->getKey('compression'));
$page->assign('SRI', $this->_conf->getSection('sri')); $page->assign('SRI', $this->_conf->getSection('sri'));
$page->draw(TemplateSwitcher::getTemplate()); $page->draw($this->_conf->getKey('template'));
} }
/** /**

View file

@ -34,7 +34,7 @@ abstract class AbstractData
* @param array $paste * @param array $paste
* @return bool * @return bool
*/ */
abstract public function create($pasteid, array &$paste); abstract public function create($pasteid, array $paste);
/** /**
* Read a paste. * Read a paste.
@ -72,7 +72,7 @@ abstract class AbstractData
* @param array $comment * @param array $comment
* @return bool * @return bool
*/ */
abstract public function createComment($pasteid, $parentid, $commentid, array &$comment); abstract public function createComment($pasteid, $parentid, $commentid, array $comment);
/** /**
* Read all comments of paste. * Read all comments of paste.
@ -199,7 +199,7 @@ abstract class AbstractData
* @param array $paste * @param array $paste
* @return array * @return array
*/ */
protected static function upgradePreV1Format(array &$paste) protected static function upgradePreV1Format(array $paste)
{ {
if (array_key_exists('attachment', $paste['meta'])) { if (array_key_exists('attachment', $paste['meta'])) {
$paste['attachment'] = $paste['meta']['attachment']; $paste['attachment'] = $paste['meta']['attachment'];

View file

@ -140,7 +140,7 @@ class Database extends AbstractData
* @param array $paste * @param array $paste
* @return bool * @return bool
*/ */
public function create($pasteid, array &$paste) public function create($pasteid, array $paste)
{ {
$expire_date = 0; $expire_date = 0;
$opendiscussion = $burnafterreading = false; $opendiscussion = $burnafterreading = false;
@ -297,18 +297,14 @@ class Database extends AbstractData
* @param array $comment * @param array $comment
* @return bool * @return bool
*/ */
public function createComment($pasteid, $parentid, $commentid, array &$comment) public function createComment($pasteid, $parentid, $commentid, array $comment)
{ {
if (array_key_exists('data', $comment)) { if (array_key_exists('data', $comment)) {
$version = 1; $version = 1;
$data = $comment['data']; $data = $comment['data'];
} else { } else {
try { $version = 2;
$version = 2; $data = Json::encode($comment);
$data = Json::encode($comment);
} catch (Exception $e) {
return false;
}
} }
list($createdKey, $iconKey) = $this->_getVersionedKeys($version); list($createdKey, $iconKey) = $this->_getVersionedKeys($version);
$meta = $comment['meta']; $meta = $comment['meta'];

View file

@ -85,7 +85,7 @@ class Filesystem extends AbstractData
* @param array $paste * @param array $paste
* @return bool * @return bool
*/ */
public function create($pasteid, array &$paste) public function create($pasteid, array $paste)
{ {
$storagedir = $this->_dataid2path($pasteid); $storagedir = $this->_dataid2path($pasteid);
$file = $storagedir . $pasteid . '.php'; $file = $storagedir . $pasteid . '.php';
@ -188,7 +188,7 @@ class Filesystem extends AbstractData
* @param array $comment * @param array $comment
* @return bool * @return bool
*/ */
public function createComment($pasteid, $parentid, $commentid, array &$comment) public function createComment($pasteid, $parentid, $commentid, array $comment)
{ {
$storagedir = $this->_dataid2discussionpath($pasteid); $storagedir = $this->_dataid2discussionpath($pasteid);
$file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid . '.php'; $file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid . '.php';
@ -276,7 +276,7 @@ class Filesystem extends AbstractData
case 'purge_limiter': case 'purge_limiter':
return $this->_storeString( return $this->_storeString(
$this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php', $this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php',
'<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . var_export($value, true) . ';' '<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $value . ';'
); );
case 'salt': case 'salt':
return $this->_storeString( return $this->_storeString(
@ -308,9 +308,7 @@ class Filesystem extends AbstractData
$file = $this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php'; $file = $this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php';
if (is_readable($file)) { if (is_readable($file)) {
require $file; require $file;
if (array_key_exists('purge_limiter', $GLOBALS)) { return $GLOBALS['purge_limiter'];
return $GLOBALS['purge_limiter'];
}
} }
break; break;
case 'salt': case 'salt':
@ -326,11 +324,9 @@ class Filesystem extends AbstractData
$file = $this->_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php'; $file = $this->_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php';
if (is_readable($file)) { if (is_readable($file)) {
require $file; require $file;
if (array_key_exists('traffic_limiter', $GLOBALS)) { $this->_last_cache = $GLOBALS['traffic_limiter'];
$this->_last_cache = $GLOBALS['traffic_limiter']; if (array_key_exists($key, $this->_last_cache)) {
if (array_key_exists($key, $this->_last_cache)) { return $this->_last_cache[$key];
return $this->_last_cache[$key];
}
} }
} }
break; break;
@ -347,11 +343,12 @@ class Filesystem extends AbstractData
*/ */
private function _get($filename) private function _get($filename)
{ {
$data = substr( return Json::decode(
file_get_contents($filename), substr(
strlen(self::PROTECTION_LINE . PHP_EOL) file_get_contents($filename),
strlen(self::PROTECTION_LINE . PHP_EOL)
)
); );
return Json::decode($data);
} }
/** /**

View file

@ -105,7 +105,7 @@ class GoogleCloudStorage extends AbstractData
* @param $payload array to store * @param $payload array to store
* @return bool true if successful, otherwise false. * @return bool true if successful, otherwise false.
*/ */
private function _upload($key, &$payload) private function _upload($key, $payload)
{ {
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array(); $metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']); unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
@ -136,7 +136,7 @@ class GoogleCloudStorage extends AbstractData
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function create($pasteid, array &$paste) public function create($pasteid, array $paste)
{ {
if ($this->exists($pasteid)) { if ($this->exists($pasteid)) {
return false; return false;
@ -201,7 +201,7 @@ class GoogleCloudStorage extends AbstractData
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function createComment($pasteid, $parentid, $commentid, array &$comment) public function createComment($pasteid, $parentid, $commentid, array $comment)
{ {
if ($this->existsComment($pasteid, $parentid, $commentid)) { if ($this->existsComment($pasteid, $parentid, $commentid)) {
return false; return false;
@ -219,8 +219,7 @@ class GoogleCloudStorage extends AbstractData
$prefix = $this->_getKey($pasteid) . '/discussion/'; $prefix = $this->_getKey($pasteid) . '/discussion/';
try { try {
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) { foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) {
$data = $this->_bucket->object($key->name())->downloadAsString(); $comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString());
$comment = Json::decode($data);
$comment['id'] = basename($key->name()); $comment['id'] = basename($key->name());
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']); $slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
$comments[$slot] = $comment; $comments[$slot] = $comment;

View file

@ -165,7 +165,7 @@ class S3Storage extends AbstractData
* @param $payload array to store * @param $payload array to store
* @return bool true if successful, otherwise false. * @return bool true if successful, otherwise false.
*/ */
private function _upload($key, &$payload) private function _upload($key, $payload)
{ {
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array(); $metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']); unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
@ -191,7 +191,7 @@ class S3Storage extends AbstractData
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function create($pasteid, array &$paste) public function create($pasteid, array $paste)
{ {
if ($this->exists($pasteid)) { if ($this->exists($pasteid)) {
return false; return false;
@ -263,7 +263,7 @@ class S3Storage extends AbstractData
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function createComment($pasteid, $parentid, $commentid, array &$comment) public function createComment($pasteid, $parentid, $commentid, array $comment)
{ {
if ($this->existsComment($pasteid, $parentid, $commentid)) { if ($this->existsComment($pasteid, $parentid, $commentid)) {
return false; return false;

View file

@ -29,7 +29,7 @@ class FormatV2
* @param bool $isComment * @param bool $isComment
* @return bool * @return bool
*/ */
public static function isValid(&$message, $isComment = false) public static function isValid($message, $isComment = false)
{ {
$required_keys = array('adata', 'v', 'ct'); $required_keys = array('adata', 'v', 'ct');
if ($isComment) { if ($isComment) {

View file

@ -183,12 +183,9 @@ class I18n
// load translations // load translations
self::$_language = $match; self::$_language = $match;
if ($match == 'en') { self::$_translations = ($match == 'en') ? array() : Json::decode(
self::$_translations = array(); file_get_contents(self::_getPath($match . '.json'))
} else { );
$data = file_get_contents(self::_getPath($match . '.json'));
self::$_translations = Json::decode($data);
}
} }
/** /**
@ -276,8 +273,7 @@ class I18n
{ {
$file = self::_getPath('languages.json'); $file = self::_getPath('languages.json');
if (count(self::$_languageLabels) == 0 && is_readable($file)) { if (count(self::$_languageLabels) == 0 && is_readable($file)) {
$data = file_get_contents($file); self::$_languageLabels = Json::decode(file_get_contents($file));
self::$_languageLabels = Json::decode($data);
} }
if (count($languages) == 0) { if (count($languages) == 0) {
return self::$_languageLabels; return self::$_languageLabels;

View file

@ -29,7 +29,7 @@ class Json
* @throws Exception * @throws Exception
* @return string * @return string
*/ */
public static function encode(&$input) public static function encode($input)
{ {
$jsonString = json_encode($input); $jsonString = json_encode($input);
self::_detectError(); self::_detectError();
@ -45,7 +45,7 @@ class Json
* @throws Exception * @throws Exception
* @return mixed * @return mixed
*/ */
public static function decode(&$input) public static function decode($input)
{ {
$output = json_decode($input, true); $output = json_decode($input, true);
self::_detectError(); self::_detectError();

View file

@ -100,9 +100,9 @@ abstract class AbstractModel
* @param array $data * @param array $data
* @throws Exception * @throws Exception
*/ */
public function setData(array &$data) public function setData(array $data)
{ {
$this->_sanitize($data); $data = $this->_sanitize($data);
$this->_validate($data); $this->_validate($data);
$this->_data = $data; $this->_data = $data;
@ -155,7 +155,7 @@ abstract class AbstractModel
*/ */
public static function isValidId($id) public static function isValidId($id)
{ {
return (bool) preg_match('#\A[a-f0-9]{16}\z#', (string) $id); return (bool) preg_match('#\A[a-f\d]{16}\z#', (string) $id);
} }
/** /**
@ -163,8 +163,9 @@ abstract class AbstractModel
* *
* @access protected * @access protected
* @param array $data * @param array $data
* @return array
*/ */
abstract protected function _sanitize(array &$data); abstract protected function _sanitize(array $data);
/** /**
* Validate data. * Validate data.
@ -173,7 +174,7 @@ abstract class AbstractModel
* @param array $data * @param array $data
* @throws Exception * @throws Exception
*/ */
protected function _validate(array &$data) protected function _validate(array $data)
{ {
} }
} }

View file

@ -104,7 +104,7 @@ class Comment extends AbstractModel
* @param Paste $paste * @param Paste $paste
* @throws Exception * @throws Exception
*/ */
public function setPaste(Paste &$paste) public function setPaste(Paste $paste)
{ {
$this->_paste = $paste; $this->_paste = $paste;
$this->_data['pasteid'] = $paste->getId(); $this->_data['pasteid'] = $paste->getId();
@ -155,8 +155,9 @@ class Comment extends AbstractModel
* *
* @access protected * @access protected
* @param array $data * @param array $data
* @return array
*/ */
protected function _sanitize(array &$data) protected function _sanitize(array $data)
{ {
// we generate an icon based on a SHA512 HMAC of the users IP, if configured // we generate an icon based on a SHA512 HMAC of the users IP, if configured
$icon = $this->_conf->getKey('icon'); $icon = $this->_conf->getKey('icon');
@ -189,5 +190,6 @@ class Comment extends AbstractModel
$data['meta']['icon'] = $pngdata; $data['meta']['icon'] = $pngdata;
} }
} }
return $data;
} }
} }

View file

@ -219,10 +219,11 @@ class Paste extends AbstractModel
* *
* @access protected * @access protected
* @param array $data * @param array $data
* @return array
*/ */
protected function _sanitize(array &$data) protected function _sanitize(array $data)
{ {
$expiration = $data['meta']['expire'] ?? 0; $expiration = $data['meta']['expire'];
unset($data['meta']['expire']); unset($data['meta']['expire']);
$expire_options = $this->_conf->getSection('expire_options'); $expire_options = $this->_conf->getSection('expire_options');
if (array_key_exists($expiration, $expire_options)) { if (array_key_exists($expiration, $expire_options)) {
@ -234,6 +235,7 @@ class Paste extends AbstractModel
if ($expire > 0) { if ($expire > 0) {
$data['meta']['expire_date'] = time() + $expire; $data['meta']['expire_date'] = time() + $expire;
} }
return $data;
} }
/** /**
@ -243,7 +245,7 @@ class Paste extends AbstractModel
* @param array $data * @param array $data
* @throws Exception * @throws Exception
*/ */
protected function _validate(array &$data) protected function _validate(array $data)
{ {
// reject invalid or disabled formatters // reject invalid or disabled formatters
if (!array_key_exists($data['adata'][1], $this->_conf->getSection('formatter_options'))) { if (!array_key_exists($data['adata'][1], $this->_conf->getSection('formatter_options'))) {

Some files were not shown because too many files have changed in this diff Show more