From 91f78ecd0f3ff7a2ae225a835b3c87b8a7224fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Tue, 5 May 2020 14:16:22 -0700 Subject: [PATCH 01/68] added "whitelist" under [traffic] --- cfg/conf.sample.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index d2d285de..e2d7fec8 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -127,6 +127,10 @@ markdown = "Markdown" ; Set this to 0 to disable rate limiting. limit = 10 +; (optional) if you only want some source IP addresses to create pastes +; enter their IPv4 address(es) here, separated by commas +; whitelist = "12.34.56.78,99.88.77.66" + ; (optional) if your website runs behind a reverse proxy or load balancer, ; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR ; header = "X_FORWARDED_FOR" From 5644001c5377b5a0791e136cb67436a77490db6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Tue, 5 May 2020 14:17:15 -0700 Subject: [PATCH 02/68] added "whitelist" under [traffic] --- lib/Configuration.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Configuration.php b/lib/Configuration.php index 06edf68b..aa6d15d0 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -78,6 +78,7 @@ class Configuration ), 'traffic' => array( 'limit' => 10, + 'whitelist' => null, 'header' => null, 'dir' => 'data', ), From 9327c9b58bc70709ce27ca30d29f63e4d801bd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Tue, 5 May 2020 14:18:52 -0700 Subject: [PATCH 03/68] added whitelist check --- lib/Controller.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/Controller.php b/lib/Controller.php index 21a27b27..5db14c22 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -196,6 +196,19 @@ class Controller */ private function _create() { + // Check whitelist if allowed to create + $whitelist = explode(',', $this->_conf->getKey('whitelist', 'traffic')); + if (($option = $this->_conf->getKey('header', 'traffic')) !== null) { + $httpHeader = 'HTTP_' . $option; + if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { + $remoteip = $_SERVER[$httpHeader]; + } + } + if( !in_array($remoteip, $whitelist) ) { + $this->_return_message(1, I18n::_('Your IP is not authorized')); + return; + } + // Ensure last paste from visitors IP address was more than configured amount of seconds ago. TrafficLimiter::setConfiguration($this->_conf); if (!TrafficLimiter::canPass()) { From 9ca041fa068e9ecb30a8924b91778ffc9f9c396a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Thu, 7 May 2020 15:53:56 -0700 Subject: [PATCH 04/68] Update lib/Controller.php Co-authored-by: rugk --- lib/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Controller.php b/lib/Controller.php index 5db14c22..c202f391 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -205,7 +205,7 @@ class Controller } } if( !in_array($remoteip, $whitelist) ) { - $this->_return_message(1, I18n::_('Your IP is not authorized')); + $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.')); return; } From ef9780707a941780bf13caa29e9ef28858f4b56c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Thu, 7 May 2020 15:54:13 -0700 Subject: [PATCH 05/68] Update lib/Controller.php Co-authored-by: rugk --- lib/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Controller.php b/lib/Controller.php index c202f391..4f3cbdf9 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -204,7 +204,7 @@ class Controller $remoteip = $_SERVER[$httpHeader]; } } - if( !in_array($remoteip, $whitelist) ) { + if(!in_array($remoteip, $whitelist)) { $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.')); return; } From cea96ee12a1e3e91c72f6efe6cac636cca6a80a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Thu, 7 May 2020 15:55:09 -0700 Subject: [PATCH 06/68] Update cfg/conf.sample.php Co-authored-by: rugk --- cfg/conf.sample.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index e2d7fec8..52f152e5 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -129,7 +129,7 @@ limit = 10 ; (optional) if you only want some source IP addresses to create pastes ; enter their IPv4 address(es) here, separated by commas -; whitelist = "12.34.56.78,99.88.77.66" +; whitelist_paste_creation = "12.34.56.78,99.88.77.66" ; (optional) if your website runs behind a reverse proxy or load balancer, ; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR From 819d25a74cfdde26be18d592e827ab24e49e2b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Thu, 7 May 2020 16:13:25 -0700 Subject: [PATCH 07/68] change to whitelist_paste_creation --- lib/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Controller.php b/lib/Controller.php index 4f3cbdf9..45b6dacd 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -197,7 +197,7 @@ class Controller private function _create() { // Check whitelist if allowed to create - $whitelist = explode(',', $this->_conf->getKey('whitelist', 'traffic')); + $whitelist = explode(',', $this->_conf->getKey('whitelist_paste_creation', 'traffic')); if (($option = $this->_conf->getKey('header', 'traffic')) !== null) { $httpHeader = 'HTTP_' . $option; if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { From c152f85b50cf38c83562bd9e7dfde543cce0d49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Thu, 7 May 2020 16:45:24 -0700 Subject: [PATCH 08/68] removed $remoteip that the audit didn't like --- lib/Controller.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Controller.php b/lib/Controller.php index 45b6dacd..00bd981b 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -201,13 +201,13 @@ class Controller if (($option = $this->_conf->getKey('header', 'traffic')) !== null) { $httpHeader = 'HTTP_' . $option; if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { - $remoteip = $_SERVER[$httpHeader]; + // compare source IP from web server with whitelist + if(!in_array($_SERVER[$httpHeader], $whitelist)) { + $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.')); + return; + } } } - if(!in_array($remoteip, $whitelist)) { - $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.')); - return; - } // Ensure last paste from visitors IP address was more than configured amount of seconds ago. TrafficLimiter::setConfiguration($this->_conf); From d847e2fcf22d72fe62b9e9c78972bf20c7b85f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Thu, 7 May 2020 16:46:31 -0700 Subject: [PATCH 09/68] alignment --- lib/Configuration.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Configuration.php b/lib/Configuration.php index aa6d15d0..95f54253 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -77,10 +77,10 @@ class Configuration 'markdown' => 'Markdown', ), 'traffic' => array( - 'limit' => 10, + 'limit' => 10, 'whitelist' => null, - 'header' => null, - 'dir' => 'data', + 'header' => null, + 'dir' => 'data', ), 'purge' => array( 'limit' => 300, From b8594c174a1027bb6cd449e4cb50f99617055ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Thu, 7 May 2020 16:48:17 -0700 Subject: [PATCH 10/68] whitelist_paste_creation description --- cfg/conf.sample.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index 52f152e5..6b266a7c 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -128,7 +128,8 @@ markdown = "Markdown" limit = 10 ; (optional) if you only want some source IP addresses to create pastes -; enter their IPv4 address(es) here, separated by commas +; enter their IPv4 address(es) here, separated by commas. This does not +; currently support CIDR notation, only individual IPv4 addresses. ; whitelist_paste_creation = "12.34.56.78,99.88.77.66" ; (optional) if your website runs behind a reverse proxy or load balancer, From 8fbdb69d8a2daf48c96ce39bc91bafbc2febb451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Fri, 8 May 2020 11:36:19 -0700 Subject: [PATCH 11/68] added check for null whitelist --- lib/Controller.php | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/Controller.php b/lib/Controller.php index 00bd981b..6b1dbcb5 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -196,16 +196,21 @@ class Controller */ private function _create() { - // Check whitelist if allowed to create - $whitelist = explode(',', $this->_conf->getKey('whitelist_paste_creation', 'traffic')); - if (($option = $this->_conf->getKey('header', 'traffic')) !== null) { - $httpHeader = 'HTTP_' . $option; - if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { - // compare source IP from web server with whitelist - if(!in_array($_SERVER[$httpHeader], $whitelist)) { - $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.')); - return; - } + // Check if whitelist feature is enabled + if (($option = $this->_conf->getKey('whitelist', 'traffic')) !== null) { + // Parse whitelist into array + $whitelist = explode(',', $this->_conf->getKey('whitelist_paste_creation', 'traffic')); + // Check for source IP in HTTP header + if (($option = $this->_conf->getKey('header', 'traffic')) !== null) { + $httpHeader = 'HTTP_' . $option; + // Grab source IP from HTTP header (if it exists) + if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { + // Check if source IP reported from HTTP header is in whitelist array + if (!in_array($_SERVER[$httpHeader], $whitelist)) { + $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.')); + return; + } + } } } From effe6ad3e55201d7999c20873a28ab1bba8bc3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Fri, 8 May 2020 11:37:21 -0700 Subject: [PATCH 12/68] fixed spacing to please StyleCI --- lib/Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Controller.php b/lib/Controller.php index 6b1dbcb5..2c08b308 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -209,11 +209,11 @@ class Controller if (!in_array($_SERVER[$httpHeader], $whitelist)) { $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.')); return; - } + } } } } - + // Ensure last paste from visitors IP address was more than configured amount of seconds ago. TrafficLimiter::setConfiguration($this->_conf); if (!TrafficLimiter::canPass()) { From 3f75c81a2feaa1c2350f2d674c86e5a0dc936471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Andr=C3=A9s?= Date: Fri, 8 May 2020 12:18:20 -0700 Subject: [PATCH 13/68] fixed duplicated getKey() --- lib/Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Controller.php b/lib/Controller.php index 2c08b308..0aa3fe1a 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -197,9 +197,9 @@ class Controller private function _create() { // Check if whitelist feature is enabled - if (($option = $this->_conf->getKey('whitelist', 'traffic')) !== null) { + if (($option = $this->_conf->getKey('whitelist_paste_creation', 'traffic')) !== null) { // Parse whitelist into array - $whitelist = explode(',', $this->_conf->getKey('whitelist_paste_creation', 'traffic')); + $whitelist = explode(',', $option); // Check for source IP in HTTP header if (($option = $this->_conf->getKey('header', 'traffic')) !== null) { $httpHeader = 'HTTP_' . $option; From 91041d8c59d81e5c2675d8c3c5021e471da0cfcb Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 20 Feb 2022 09:09:20 +0100 Subject: [PATCH 14/68] simplify/unify naming & wording of the two types of IP lists for the traffic limiter --- cfg/conf.sample.php | 17 ++++++++++------- lib/Configuration.php | 8 ++++---- lib/Controller.php | 2 +- lib/Persistence/TrafficLimiter.php | 20 ++++++++++---------- tst/Persistence/TrafficLimiterTest.php | 4 ++-- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index d7e21f86..3bc4ec66 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -135,14 +135,17 @@ markdown = "Markdown" ; Set this to 0 to disable rate limiting. limit = 10 -; Set ips (v4|v6) which should be exempted for the rate-limit. CIDR also supported. Needed to be comma separated. -; Unset for enabling and invalid values will be ignored -; eg: exemptedIp = '1.2.3.4,10.10.10/24' +; (optional) Set IPs adresses (v4 or v6) or subnets (CIDR) which are exempted +; from the rate-limit. Invalid IPs will be ignored. If multiple values are to +; be exempted, the list needs to be comma separated. Leave unset to disable +; exemptions. +; exempted = "1.2.3.4,10.10.10/24" -; (optional) if you only want some source IP addresses to create pastes -; enter their IPv4 address(es) here, separated by commas. This does not -; currently support CIDR notation, only individual IPv4 addresses. -; whitelist_paste_creation = "12.34.56.78,99.88.77.66" +; (optional) If you want only some source IP addresses (v4 or v6) or subnets +; (CIDR) to be allowed to create pastes, set these here. Invalid IPs will be +; ignored. If multiple values are to be exempted, the list needs to be comma +; separated. Leave unset to allow anyone to create pastes. +; creators = "1.2.3.4,10.10.10/24" ; (optional) if your website runs behind a reverse proxy or load balancer, ; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR diff --git a/lib/Configuration.php b/lib/Configuration.php index c3a6fa25..130eecf6 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -78,10 +78,10 @@ class Configuration 'markdown' => 'Markdown', ), 'traffic' => array( - 'limit' => 10, - 'header' => null, - 'exemptedIp' => null, - 'whitelist' => null, + 'limit' => 10, + 'header' => '', + 'exempted' => '', + 'creators' => '', ), 'purge' => array( 'limit' => 300, diff --git a/lib/Controller.php b/lib/Controller.php index b150691a..8ead8848 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -196,7 +196,7 @@ class Controller private function _create() { // Check if whitelist feature is enabled - if (($option = $this->_conf->getKey('whitelist_paste_creation', 'traffic')) !== null) { + if (($option = $this->_conf->getKey('creators', 'traffic')) !== '') { // Parse whitelist into array $whitelist = explode(',', $option); // Check for source IP in HTTP header diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 9e896c1d..168b46c0 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -33,13 +33,13 @@ class TrafficLimiter extends AbstractPersistence private static $_limit = 10; /** - * listed ips are exempted from limits, defaults to null + * listed IPs are exempted from limits, defaults to null * * @access private * @static * @var string|null */ - private static $_exemptedIp = null; + private static $_exempted = null; /** * key to fetch IP address @@ -63,15 +63,15 @@ class TrafficLimiter extends AbstractPersistence } /** - * set a list of ip(ranges) as string + * set a list of IP(-ranges) as string * * @access public * @static - * @param string $exemptedIps + * @param string $exempted */ - public static function setExemptedIp($exemptedIp) + public static function setExempted($exempted) { - self::$_exemptedIp = $exemptedIp; + self::$_exempted = $exempted; } /** @@ -84,9 +84,9 @@ class TrafficLimiter extends AbstractPersistence public static function setConfiguration(Configuration $conf) { self::setLimit($conf->getKey('limit', 'traffic')); - self::setExemptedIp($conf->getKey('exemptedIp', 'traffic')); + self::setExempted($conf->getKey('exempted', 'traffic')); - if (($option = $conf->getKey('header', 'traffic')) !== null) { + if (($option = $conf->getKey('header', 'traffic')) !== '') { $httpHeader = 'HTTP_' . $option; if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { self::$_ipKey = $httpHeader; @@ -152,8 +152,8 @@ class TrafficLimiter extends AbstractPersistence } // Check if $_ipKey is exempted from ratelimiting - if (!is_null(self::$_exemptedIp)) { - $exIp_array = explode(',', self::$_exemptedIp); + if (!empty(self::$_exempted)) { + $exIp_array = explode(',', self::$_exempted); foreach ($exIp_array as $ipRange) { if (self::matchIp($ipRange) === true) { return true; diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index aedbf889..8c83f0b5 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -47,7 +47,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $this->assertFalse(TrafficLimiter::canPass(), 'fifth request is to fast, may not pass'); // exempted IPs configuration - TrafficLimiter::setExemptedIp('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); + TrafficLimiter::setExempted('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); $this->assertFalse(TrafficLimiter::canPass(), 'still too fast and not exempted'); $_SERVER['REMOTE_ADDR'] = '10.10.10.10'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in exempted range'); @@ -55,7 +55,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in exempted range'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in exempted range'); - TrafficLimiter::setExemptedIp('127.*,foobar'); + TrafficLimiter::setExempted('127.*,foobar'); $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, invalid range'); $_SERVER['REMOTE_ADDR'] = 'foobar'; $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); From 190a35a53bdd29aedbf0331fc300fe89819a8465 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 20 Feb 2022 09:30:41 +0100 Subject: [PATCH 15/68] small unit test refactoring, comment wording --- lib/Persistence/TrafficLimiter.php | 2 +- tst/Persistence/TrafficLimiterTest.php | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 168b46c0..f5f8e7a8 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -161,7 +161,7 @@ class TrafficLimiter extends AbstractPersistence } } - // this hash is used as an array key, hence a shorter algo is used + // used as array key, which are limited in length, hence using algo with shorter range $hash = self::getHash('sha256'); $now = time(); $tl = (int) self::$_store->getValue('traffic_limiter', $hash); diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index 8c83f0b5..289598ef 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -45,10 +45,14 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $this->assertTrue(TrafficLimiter::canPass(), 'fourth request has different ip and may pass'); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $this->assertFalse(TrafficLimiter::canPass(), 'fifth request is to fast, may not pass'); + } - // exempted IPs configuration + public function testTrafficLimitExempted() + { TrafficLimiter::setExempted('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); - $this->assertFalse(TrafficLimiter::canPass(), 'still too fast and not exempted'); + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $this->assertTrue(TrafficLimiter::canPass(), 'first request may pass'); + $this->assertFalse(TrafficLimiter::canPass(), 'not exempted'); $_SERVER['REMOTE_ADDR'] = '10.10.10.10'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in exempted range'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in exempted range'); @@ -56,6 +60,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in exempted range'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in exempted range'); TrafficLimiter::setExempted('127.*,foobar'); + $this->assertTrue(TrafficLimiter::canPass(), 'first cached request may pass'); $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, invalid range'); $_SERVER['REMOTE_ADDR'] = 'foobar'; $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); From dbe8debe306c2453ed00ea381269ce044c889e44 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 20 Feb 2022 09:35:05 +0100 Subject: [PATCH 16/68] add creator unit tests for refactoring target, currently failing --- tst/Persistence/TrafficLimiterTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index 289598ef..4e56b8a3 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -66,4 +66,23 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches exempted range'); } + + public function testTrafficLimitCreators() + { + TrafficLimiter::setCreators('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $this->assertFalse(TrafficLimiter::canPass(), 'not a creator'); + $_SERVER['REMOTE_ADDR'] = '10.10.10.10'; + $this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in creator range'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in creator range'); + $_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe'; + $this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in creator range'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in creator range'); + TrafficLimiter::setExempted('127.*,foobar'); + $this->assertTrue(TrafficLimiter::canPass(), 'first cached request may pass'); + $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, not a creator'); + $_SERVER['REMOTE_ADDR'] = 'foobar'; + $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches creator'); + } } From 1034d4038e6c59b5b4d69b3b9e8a9b0b35a6fc18 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 20 Feb 2022 11:25:19 +0100 Subject: [PATCH 17/68] unify IP-related logic into traffic limiter --- lib/Controller.php | 29 ++------ lib/Persistence/TrafficLimiter.php | 93 ++++++++++++++++++-------- tst/Persistence/TrafficLimiterTest.php | 39 ++++++++--- 3 files changed, 101 insertions(+), 60 deletions(-) diff --git a/lib/Controller.php b/lib/Controller.php index 8ead8848..56f424c0 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -195,35 +195,14 @@ class Controller */ private function _create() { - // Check if whitelist feature is enabled - if (($option = $this->_conf->getKey('creators', 'traffic')) !== '') { - // Parse whitelist into array - $whitelist = explode(',', $option); - // Check for source IP in HTTP header - if (($option = $this->_conf->getKey('header', 'traffic')) !== null) { - $httpHeader = 'HTTP_' . $option; - // Grab source IP from HTTP header (if it exists) - if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { - // Check if source IP reported from HTTP header is in whitelist array - if (!in_array($_SERVER[$httpHeader], $whitelist)) { - $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.')); - return; - } - } - } - } - // Ensure last paste from visitors IP address was more than configured amount of seconds ago. ServerSalt::setStore($this->_model->getStore()); TrafficLimiter::setConfiguration($this->_conf); TrafficLimiter::setStore($this->_model->getStore()); - if (!TrafficLimiter::canPass()) { - $this->_return_message( - 1, I18n::_( - 'Please wait %d seconds between each post.', - $this->_conf->getKey('limit', 'traffic') - ) - ); + try { + TrafficLimiter::canPass(); + } catch (Exception $e) { + $this->_return_message(1, $e->getMessage()); return; } diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index f5f8e7a8..fab33c41 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -13,8 +13,10 @@ namespace PrivateBin\Persistence; +use Exception; use IPLib\Factory; use PrivateBin\Configuration; +use PrivateBin\I18n; /** * TrafficLimiter @@ -24,13 +26,13 @@ use PrivateBin\Configuration; class TrafficLimiter extends AbstractPersistence { /** - * time limit in seconds, defaults to 10s + * listed IPs are the only ones allowed to create, defaults to null * * @access private * @static - * @var int + * @var string|null */ - private static $_limit = 10; + private static $_creators = null; /** * listed IPs are exempted from limits, defaults to null @@ -51,19 +53,49 @@ class TrafficLimiter extends AbstractPersistence private static $_ipKey = 'REMOTE_ADDR'; /** - * set the time limit in seconds + * time limit in seconds, defaults to 10s + * + * @access private + * @static + * @var int + */ + private static $_limit = 10; + + /** + * set configuration options of the traffic limiter * * @access public * @static - * @param int $limit + * @param Configuration $conf */ - public static function setLimit($limit) + public static function setConfiguration(Configuration $conf) { - self::$_limit = $limit; + self::setCreators($conf->getKey('creators', 'traffic')); + self::setExempted($conf->getKey('exempted', 'traffic')); + self::setLimit($conf->getKey('limit', 'traffic')); + + if (($option = $conf->getKey('header', 'traffic')) !== '') { + $httpHeader = 'HTTP_' . $option; + if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { + self::$_ipKey = $httpHeader; + } + } } /** - * set a list of IP(-ranges) as string + * set a list of creator IP(-ranges) as string + * + * @access public + * @static + * @param string $creators + */ + public static function setCreators($creators) + { + self::$_creators = $creators; + } + + /** + * set a list of exempted IP(-ranges) as string * * @access public * @static @@ -75,23 +107,15 @@ class TrafficLimiter extends AbstractPersistence } /** - * set configuration options of the traffic limiter + * set the time limit in seconds * * @access public * @static - * @param Configuration $conf + * @param int $limit */ - public static function setConfiguration(Configuration $conf) + public static function setLimit($limit) { - self::setLimit($conf->getKey('limit', 'traffic')); - self::setExempted($conf->getKey('exempted', 'traffic')); - - if (($option = $conf->getKey('header', 'traffic')) !== '') { - $httpHeader = 'HTTP_' . $option; - if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { - self::$_ipKey = $httpHeader; - } - } + self::$_limit = $limit; } /** @@ -108,7 +132,7 @@ class TrafficLimiter extends AbstractPersistence } /** - * Validate $_ipKey against configured ipranges. If matched we will ignore the ip + * validate $_ipKey against configured ipranges. If matched we will ignore the ip * * @access private * @static @@ -136,22 +160,33 @@ class TrafficLimiter extends AbstractPersistence } /** - * traffic limiter - * - * Make sure the IP address makes at most 1 request every 10 seconds. + * make sure the IP address is allowed to perfom a request * * @access public * @static - * @return bool + * @throws Exception + * @return true */ public static function canPass() { + // if creators are defined, the traffic limiter will only allow creation + // for these, with no limits, and skip any other rules + if (!empty(self::$_creators)) { + $creatorIps = explode(',', self::$_creators); + foreach ($creatorIps as $ipRange) { + if (self::matchIp($ipRange) === true) { + return true; + } + } + throw new Exception(I18n::_('Your IP is not authorized to create pastes.')); + } + // disable limits if set to less then 1 if (self::$_limit < 1) { return true; } - // Check if $_ipKey is exempted from ratelimiting + // check if $_ipKey is exempted from ratelimiting if (!empty(self::$_exempted)) { $exIp_array = explode(',', self::$_exempted); foreach ($exIp_array as $ipRange) { @@ -175,6 +210,10 @@ class TrafficLimiter extends AbstractPersistence if (!self::$_store->setValue((string) $tl, 'traffic_limiter', $hash)) { error_log('failed to store the traffic limiter, it probably contains outdated information'); } - return $result; + if ($result) return true; + throw new Exception(I18n::_( + 'Please wait %d seconds between each post.', + self::$_limit + )); } } diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index 4e56b8a3..e5256c74 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -38,13 +38,21 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $this->assertTrue(TrafficLimiter::canPass(), 'first request may pass'); sleep(1); - $this->assertFalse(TrafficLimiter::canPass(), 'second request is to fast, may not pass'); + try { + $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); + } catch (Exception $e) { + $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'second request is to fast, may not pass'); + } sleep(4); $this->assertTrue(TrafficLimiter::canPass(), 'third request waited long enough and may pass'); $_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe'; $this->assertTrue(TrafficLimiter::canPass(), 'fourth request has different ip and may pass'); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - $this->assertFalse(TrafficLimiter::canPass(), 'fifth request is to fast, may not pass'); + try { + $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); + } catch (Exception $e) { + $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'fifth request is to fast, may not pass'); + } } public function testTrafficLimitExempted() @@ -52,7 +60,11 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase TrafficLimiter::setExempted('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $this->assertTrue(TrafficLimiter::canPass(), 'first request may pass'); - $this->assertFalse(TrafficLimiter::canPass(), 'not exempted'); + try { + $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); + } catch (Exception $e) { + $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'not exempted'); + } $_SERVER['REMOTE_ADDR'] = '10.10.10.10'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in exempted range'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in exempted range'); @@ -61,7 +73,11 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in exempted range'); TrafficLimiter::setExempted('127.*,foobar'); $this->assertTrue(TrafficLimiter::canPass(), 'first cached request may pass'); - $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, invalid range'); + try { + $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); + } catch (Exception $e) { + $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'request is to fast, invalid range'); + } $_SERVER['REMOTE_ADDR'] = 'foobar'; $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches exempted range'); @@ -71,16 +87,23 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase { TrafficLimiter::setCreators('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - $this->assertFalse(TrafficLimiter::canPass(), 'not a creator'); + try { + $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); + } catch (Exception $e) { + $this->assertEquals($e->getMessage(), 'Your IP is not authorized to create pastes.', 'not a creator'); + } $_SERVER['REMOTE_ADDR'] = '10.10.10.10'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in creator range'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in creator range'); $_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in creator range'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in creator range'); - TrafficLimiter::setExempted('127.*,foobar'); - $this->assertTrue(TrafficLimiter::canPass(), 'first cached request may pass'); - $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, not a creator'); + TrafficLimiter::setCreators('127.*,foobar'); + try { + $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); + } catch (Exception $e) { + $this->assertEquals($e->getMessage(), 'Your IP is not authorized to create pastes.', 'request is to fast, not a creator'); + } $_SERVER['REMOTE_ADDR'] = 'foobar'; $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches creator'); From 6b59d4f3806e27b483ae0d4521e7dd46073fe0af Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 20 Feb 2022 11:51:41 +0100 Subject: [PATCH 18/68] document change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c3f8045..f861baa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * ADDED: Configuration option to exempt IPs from the rate-limiter (#787) * ADDED: Google Cloud Storage backend support (#795) * ADDED: Oracle database support (#868) + * ADDED: Configuration option to limit paste creation and commenting to certain IPs (#883) * CHANGED: Language selection cookie only transmitted over HTTPS (#472) * CHANGED: Upgrading libraries to: random_compat 2.0.20 * CHANGED: Removed automatic `.ini` configuration file migration (#808) From 1054319313610579f773016c12e184b5c62bd12d Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 20 Feb 2022 12:22:34 +0100 Subject: [PATCH 19/68] add new translation string --- i18n/ar.json | 3 ++- i18n/bg.json | 3 ++- i18n/ca.json | 3 ++- i18n/cs.json | 3 ++- i18n/de.json | 3 ++- i18n/el.json | 3 ++- i18n/en.json | 3 ++- i18n/es.json | 3 ++- i18n/et.json | 3 ++- i18n/fi.json | 3 ++- i18n/fr.json | 3 ++- i18n/he.json | 3 ++- i18n/hi.json | 3 ++- i18n/hu.json | 3 ++- i18n/id.json | 3 ++- i18n/it.json | 3 ++- i18n/ja.json | 3 ++- i18n/jbo.json | 3 ++- i18n/ku.json | 3 ++- i18n/la.json | 3 ++- i18n/lt.json | 3 ++- i18n/nl.json | 3 ++- i18n/no.json | 3 ++- i18n/oc.json | 3 ++- i18n/pl.json | 3 ++- i18n/pt.json | 3 ++- i18n/ru.json | 3 ++- i18n/sl.json | 3 ++- i18n/sv.json | 3 ++- i18n/tr.json | 3 ++- i18n/uk.json | 3 ++- i18n/zh.json | 3 ++- 32 files changed, 64 insertions(+), 32 deletions(-) diff --git a/i18n/ar.json b/i18n/ar.json index ddca0d62..1827ddf2 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/bg.json b/i18n/bg.json index 4a025389..717c5f18 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/ca.json b/i18n/ca.json index 02b48d73..245b884f 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/cs.json b/i18n/cs.json index e6eff88a..886e9c32 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Šifrovaná poznámka ve službě PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Navštivte tento odkaz pro zobrazení poznámky. Přeposláním URL umožníte také jiným lidem přístup.", "URL shortener may expose your decrypt key in URL.": "Zkracovač URL může odhalit váš dešifrovací klíč v URL.", - "Save paste": "Uložit příspěvek" + "Save paste": "Uložit příspěvek", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/de.json b/i18n/de.json index 0c4cd70e..c60c1563 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Verschlüsselte Notiz auf PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diesen Link um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.", "URL shortener may expose your decrypt key in URL.": "Der URL-Verkürzer kann den Schlüssel in der URL enthüllen.", - "Save paste": "Text speichern" + "Save paste": "Text speichern", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/el.json b/i18n/el.json index 6e33978d..acfca8f9 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/en.json b/i18n/en.json index a96bab5d..2ba6068f 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/es.json b/i18n/es.json index 5aa2b380..909b32cf 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Nota cifrada en PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visite este enlace para ver la nota. Dar la URL a cualquier persona también les permite acceder a la nota.", "URL shortener may expose your decrypt key in URL.": "El acortador de URL puede exponer su clave de descifrado en el URL.", - "Save paste": "Guardar \"paste\"" + "Save paste": "Guardar \"paste\"", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/et.json b/i18n/et.json index 9ab2840c..f02cefba 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Krüpteeritud kiri PrivateBin-is", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kirja nägemiseks külasta seda linki. Teistele URL-i andmine lubab ka neil ligi pääseda kirjale.", "URL shortener may expose your decrypt key in URL.": "URL-i lühendaja võib paljastada sinu dekrüpteerimisvõtme URL-is.", - "Save paste": "Salvesta kleebe" + "Save paste": "Salvesta kleebe", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/fi.json b/i18n/fi.json index aea2b63c..0ad8c410 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Salattu viesti PrivateBinissä", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Käy tässä linkissä nähdäksesi viestin. URL:n antaminen kenellekään antaa heidänkin päästä katsomeen viestiä. ", "URL shortener may expose your decrypt key in URL.": "URL-lyhentäjä voi paljastaa purkuavaimesi URL:ssä.", - "Save paste": "Tallenna paste" + "Save paste": "Tallenna paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/fr.json b/i18n/fr.json index 0e14d799..b5767f70 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Message chiffré sur PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visiter ce lien pour voir la note. Donner l'URL à une autre personne lui permet également d'accéder à la note.", "URL shortener may expose your decrypt key in URL.": "Raccourcir l'URL peut exposer votre clé de déchiffrement dans l'URL.", - "Save paste": "Sauver le paste" + "Save paste": "Sauver le paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/he.json b/i18n/he.json index 046874e2..503dbc16 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "הערה מוצפנת ב־PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/hi.json b/i18n/hi.json index 0cd2787a..4df6aa71 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/hu.json b/i18n/hu.json index 1cff1a6f..c9acb042 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Titkosított jegyzet a PrivateBinen", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Látogasd meg ezt a hivatkozást a bejegyzés megtekintéséhez. Ha mások számára is megadod ezt a linket, azzal hozzáférnek ők is.", "URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/id.json b/i18n/id.json index e58900a9..1edeb997 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Catatan ter-ekrip di PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kunjungi tautan ini untuk melihat catatan. Memberikan alamat URL pada siapapun juga, akan mengizinkan mereka untuk mengakses catatan, so pasti gitu loh Kaka.", "URL shortener may expose your decrypt key in URL.": "Pemendek URL mungkin akan menampakkan kunci dekrip Anda dalam URL.", - "Save paste": "Simpan paste" + "Save paste": "Simpan paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/it.json b/i18n/it.json index 659816e4..8b9f391f 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Nota crittografata su PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visita questo collegamento per vedere la nota. Dare l'URL a chiunque consente anche a loro di accedere alla nota.", "URL shortener may expose your decrypt key in URL.": "URL shortener può esporre la tua chiave decrittografata nell'URL.", - "Save paste": "Salva il messagio" + "Save paste": "Salva il messagio", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/ja.json b/i18n/ja.json index 807a38c0..330032fd 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/jbo.json b/i18n/jbo.json index 10925d0f..82cea900 100644 --- a/i18n/jbo.json +++ b/i18n/jbo.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": ".i lo lo notci ku mifra cu zvati sivlolnitvanku'a", "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 shortener may expose your decrypt key in URL.", - "Save paste": "rejgau fukpi" + "Save paste": "rejgau fukpi", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/ku.json b/i18n/ku.json index ae72ebd8..bca43477 100644 --- a/i18n/ku.json +++ b/i18n/ku.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/la.json b/i18n/la.json index 2098982d..fe6c4001 100644 --- a/i18n/la.json +++ b/i18n/la.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/lt.json b/i18n/lt.json index 10b62d84..195fab06 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Šifruoti užrašai ties PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Norėdami matyti užrašus, aplankykite šį tinklalapį. Pasidalinus šiuo URL adresu su kitais žmonėmis, jiems taip pat bus leidžiama prieiga prie šių užrašų.", "URL shortener may expose your decrypt key in URL.": "URL trumpinimo įrankis gali atskleisti URL adrese jūsų iššifravimo raktą.", - "Save paste": "Įrašyti įdėjimą" + "Save paste": "Įrašyti įdėjimą", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/nl.json b/i18n/nl.json index 65c4e37c..b20416ae 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/no.json b/i18n/no.json index b0ca95a0..eda1e4c5 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Kryptert notat på PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besøk denne lenken for å se notatet. Hvis lenken deles med andre, vil de også kunne se notatet.", "URL shortener may expose your decrypt key in URL.": "URL forkorter kan avsløre dekrypteringsnøkkelen.", - "Save paste": "Lagre utklipp" + "Save paste": "Lagre utklipp", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/oc.json b/i18n/oc.json index 6a0a69ce..1bb4de67 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Nòtas chifradas sus PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitatz aqueste ligam per veire la nòta. Fornir lo ligam a qualqu’un mai li permet tanben d’accedir a la nòta.", "URL shortener may expose your decrypt key in URL.": "Los espleches d’acorchiment d’URL pòdon expausar la clau de deschiframent dins l’URL.", - "Save paste": "Enregistrar lo tèxt" + "Save paste": "Enregistrar lo tèxt", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/pl.json b/i18n/pl.json index 4b4a67be..b6127cad 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/pt.json b/i18n/pt.json index 0f6c1333..72d0250f 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Nota criptografada no PrivateBin", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visite esse link para ver a nota. Dar a URL para qualquer um permite que eles também acessem a nota.", "URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/ru.json b/i18n/ru.json index a9112330..2d3edb9a 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Зашифрованная запись на PrivateBin", "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.": "Сервис сокращения ссылок может получить ваш ключ расшифровки из ссылки.", - "Save paste": "Сохранить запись" + "Save paste": "Сохранить запись", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/sl.json b/i18n/sl.json index c4bb0cb7..27c43611 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/sv.json b/i18n/sv.json index f2286e02..bb613296 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/tr.json b/i18n/tr.json index 0abbc37d..c2c380e1 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/uk.json b/i18n/uk.json index 1916a244..52e97233 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste" + "Save paste": "Save paste", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } diff --git a/i18n/zh.json b/i18n/zh.json index 2ab63bf4..e90627fb 100644 --- a/i18n/zh.json +++ b/i18n/zh.json @@ -185,5 +185,6 @@ "Encrypted note on PrivateBin": "PrivateBin 上的加密笔记", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "访问此链接来查看该笔记。将此 URL 发送给任何人即可允许其访问该笔记。", "URL shortener may expose your decrypt key in URL.": "短链接服务可能会暴露您在 URL 中的解密密钥。", - "Save paste": "保存内容" + "Save paste": "保存内容", + "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." } From f987e96d4b1edc336009062a7e31549886f540ef Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 20 Feb 2022 12:25:55 +0100 Subject: [PATCH 20/68] apply StyleCI recommendation --- lib/Persistence/TrafficLimiter.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index fab33c41..d0b82be9 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -210,7 +210,9 @@ class TrafficLimiter extends AbstractPersistence if (!self::$_store->setValue((string) $tl, 'traffic_limiter', $hash)) { error_log('failed to store the traffic limiter, it probably contains outdated information'); } - if ($result) return true; + if ($result) { + return true; + } throw new Exception(I18n::_( 'Please wait %d seconds between each post.', self::$_limit From d5d06caf404868f56a2124899eb89788176be88c Mon Sep 17 00:00:00 2001 From: Patriccollu Date: Thu, 24 Feb 2022 19:50:27 +0100 Subject: [PATCH 21/68] Adding co.json for Corsican --- i18n/co.json | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 i18n/co.json diff --git a/i18n/co.json b/i18n/co.json new file mode 100644 index 00000000..107f480b --- /dev/null +++ b/i18n/co.json @@ -0,0 +1,189 @@ +{ + "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 hè un serviziu in linea di tipu « pastebin » (ghjestiunariu d’appiccicu di pezzi di testu è di codice di fonte) minimalistu è à fonte aperta induve u servitore ùn hà micca cunnuscenza di i dati mandati. I dati sò cifrati è dicifrati %sin u navigatore%s cù una cifratura AES di 256 bit.", + "More information on the project page.": "Più d’infurmazione annant’à a pagina di u prughjettu<\/a>.", + "Because ignorance is bliss": "Perchè l’ignurenza hè una campa", + "en": "co", + "Paste does not exist, has expired or has been deleted.": "L’appiccicu ùn esiste micca, hè scadutu o hè statu squassatu.", + "%s requires php %s or above to work. Sorry.": "Per disgrazzia, %s richiede php %s o più recente per funziunà.  ", + "%s requires configuration section [%s] to be present in configuration file.": "%s richiede a presenza di a sezzione di cunfigurazione [%s] in a schedariu di cunfigurazione.", + "Aspettate %d seconds between each post.": [ + "Aspettate %d seconda trà dui publicazioni.", + "Aspettate %d seconde trà dui publicazioni.", + "Please wait %d seconds between each post. (2nd plural)", + "Please wait %d seconds between each post. (3rd plural)" + ], + "Paste is limited to %s of encrypted data.": "L’appiccicu hè limitatu à %s di dati cifrati.", + "Invalid data.": "Dati inaccetevule.", + "You are unlucky. Try again.": "Pruvate torna, Serete più furtunati.", + "Error saving comment. Sorry.": "Sbagliu à l’arregistramentu di u cummentu.  ", + "Error saving paste. Sorry.": "Sbagliu à l’arregistramentu di l’appiccicu.  ", + "Invalid paste ID.": "N° di l’appiccicu inaccettevule.", + "Paste is not of burn-after-reading type.": "L’appiccicu ùn hè micca di tipu « Squassà dopu a lettura ».", + "Wrong deletion token. Paste was not deleted.": "Gettone di squassatura incurrettu. L’appiccicu ùn hè micca statu squassatu.", + "Paste was properly deleted.": "L’appiccicu hè statu squassatu currettamente.", + "JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript hè richiestu per fà funziunà %s. Scusate per stu penseru.", + "%s requires a modern browser to work.": "%s richiede un navigatore mudernu per funziunà.", + "New": "Novu", + "Send": "Mandà", + "Clone": "Duppione", + "Raw text": "Testu grossu", + "Expires": "Scadenza", + "Burn after reading": "Squassà dopu a lettura", + "Open discussion": "Apre una chjachjarata", + "Password (recommended)": "Parolla d’intesa (ricumandata)", + "Discussion": "Chjachjarata", + "Toggle navigation": "Invertisce a navigazione", + "%d seconds": [ + "%d seconda", + "%d seconde", + "%d seconds (2nd plural)", + "%d seconds (3rd plural)" + ], + "%d minutes": [ + "%d minutu", + "%d minuti", + "%d minutes (2nd plural)", + "%d minutes (3rd plural)" + ], + "%d hours": [ + "%d ora", + "%d ore", + "%d hours (2nd plural)", + "%d hours (3rd plural)" + ], + "%d days": [ + "%d ghjornu", + "%d ghjorni", + "%d days (2nd plural)", + "%d days (3rd plural)" + ], + "%d weeks": [ + "%d settimana", + "%d settimane", + "%d weeks (2nd plural)", + "%d weeks (3rd plural)" + ], + "%d months": [ + "%d mese", + "%d mesi", + "%d months (2nd plural)", + "%d months (3rd plural)" + ], + "%d years": [ + "%d annu", + "%d anni", + "%d years (2nd plural)", + "%d years (3rd plural)" + ], + "Never": "Mai", + "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Nota : Què hè un serviziu di prova ; i dati ponu esse squassati à ogni mumentu. Parechji catorni anu da esse tombi s’è vò impiegate troppu stu serviziu.", + "Stu ducumentu serà scadutu in %d seconds.": [ + "Stu ducumentu serà scadutu in %d seconda.", + "Stu ducumentu serà scadutu in %d seconde.", + "This document will expire in %d seconds. (2nd plural)", + "This document will expire in %d seconds. (3rd plural)" + ], + "Stu ducumentu serà scadutu in %d minutes.": [ + "Stu ducumentu serà scadutu in %d minutu.", + "Stu ducumentu serà scadutu in %d minuti.", + "This document will expire in %d minutes. (2nd plural)", + "This document will expire in %d minutes. (3rd plural)" + ], + "Stu ducumentu serà scadutu in %d hours.": [ + "Stu ducumentu serà scadutu in %d ora.", + "Stu ducumentu serà scadutu in %d ore.", + "This document will expire in %d hours. (2nd plural)", + "This document will expire in %d hours. (3rd plural)" + ], + "Stu ducumentu serà scadutu in %d days.": [ + "Stu ducumentu serà scadutu in %d ghjornu.", + "Stu ducumentu serà scadutu in %d ghjorni.", + "This document will expire in %d days. (2nd plural)", + "This document will expire in %d days. (3rd plural)" + ], + "Stu ducumentu serà scadutu in %d months.": [ + "Stu ducumentu serà scadutu in %d mese.", + "Stu ducumentu serà scadutu in %d mesi.", + "This document will expire in %d months. (2nd plural)", + "This document will expire in %d months. (3rd plural)" + ], + "Please enter the password for this paste:": "Stampittate a parolla d’intesa per st’appiccicu :", + "Could not decrypt data (Wrong key?)": "Ùn si pò micca dicifrà i dati ; seria incurretta a chjave ?", + "Could not delete the paste, it was not stored in burn after reading mode.": "Ùn si pò micca squassà l’appiccicu, ùn hè micca statu in u modu « Squassà dopu a lettura ».", + "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "SOLU CÙ L’OCHJI. Ùn chjudite micca sta finestra, stu messaghju un puderà più esse affissatu torna.", + "Could not decrypt comment; Wrong key?": "Ùn si pò micca dicifrà u cummentu. Seria incurretta a chjave ?", + "Reply": "Risponde", + "Anonymous": "Anonimu", + "Avatar generated from IP address": "Avatar ingeneratu da l’indirizzu IP", + "Add comment": "Aghjunghje un cummentu", + "Optional nickname…": "Cugnome ozzionale…", + "Post comment": "Impustà u cummentu", + "Sending comment…": "Inviu di u cummentu…", + "Comment posted.": "Cummentu inviatu.", + "Could not refresh display: %s": "Ùn si pò micca attualizà l’affissera : %s", + "unknown status": "statu scunnisciutu", + "server error or not responding": "sbagliu di u servitore o u servitore ùn risponde micca", + "Could not post comment: %s": "Ùn si pò micca impustà u cummentu : %s", + "Sending paste…": "Inviu di l’appiccicu…", + "Your paste is %s (Hit [Ctrl]+[c] to copy)": "U vostru appiccicu si trova à l’indirizzu%s<\/a> (Appughjate [Ctrl]+[c] per cupià u liame)<\/span>", + "Delete data": "Squassà i dati", + "Could not create paste: %s": "Ùn si pò micca creà l’appiccicu : %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à l’appiccicu : A chjave di dicifratura hè assente in l’indirizzu. Averiate impiegatu un orientadore d’indirizzu o un riduttore chì ammuzzeghja una parte di l’indirizzu ?", + "B": "o", + "KiB": "Ko", + "MiB": "Mo", + "GiB": "Go", + "TiB": "To", + "PiB": "Po", + "EiB": "Eo", + "ZiB": "Zo", + "YiB": "Yo", + "Format": "Furmatu", + "Plain Text": "Testu in chjaru", + "Source Code": "Codice di fonte", + "Markdown": "Markdown", + "Download attachment": "Scaricà a pezza aghjunta", + "Cloned: '%s'": "Duppiatu : « %s »", + "The cloned file '%s' was attached to this paste.": "U schedariu duppiatu « %s » hè statu aghjuntu à st’appiccicu.", + "Attach a file": "Aghjunghje un schedariu", + "alternatively drag & drop a file or paste an image from the clipboard": "in alternanza, sguillà è depone un schedariu o incullà una fiura da u preme’papei", + "File too large, to display a preview. Please download the attachment.": "Schedariu troppu maiò per affissà una fighjulata. Scaricate a pezza aghjunta.", + "Remove attachment": "Caccià a pezza aghjunta.", + "Your browser does not support uploading encrypted files. Please use a newer browser.": "U vostru navigatore ùn accetta micca l’inviu di i schedarii cifrati. Impiegate un navigatore più recente.", + "Invalid attachment.": "A pezza aghjunta hè inaccettevule.", + "Options": "Ozzioni", + "Shorten URL": "Ammuzzà l’indirizzu", + "Editor": "Editore", + "Preview": "Fighjulata", + "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s richiede chì a variabile PATH si compii cù « %s ». Mudificate a variabile PATH in u vostru index.php.", + "Decrypt": "Dicifrà", + "Enter password": "Stampittate a parolla d’intesa", + "Loading…": "Caricamentu…", + "Decrypting paste…": "Dicifratura di l’appiccicu…", + "Preparing new paste…": "Approntu di u novu appiccicu…", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "S’è stu messaghju ùn smarisce micca, lighjite sta FAQ per ottene infurmazioni annant’à a risuluzione di i prublemi<\/a>.", + "+++ no paste text +++": "+++ nisunu testu incullatu +++", + "Could not get paste data: %s": "Ùn si pò micca ottene i dati di l’appiccicu : %s", + "QR code": "Codice QR", + "This website is using an insecure HTTP connection! Please use it only for testing.": "Stu situ web impiegheghja una cunnessione HTTP non sicura ! impiegatelu solu per una prova.", + "For more information see this FAQ entry.": "Per sapene di più, lighjite sta rubrica di a FAQ<\/a>.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "U vostru navigatore pò richiede una cunnessione HTTPS per permette l’usu di l’API WebCrypto. Pruvate di passà à HTTPS<\/a>.", + "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "U vostru navigatore ùn accetta micca WebAssembly, impiegatu per a cumpressione zlib. Pudete creà ducumenti micca cumpressi, ma ùn pudete micca leghje quelli chì sò cumpressi.", + "waiting on user to provide a password": "in attesa di l’utilizatore per furnisce una parolla d’intesa", + "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Ùn si pò micca dicifrà i dati. Avete stampittatu una parolla d’intesa incurretta ? Pruvate torna cù u buttone insù.", + "Retry": "Pruvà torna", + "Showing raw text…": "Affissera di u testu grossu…", + "Notice:": "Avertimentu :", + "This link will expire after %s.": "Stu liame hà da scade dopu à %s.", + "This link can only be accessed once, do not use back or refresh button in your browser.": "Stu liame pò esse accessu solu una volta, ùn impiegate micca i buttoni Precedente o Attualizà di u vostru navigatore.", + "Link:": "Liame :", + "Recipient may become aware of your timezone, convert time to UTC?": "U destinatariu pò cunnnosce u vostru fusu orariu. Vulete cunvertisce l’ora in u furmatu UTC ?", + "Use Current Timezone": "Impiegà u fusu orariu attuale", + "Convert To UTC": "Cunvertisce in UTC", + "Close": "Chjode", + "Encrypted note on PrivateBin": "Nota cifrata nant’à PrivateBin", + "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitate stu liame per vede a nota. Date l’indirizzu à qualunque li permette d’accede à a nota dinù.", + "URL shortener may expose your decrypt key in URL.": "Un ammuzzatore d’indirizzu pò palisà a vostra chjave di dicifratura in l’indirizzu.", + "Save paste": "Arregistrà l’appiccicu" +} From 004e2dd75c3f2406410832577612c886e05e1598 Mon Sep 17 00:00:00 2001 From: Patriccollu Date: Thu, 24 Feb 2022 20:03:48 +0100 Subject: [PATCH 22/68] Update to add Corsican as new locale --- js/privatebin.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/privatebin.js b/js/privatebin.js index ef030fb3..b6a3226e 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -601,7 +601,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { * @prop {string[]} * @readonly */ - const supportedLanguages = ['bg', 'ca', 'cs', 'de', 'es', 'et', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh']; + const supportedLanguages = ['bg', 'ca', 'co', 'cs', 'de', 'es', 'et', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh']; /** * built in language @@ -778,6 +778,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { { case 'cs': return n === 1 ? 0 : (n >= 2 && n <=4 ? 1 : 2); + case 'co': case 'fr': case 'oc': case 'zh': From 30c0d22468503c572456bd533adf638f80a045fa Mon Sep 17 00:00:00 2001 From: Patriccollu Date: Thu, 24 Feb 2022 20:05:19 +0100 Subject: [PATCH 23/68] Updating I18n.php to add Corsican as new locale --- lib/I18n.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/I18n.php b/lib/I18n.php index bc8b765c..1321c4a2 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -317,6 +317,7 @@ class I18n switch (self::$_language) { case 'cs': return $n == 1 ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2); + case 'co': case 'fr': case 'oc': case 'zh': From 9b9be50678cd1ea3948519e80d1c773e4ed5430c Mon Sep 17 00:00:00 2001 From: Patriccollu Date: Fri, 25 Feb 2022 00:02:58 +0100 Subject: [PATCH 24/68] Adding co.json for Corsican --- i18n/co.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/co.json b/i18n/co.json index 107f480b..10310425 100644 --- a/i18n/co.json +++ b/i18n/co.json @@ -7,7 +7,7 @@ "Paste does not exist, has expired or has been deleted.": "L’appiccicu ùn esiste micca, hè scadutu o hè statu squassatu.", "%s requires php %s or above to work. Sorry.": "Per disgrazzia, %s richiede php %s o più recente per funziunà.  ", "%s requires configuration section [%s] to be present in configuration file.": "%s richiede a presenza di a sezzione di cunfigurazione [%s] in a schedariu di cunfigurazione.", - "Aspettate %d seconds between each post.": [ + "Please wait %d seconds between each post.": [ "Aspettate %d seconda trà dui publicazioni.", "Aspettate %d seconde trà dui publicazioni.", "Please wait %d seconds between each post. (2nd plural)", From d73cfb093c771dd158985f12ae165654136a4e06 Mon Sep 17 00:00:00 2001 From: Patriccollu Date: Fri, 25 Feb 2022 13:17:50 +0100 Subject: [PATCH 25/68] Updating CHANGELOG.md for new locale Corsican --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c3f8045..a6ab12b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # PrivateBin version history * **1.4 (not yet released)** - * ADDED: Translations for Estonian and Lojban + * ADDED: Translations for Corsican, Estonian and Lojban * ADDED: new HTTP headers improving security (#765) * ADDED: Download button for paste text (#774) * ADDED: Opt-out of federated learning of cohorts (FLoC) (#776) From 110962bc8e8a2299c2f4a38872146fefb530e7d0 Mon Sep 17 00:00:00 2001 From: Patriccollu Date: Fri, 25 Feb 2022 13:18:01 +0100 Subject: [PATCH 26/68] Updating CREDITS.md for new locale Corsican --- CREDITS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.md b/CREDITS.md index de0ebe81..536042b1 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -55,3 +55,4 @@ Sébastien Sauvage - original idea and main developer * retiolus - Catalan * sarnane - Estonian * foxsouns - Lojban +* Patriccollu di Santa Maria è Sichè - Corsican From 77153a9b493102fc5edbc7875ea11a862ae3b1fd Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 26 Feb 2022 06:58:41 +0100 Subject: [PATCH 27/68] Update tst/Persistence/TrafficLimiterTest.php Co-authored-by: rugk --- tst/Persistence/TrafficLimiterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index e5256c74..6fe7de04 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -76,7 +76,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase try { $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); } catch (Exception $e) { - $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'request is to fast, invalid range'); + $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'request is too fast, invalid range'); } $_SERVER['REMOTE_ADDR'] = 'foobar'; $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); From 247992fbcad07e0639bb441f4f51790b1d630358 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 26 Feb 2022 06:58:54 +0100 Subject: [PATCH 28/68] Update tst/Persistence/TrafficLimiterTest.php Co-authored-by: rugk --- tst/Persistence/TrafficLimiterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index 6fe7de04..efa5746f 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -80,7 +80,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase } $_SERVER['REMOTE_ADDR'] = 'foobar'; $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); - $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches exempted range'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is too fast, but non-IP address matches exempted range'); } public function testTrafficLimitCreators() From 094c96afc60a41c06aa6ddd00c542950caba45e8 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 26 Feb 2022 06:59:02 +0100 Subject: [PATCH 29/68] Update tst/Persistence/TrafficLimiterTest.php Co-authored-by: rugk --- tst/Persistence/TrafficLimiterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index efa5746f..22463568 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -94,7 +94,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase } $_SERVER['REMOTE_ADDR'] = '10.10.10.10'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in creator range'); - $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in creator range'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is too fast, but IPv4 in creator range'); $_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in creator range'); $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in creator range'); From d544d5e763b3777c99035d77dd4b25cee02d70bf Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 26 Feb 2022 06:59:11 +0100 Subject: [PATCH 30/68] Update tst/Persistence/TrafficLimiterTest.php Co-authored-by: rugk --- tst/Persistence/TrafficLimiterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index 22463568..a8b8040c 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -97,7 +97,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $this->assertTrue(TrafficLimiter::canPass(), 'request is too fast, but IPv4 in creator range'); $_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe'; $this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in creator range'); - $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in creator range'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is too fast, but IPv6 in creator range'); TrafficLimiter::setCreators('127.*,foobar'); try { $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); From fe89161848298f9583384e62ed3d769dcce2eda1 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 26 Feb 2022 07:18:59 +0100 Subject: [PATCH 31/68] replace deprecated function calls --- lib/Persistence/TrafficLimiter.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index d0b82be9..f071562b 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -15,6 +15,7 @@ namespace PrivateBin\Persistence; use Exception; use IPLib\Factory; +use IPLib\ParseStringFlag; use PrivateBin\Configuration; use PrivateBin\I18n; @@ -144,8 +145,8 @@ class TrafficLimiter extends AbstractPersistence if (is_string($ipRange)) { $ipRange = trim($ipRange); } - $address = Factory::addressFromString($_SERVER[self::$_ipKey]); - $range = Factory::rangeFromString($ipRange); + $address = Factory::parseAddressString($_SERVER[self::$_ipKey]); + $range = Factory::parseRangeString($ipRange, ParseStringFlag::IPV4_MAYBE_NON_DECIMAL); // address could not be parsed, we might not be in IP space and try a string comparison instead if (is_null($address)) { From 4c8d23d3a5162014aae63594d2ade506af9f6021 Mon Sep 17 00:00:00 2001 From: Patriccollu Date: Sat, 26 Feb 2022 10:35:08 +0100 Subject: [PATCH 32/68] Adding co.json for Corsican --- i18n/co.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/i18n/co.json b/i18n/co.json index 10310425..652d0d75 100644 --- a/i18n/co.json +++ b/i18n/co.json @@ -78,31 +78,31 @@ ], "Never": "Mai", "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Nota : Què hè un serviziu di prova ; i dati ponu esse squassati à ogni mumentu. Parechji catorni anu da esse tombi s’è vò impiegate troppu stu serviziu.", - "Stu ducumentu serà scadutu in %d seconds.": [ + "This document will expire in %d seconds.": [ "Stu ducumentu serà scadutu in %d seconda.", "Stu ducumentu serà scadutu in %d seconde.", "This document will expire in %d seconds. (2nd plural)", "This document will expire in %d seconds. (3rd plural)" ], - "Stu ducumentu serà scadutu in %d minutes.": [ + "This document will expire in %d minutes.": [ "Stu ducumentu serà scadutu in %d minutu.", "Stu ducumentu serà scadutu in %d minuti.", "This document will expire in %d minutes. (2nd plural)", "This document will expire in %d minutes. (3rd plural)" ], - "Stu ducumentu serà scadutu in %d hours.": [ + "This document will expire in %d hours.": [ "Stu ducumentu serà scadutu in %d ora.", "Stu ducumentu serà scadutu in %d ore.", "This document will expire in %d hours. (2nd plural)", "This document will expire in %d hours. (3rd plural)" ], - "Stu ducumentu serà scadutu in %d days.": [ + "This document will expire in %d days.": [ "Stu ducumentu serà scadutu in %d ghjornu.", "Stu ducumentu serà scadutu in %d ghjorni.", "This document will expire in %d days. (2nd plural)", "This document will expire in %d days. (3rd plural)" ], - "Stu ducumentu serà scadutu in %d months.": [ + "This document will expire in %d months.": [ "Stu ducumentu serà scadutu in %d mese.", "Stu ducumentu serà scadutu in %d mesi.", "This document will expire in %d months. (2nd plural)", From bef5c647cf4a49ce6fec02778e71f0cca1b57ffd Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Mon, 28 Feb 2022 11:29:33 +0100 Subject: [PATCH 33/68] New translations en.json (Occitan) --- i18n/oc.json | 72 ++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/i18n/oc.json b/i18n/oc.json index 1bb4de67..f074d919 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -37,76 +37,76 @@ "%d seconds": [ "%d segonda", "%d segondas", - "%d seconds (2nd plural)", - "%d seconds (3rd plural)" + "%d segondas", + "%d segondas" ], "%d minutes": [ "%d minuta", "%d minutas", - "%d minutes (2nd plural)", - "%d minutes (3rd plural)" + "%d minutas", + "%d minutas" ], "%d hours": [ "%d ora", "%d oras", - "%d hours (2nd plural)", - "%d hours (3rd plural)" + "%d oras", + "%d oras" ], "%d days": [ "%d jorn", "%d jorns", - "%d days (2nd plural)", - "%d days (3rd plural)" + "%d jorns", + "%d jorns" ], "%d weeks": [ "%d setmana", "%d setmanas", - "%d weeks (2nd plural)", - "%d weeks (3rd plural)" + "%d setmanas", + "%d setmanas" ], "%d months": [ "%d mes", "%d meses", - "%d months (2nd plural)", - "%d months (3rd plural)" + "%d meses", + "%d meses" ], "%d years": [ "%d an", "%d ans", - "%d years (2nd plural)", - "%d years (3rd plural)" + "%d ans", + "%d ans" ], "Never": "Jamai", "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Nota : Aquò es un servici d’espròva : las donadas pòdon èsser suprimidas a cada moment. De catons moriràn s’abusatz d’aqueste servici.", "This document will expire in %d seconds.": [ - "Ce document expirera dans %d seconde.", - "Aqueste document expirarà dins %d segondas.", - "Aqueste document expirarà dins %d segondas.", - "Aqueste document expirarà dins %d segondas." + "Aqueste document expirarà d’aquí %d segonda.", + "Aqueste document expirarà d’aquí %d segondas.", + "Aqueste document expirarà d’aquí %d segondas.", + "Aqueste document expirarà d’aquí %d segondas." ], "This document will expire in %d minutes.": [ - "Ce document expirera dans %d minute.", - "Aqueste document expirarà dins %d minutas.", - "Aqueste document expirarà dins %d minutas.", - "Aqueste document expirarà dins %d minutas." + "Aqueste document expirarà d’aquí %d minuta.", + "Aqueste document expirarà d’aquí %d minutas.", + "Aqueste document expirarà d’aquí %d minutas.", + "Aqueste document expirarà d’aquí %d minutas." ], "This document will expire in %d hours.": [ - "Ce document expirera dans %d heure.", - "Aqueste document expirarà dins %d oras.", - "Aqueste document expirarà dins %d oras.", - "Aqueste document expirarà dins %d oras." + "Aqueste document expirarà d’aquí %d ora.", + "Aqueste document expirarà d’aquí %d oras.", + "Aqueste document expirarà d’aquí %d oras.", + "Aqueste document expirarà d’aquí %d oras." ], "This document will expire in %d days.": [ - "Ce document expirera dans %d jour.", - "Aqueste document expirarà dins %d jorns.", - "Aqueste document expirarà dins %d jorns.", - "Aqueste document expirarà dins %d jorns." + "Aqueste document expirarà d’aquí %d jorn.", + "Aqueste document expirarà d’aquí %d jorns.", + "Aqueste document expirarà d’aquí %d jorns.", + "Aqueste document expirarà d’aquí %d jorns." ], "This document will expire in %d months.": [ - "Ce document expirera dans %d mois.", - "Aqueste document expirarà dins %d meses.", - "Aqueste document expirarà dins %d meses.", - "Aqueste document expirarà dins %d meses." + "Aqueste document expirarà d’aquí %d mes.", + "Aqueste document expirarà d’aquí %d meses.", + "Aqueste document expirarà d’aquí %d meses.", + "Aqueste document expirarà d’aquí %d meses." ], "Please enter the password for this paste:": "Picatz lo senhal per aqueste tèxte :", "Could not decrypt data (Wrong key?)": "Impossible de deschifrar las donadas (marrida clau ?)", @@ -156,7 +156,7 @@ "Shorten URL": "Acorchir l’URL", "Editor": "Editar", "Preview": "Previsualizar", - "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s demanda que lo PATH termine en \"%s\". Mercés de metre a jorn lo PATH dins vòstre index.php.", + "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s demanda que lo PATH termine en « %s ». Mercés de metre a jorn lo PATH dins vòstre index.php.", "Decrypt": "Deschifrar", "Enter password": "Picatz lo senhal", "Loading…": "Cargament…", @@ -186,5 +186,5 @@ "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitatz aqueste ligam per veire la nòta. Fornir lo ligam a qualqu’un mai li permet tanben d’accedir a la nòta.", "URL shortener may expose your decrypt key in URL.": "Los espleches d’acorchiment d’URL pòdon expausar la clau de deschiframent dins l’URL.", "Save paste": "Enregistrar lo tèxt", - "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." + "Your IP is not authorized to create pastes.": "Vòstra adreça IP a pas l’autorizacion de crear de tèxtes." } From ccdb26df513530d84c363f1d720695b46859e1f7 Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Mon, 28 Feb 2022 12:45:13 +0100 Subject: [PATCH 34/68] New translations en.json (Corsican) --- i18n/co.json | 74 ++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/i18n/co.json b/i18n/co.json index 0abb2048..21ca71ec 100644 --- a/i18n/co.json +++ b/i18n/co.json @@ -1,23 +1,23 @@ { "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 hè un serviziu in linea di tipu « pastebin » (ghjestiunariu d’appiccicu di pezzi di testu è di codice di fonte) minimalistu è à fonte aperta induve u servitore ùn hà micca cunnuscenza di i dati mandati. I dati sò cifrati è dicifrati %sin u navigatore%s cù una cifratura AES di 256 bit.", - "More information on the project page.": "Più d’infurmazione annant’à a pagina di u prughjettu<\/a>.", + "More information on the project page.": "Più d’infurmazione annant’à a pagina di u prughjettu.", "Because ignorance is bliss": "Perchè l’ignurenza hè una campa", "en": "co", "Paste does not exist, has expired or has been deleted.": "L’appiccicu ùn esiste micca, hè scadutu o hè statu squassatu.", - "%s requires php %s or above to work. Sorry.": "Per disgrazzia, %s richiede php %s o più recente per funziunà.  ", + "%s requires php %s or above to work. Sorry.": "Per disgrazzia, %s richiede php %s o più recente per funziunà.", "%s requires configuration section [%s] to be present in configuration file.": "%s richiede a presenza di a sezzione di cunfigurazione [%s] in a schedariu di cunfigurazione.", "Please wait %d seconds between each post.": [ "Aspettate %d seconda trà dui publicazioni.", "Aspettate %d seconde trà dui publicazioni.", - "Please wait %d seconds between each post. (2nd plural)", - "Please wait %d seconds between each post. (3rd plural)" - ], + "Aspettate %d seconde trà dui publicazioni.", + "Aspettate %d seconde trà dui publicazioni." + ], "Paste is limited to %s of encrypted data.": "L’appiccicu hè limitatu à %s di dati cifrati.", "Invalid data.": "Dati inaccetevule.", "You are unlucky. Try again.": "Pruvate torna, Serete più furtunati.", - "Error saving comment. Sorry.": "Sbagliu à l’arregistramentu di u cummentu.  ", - "Error saving paste. Sorry.": "Sbagliu à l’arregistramentu di l’appiccicu.  ", + "Error saving comment. Sorry.": "Per disgrazzia, ci hè un sbagliu à l’arregistramentu di u cummentu.", + "Error saving paste. Sorry.": "Per disgrazzia, ci hè un sbagliu à l’arregistramentu di l’appiccicu.", "Invalid paste ID.": "N° di l’appiccicu inaccettevule.", "Paste is not of burn-after-reading type.": "L’appiccicu ùn hè micca di tipu « Squassà dopu a lettura ».", "Wrong deletion token. Paste was not deleted.": "Gettone di squassatura incurrettu. L’appiccicu ùn hè micca statu squassatu.", @@ -37,76 +37,76 @@ "%d seconds": [ "%d seconda", "%d seconde", - "%d seconds (2nd plural)", - "%d seconds (3rd plural)" + "%d seconde", + "%d seconde" ], "%d minutes": [ "%d minutu", "%d minuti", - "%d minutes (2nd plural)", - "%d minutes (3rd plural)" + "%d minuti", + "%d minuti" ], "%d hours": [ "%d ora", "%d ore", - "%d hours (2nd plural)", - "%d hours (3rd plural)" + "%d ore", + "%d ore" ], "%d days": [ "%d ghjornu", "%d ghjorni", - "%d days (2nd plural)", - "%d days (3rd plural)" + "%d ghjorni", + "%d ghjorni" ], "%d weeks": [ "%d settimana", "%d settimane", - "%d weeks (2nd plural)", - "%d weeks (3rd plural)" + "%d settimane", + "%d settimane" ], "%d months": [ "%d mese", "%d mesi", - "%d months (2nd plural)", - "%d months (3rd plural)" + "%d mesi", + "%d mesi" ], "%d years": [ "%d annu", "%d anni", - "%d years (2nd plural)", - "%d years (3rd plural)" + "%d anni", + "%d anni" ], "Never": "Mai", "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Nota : Què hè un serviziu di prova ; i dati ponu esse squassati à ogni mumentu. Parechji catorni anu da esse tombi s’è vò impiegate troppu stu serviziu.", "This document will expire in %d seconds.": [ "Stu ducumentu serà scadutu in %d seconda.", "Stu ducumentu serà scadutu in %d seconde.", - "This document will expire in %d seconds. (2nd plural)", - "This document will expire in %d seconds. (3rd plural)" + "Stu ducumentu serà scadutu in %d seconde.", + "Stu ducumentu serà scadutu in %d seconde." ], "This document will expire in %d minutes.": [ "Stu ducumentu serà scadutu in %d minutu.", "Stu ducumentu serà scadutu in %d minuti.", - "This document will expire in %d minutes. (2nd plural)", - "This document will expire in %d minutes. (3rd plural)" + "Stu ducumentu serà scadutu in %d minuti.", + "Stu ducumentu serà scadutu in %d minuti." ], "This document will expire in %d hours.": [ "Stu ducumentu serà scadutu in %d ora.", "Stu ducumentu serà scadutu in %d ore.", - "This document will expire in %d hours. (2nd plural)", - "This document will expire in %d hours. (3rd plural)" + "Stu ducumentu serà scadutu in %d ore.", + "Stu ducumentu serà scadutu in %d ore." ], "This document will expire in %d days.": [ "Stu ducumentu serà scadutu in %d ghjornu.", "Stu ducumentu serà scadutu in %d ghjorni.", - "This document will expire in %d days. (2nd plural)", - "This document will expire in %d days. (3rd plural)" + "Stu ducumentu serà scadutu in %d ghjorni.", + "Stu ducumentu serà scadutu in %d ghjorni." ], "This document will expire in %d months.": [ "Stu ducumentu serà scadutu in %d mese.", "Stu ducumentu serà scadutu in %d mesi.", - "This document will expire in %d months. (2nd plural)", - "This document will expire in %d months. (3rd plural)" + "Stu ducumentu serà scadutu in %d mesi.", + "Stu ducumentu serà scadutu in %d mesi." ], "Please enter the password for this paste:": "Stampittate a parolla d’intesa per st’appiccicu :", "Could not decrypt data (Wrong key?)": "Ùn si pò micca dicifrà i dati ; seria incurretta a chjave ?", @@ -126,7 +126,7 @@ "server error or not responding": "sbagliu di u servitore o u servitore ùn risponde micca", "Could not post comment: %s": "Ùn si pò micca impustà u cummentu : %s", "Sending paste…": "Inviu di l’appiccicu…", - "Your paste is %s (Hit [Ctrl]+[c] to copy)": "U vostru appiccicu si trova à l’indirizzu%s<\/a> (Appughjate [Ctrl]+[c] per cupià u liame)<\/span>", + "Your paste is %s (Hit [Ctrl]+[c] to copy)": "U vostru appiccicu si trova à l’indirizzu%s (Appughjate [Ctrl]+[c] per cupià u liame)", "Delete data": "Squassà i dati", "Could not create paste: %s": "Ùn si pò micca creà l’appiccicu : %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à l’appiccicu : A chjave di dicifratura hè assente in l’indirizzu. Averiate impiegatu un orientadore d’indirizzu o un riduttore chì ammuzzeghja una parte di l’indirizzu ?", @@ -149,7 +149,7 @@ "Attach a file": "Aghjunghje un schedariu", "alternatively drag & drop a file or paste an image from the clipboard": "in alternanza, sguillà è depone un schedariu o incullà una fiura da u preme’papei", "File too large, to display a preview. Please download the attachment.": "Schedariu troppu maiò per affissà una fighjulata. Scaricate a pezza aghjunta.", - "Remove attachment": "Caccià a pezza aghjunta.", + "Remove attachment": "Caccià a pezza aghjunta", "Your browser does not support uploading encrypted files. Please use a newer browser.": "U vostru navigatore ùn accetta micca l’inviu di i schedarii cifrati. Impiegate un navigatore più recente.", "Invalid attachment.": "A pezza aghjunta hè inaccettevule.", "Options": "Ozzioni", @@ -162,13 +162,13 @@ "Loading…": "Caricamentu…", "Decrypting paste…": "Dicifratura di l’appiccicu…", "Preparing new paste…": "Approntu di u novu appiccicu…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "S’è stu messaghju ùn smarisce micca, lighjite sta FAQ per ottene infurmazioni annant’à a risuluzione di i prublemi<\/a>.", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "S’è stu messaghju ùn smarisce micca, lighjite sta FAQ per ottene infurmazioni annant’à a risuluzione di i prublemi.", "+++ no paste text +++": "+++ nisunu testu incullatu +++", "Could not get paste data: %s": "Ùn si pò micca ottene i dati di l’appiccicu : %s", "QR code": "Codice QR", "This website is using an insecure HTTP connection! Please use it only for testing.": "Stu situ web impiegheghja una cunnessione HTTP non sicura ! impiegatelu solu per una prova.", - "For more information see this FAQ entry.": "Per sapene di più, lighjite sta rubrica di a FAQ<\/a>.", - "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "U vostru navigatore pò richiede una cunnessione HTTPS per permette l’usu di l’API WebCrypto. Pruvate di passà à HTTPS<\/a>.", + "For more information see this FAQ entry.": "Per sapene di più, lighjite sta rubrica di a FAQ.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "U vostru navigatore pò richiede una cunnessione HTTPS per permette l’usu di l’API WebCrypto. Pruvate di passà à HTTPS.", "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "U vostru navigatore ùn accetta micca WebAssembly, impiegatu per a cumpressione zlib. Pudete creà ducumenti micca cumpressi, ma ùn pudete micca leghje quelli chì sò cumpressi.", "waiting on user to provide a password": "in attesa di l’utilizatore per furnisce una parolla d’intesa", "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Ùn si pò micca dicifrà i dati. Avete stampittatu una parolla d’intesa incurretta ? Pruvate torna cù u buttone insù.", @@ -186,5 +186,5 @@ "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitate stu liame per vede a nota. Date l’indirizzu à qualunque li permette d’accede à a nota dinù.", "URL shortener may expose your decrypt key in URL.": "Un ammuzzatore d’indirizzu pò palisà a vostra chjave di dicifratura in l’indirizzu.", "Save paste": "Arregistrà l’appiccicu", - "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." + "Your IP is not authorized to create pastes.": "U vostru indirizzu IP ùn hè micca auturizatu à creà l’appiccichi." } From 6b001b5e4abba23fe26bd8d685c15bf4c5001b9d Mon Sep 17 00:00:00 2001 From: El RIDO Date: Mon, 28 Feb 2022 16:23:11 +0100 Subject: [PATCH 35/68] typo --- cfg/conf.sample.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index 005f83be..4d21dc3e 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -135,7 +135,7 @@ markdown = "Markdown" ; Set this to 0 to disable rate limiting. limit = 10 -; (optional) Set IPs adresses (v4 or v6) or subnets (CIDR) which are exempted +; (optional) Set IPs addresses (v4 or v6) or subnets (CIDR) which are exempted ; from the rate-limit. Invalid IPs will be ignored. If multiple values are to ; be exempted, the list needs to be comma separated. Leave unset to disable ; exemptions. From 3e028183358b0241301df1b9925a32ace6c23f66 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Mon, 28 Feb 2022 16:24:06 +0100 Subject: [PATCH 36/68] actually support the short CIDR notation --- lib/Persistence/TrafficLimiter.php | 5 ++++- tst/Persistence/TrafficLimiterTest.php | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index f071562b..93f91d23 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -146,7 +146,10 @@ class TrafficLimiter extends AbstractPersistence $ipRange = trim($ipRange); } $address = Factory::parseAddressString($_SERVER[self::$_ipKey]); - $range = Factory::parseRangeString($ipRange, ParseStringFlag::IPV4_MAYBE_NON_DECIMAL); + $range = Factory::parseRangeString( + $ipRange, + ParseStringFlag::IPV4_MAYBE_NON_DECIMAL | ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT | ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED + ); // address could not be parsed, we might not be in IP space and try a string comparison instead if (is_null($address)) { diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index a8b8040c..84e0bae8 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -57,7 +57,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase public function testTrafficLimitExempted() { - TrafficLimiter::setExempted('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); + TrafficLimiter::setExempted('1.2.3.4,10.10.10/24,2001:1620:2057::/48'); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $this->assertTrue(TrafficLimiter::canPass(), 'first request may pass'); try { @@ -85,7 +85,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase public function testTrafficLimitCreators() { - TrafficLimiter::setCreators('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); + TrafficLimiter::setCreators('1.2.3.4,10.10.10/24,2001:1620:2057::/48'); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; try { $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception'); From abaa9eca355cdfd8d24c177b5dc196e681acf1a1 Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Mon, 28 Feb 2022 17:27:03 +0100 Subject: [PATCH 37/68] New translations en.json (French) --- i18n/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/fr.json b/i18n/fr.json index b5767f70..2f3d491e 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -2,7 +2,7 @@ "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 est un 'pastebin' (ou gestionnaire d'extraits de texte et de code source) minimaliste et open source, dans lequel le serveur n'a aucune connaissance des données envoyées. Les données sont chiffrées/déchiffrées %sdans le navigateur%s par un chiffrement AES 256 bits.", "More information on the project page.": "Plus d'informations sur la page du projet.", - "Because ignorance is bliss": "Parce que l'ignorance c'est le bonheur", + "Because ignorance is bliss": "Vivons heureux, vivons cachés", "en": "fr", "Paste does not exist, has expired or has been deleted.": "Le paste n'existe pas, a expiré, ou a été supprimé.", "%s requires php %s or above to work. Sorry.": "Désolé, %s nécessite php %s ou supérieur pour fonctionner.", @@ -186,5 +186,5 @@ "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visiter ce lien pour voir la note. Donner l'URL à une autre personne lui permet également d'accéder à la note.", "URL shortener may expose your decrypt key in URL.": "Raccourcir l'URL peut exposer votre clé de déchiffrement dans l'URL.", "Save paste": "Sauver le paste", - "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." + "Your IP is not authorized to create pastes.": "Votre adresse IP n'est pas autorisée à créer des pastes." } From a58bba09585870a83e091fbdaf1840310d776656 Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Mon, 28 Feb 2022 17:27:04 +0100 Subject: [PATCH 38/68] New translations en.json (German) --- i18n/de.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/i18n/de.json b/i18n/de.json index c60c1563..a800f835 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -175,16 +175,16 @@ "Retry": "Wiederholen", "Showing raw text…": "Rohtext wird angezeigt…", "Notice:": "Hinweis:", - "This link will expire after %s.": "Dieser Link wird am %s ablaufen.", - "This link can only be accessed once, do not use back or refresh button in your browser.": "Dieser Link kann nur einmal geöffnet werden, verwende nicht den Zurück- oder Neu-laden-Knopf Deines Browsers.", - "Link:": "Link:", + "This link will expire after %s.": "Diese Verknüpfung wird am %s ablaufen.", + "This link can only be accessed once, do not use back or refresh button in your browser.": "Diese Verknüpfung kann nur einmal geöffnet werden, verwende nicht den Zurück- oder Neu-laden-Knopf Deines Browsers.", + "Link:": "Verknüpfung:", "Recipient may become aware of your timezone, convert time to UTC?": "Der Empfänger könnte Deine Zeitzone erfahren, möchtest Du die Zeit in UTC umwandeln?", "Use Current Timezone": "Aktuelle Zeitzone verwenden", "Convert To UTC": "In UTC umwandeln", "Close": "Schliessen", "Encrypted note on PrivateBin": "Verschlüsselte Notiz auf PrivateBin", - "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diesen Link um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.", + "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diese Verknüpfung um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.", "URL shortener may expose your decrypt key in URL.": "Der URL-Verkürzer kann den Schlüssel in der URL enthüllen.", "Save paste": "Text speichern", - "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." + "Your IP is not authorized to create pastes.": "Deine IP ist nicht berechtigt, Texte zu erstellen." } From e9d6996db4bdcae115fcf6d8716d6348005707e4 Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Mon, 28 Feb 2022 17:27:05 +0100 Subject: [PATCH 39/68] New translations en.json (Italian) --- i18n/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/it.json b/i18n/it.json index 8b9f391f..e00ce310 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -186,5 +186,5 @@ "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visita questo collegamento per vedere la nota. Dare l'URL a chiunque consente anche a loro di accedere alla nota.", "URL shortener may expose your decrypt key in URL.": "URL shortener può esporre la tua chiave decrittografata nell'URL.", "Save paste": "Salva il messagio", - "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." + "Your IP is not authorized to create pastes.": "Il tuo IP non è autorizzato a creare dei messaggi." } From cc60ab701bb5cd43c0f3d33c918ed6c0c383097f Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Mon, 28 Feb 2022 19:34:00 +0100 Subject: [PATCH 40/68] New translations en.json (German) --- i18n/de.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/i18n/de.json b/i18n/de.json index a800f835..42752f2f 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -175,15 +175,15 @@ "Retry": "Wiederholen", "Showing raw text…": "Rohtext wird angezeigt…", "Notice:": "Hinweis:", - "This link will expire after %s.": "Diese Verknüpfung wird am %s ablaufen.", - "This link can only be accessed once, do not use back or refresh button in your browser.": "Diese Verknüpfung kann nur einmal geöffnet werden, verwende nicht den Zurück- oder Neu-laden-Knopf Deines Browsers.", - "Link:": "Verknüpfung:", + "This link will expire after %s.": "Dieser Link wird am %s ablaufen.", + "This link can only be accessed once, do not use back or refresh button in your browser.": "Dieser Link kann nur einmal geöffnet werden, verwende nicht den Zurück- oder Neu-laden-Knopf Deines Browsers.", + "Link:": "Link:", "Recipient may become aware of your timezone, convert time to UTC?": "Der Empfänger könnte Deine Zeitzone erfahren, möchtest Du die Zeit in UTC umwandeln?", "Use Current Timezone": "Aktuelle Zeitzone verwenden", "Convert To UTC": "In UTC umwandeln", "Close": "Schliessen", "Encrypted note on PrivateBin": "Verschlüsselte Notiz auf PrivateBin", - "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diese Verknüpfung um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.", + "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diesen Link um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.", "URL shortener may expose your decrypt key in URL.": "Der URL-Verkürzer kann den Schlüssel in der URL enthüllen.", "Save paste": "Text speichern", "Your IP is not authorized to create pastes.": "Deine IP ist nicht berechtigt, Texte zu erstellen." From 6c1f0dde0c4cccd8e132d27842a3cba556648d02 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 13 Mar 2022 17:24:47 +0100 Subject: [PATCH 41/68] set CSP also as meta tag, to deal with misconfigured webservers mangling the HTTP header --- CHANGELOG.md | 1 + lib/Controller.php | 11 +++++++++++ tpl/bootstrap.php | 1 + tpl/page.php | 1 + tst/ViewTest.php | 1 + 5 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4ba1cde..ef114663 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * ADDED: Google Cloud Storage backend support (#795) * ADDED: Oracle database support (#868) * ADDED: Configuration option to limit paste creation and commenting to certain IPs (#883) + * ADDED: Set CSP also as meta tag, to deal with misconfigured webservers mangling the HTTP header * CHANGED: Language selection cookie only transmitted over HTTPS (#472) * CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.0 * CHANGED: Removed automatic `.ini` configuration file migration (#808) diff --git a/lib/Controller.php b/lib/Controller.php index 56f424c0..34087957 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -364,6 +364,16 @@ class Controller setcookie('lang', $languageselection, 0, '', '', true); } + // strip policies that are unsupported in meta tag + $metacspheader = str_replace( + array( + 'frame-ancestors \'none\'; ', + '; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads', + ), + '', + $this->_conf->getKey('cspheader') + ); + $page = new View; $page->assign('NAME', $this->_conf->getKey('name')); $page->assign('BASEPATH', I18n::_($this->_conf->getKey('basepath'))); @@ -392,6 +402,7 @@ class Controller $page->assign('HTTPWARNING', $this->_conf->getKey('httpwarning')); $page->assign('HTTPSLINK', 'https://' . $this->_request->getHost() . $this->_request->getRequestUri()); $page->assign('COMPRESSION', $this->_conf->getKey('compression')); + $page->assign('CSPHEADER', $metacspheader); $page->draw($this->_conf->getKey('template')); } diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 0808fff6..1fc8feb2 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -7,6 +7,7 @@ $isPage = substr($template, -5) === '-page'; + diff --git a/tpl/page.php b/tpl/page.php index b8c83805..74254c8e 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -4,6 +4,7 @@ use PrivateBin\I18n; + <?php echo I18n::_($NAME); ?> diff --git a/tst/ViewTest.php b/tst/ViewTest.php index c729b4ef..1e4d9374 100644 --- a/tst/ViewTest.php +++ b/tst/ViewTest.php @@ -60,6 +60,7 @@ class ViewTest extends PHPUnit_Framework_TestCase $page->assign('HTTPWARNING', true); $page->assign('HTTPSLINK', 'https://example.com/'); $page->assign('COMPRESSION', 'zlib'); + $page->assign('CSPHEADER', 'default-src \'none\''); $dir = dir(PATH . 'tpl'); while (false !== ($file = $dir->read())) { From 2a4d572c1e9eb9b608d32b0cc0cb3b6c3b684eab Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 13 Mar 2022 19:56:12 +0100 Subject: [PATCH 42/68] Sanitize SVG preview, preventing script execution in instance context, while dropping support for attachment download in IE --- CHANGELOG.md | 2 + js/privatebin.js | 118 +++++++++++++++++++++++++++++++++------------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 89 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef114663..1fb3ae22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,13 @@ * ADDED: Oracle database support (#868) * ADDED: Configuration option to limit paste creation and commenting to certain IPs (#883) * ADDED: Set CSP also as meta tag, to deal with misconfigured webservers mangling the HTTP header + * ADDED: Sanitize SVG preview, preventing script execution in instance context * CHANGED: Language selection cookie only transmitted over HTTPS (#472) * CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.0 * CHANGED: Removed automatic `.ini` configuration file migration (#808) * CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419) * CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419) + * CHANGED: Drop support for attachment download in IE * **1.3.5 (2021-04-05)** * ADDED: Translations for Hebrew, Lithuanian, Indonesian and Catalan * ADDED: Make the project info configurable (#681) diff --git a/js/privatebin.js b/js/privatebin.js index b6a3226e..53474bd1 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -52,6 +52,31 @@ jQuery.PrivateBin = (function($, RawDeflate) { */ let z; + /** + * DOMpurify settings for HTML content + * + * @private + */ + const purifyHtmlConfig = { + ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i, + SAFE_FOR_JQUERY: true, + USE_PROFILES: { + html: true + } + }; + + /** + * DOMpurify settings for SVG content + * + * @private + */ + const purifySvgConfig = { + USE_PROFILES: { + svg: true, + svgFilters: true + } + }; + /** * CryptoData class * @@ -409,7 +434,8 @@ jQuery.PrivateBin = (function($, RawDeflate) { element.html().replace( /(((https?|ftp):\/\/[\w?!=&.\/-;#@~%+*-]+(?![\w\s?!&.\/;#~%"=-]>))|((magnet):[\w?=&.\/-;#@~%+*-]+))/ig, '$1' - ) + ), + purifyHtmlConfig ) ); }; @@ -2536,7 +2562,8 @@ jQuery.PrivateBin = (function($, RawDeflate) { // let showdown convert the HTML and sanitize HTML *afterwards*! $plainText.html( DOMPurify.sanitize( - converter.makeHtml(text) + converter.makeHtml(text), + purifyHtmlConfig ) ); // add table classes from bootstrap css @@ -2752,6 +2779,34 @@ jQuery.PrivateBin = (function($, RawDeflate) { $dropzone; /** + * get blob URL from string data and mime type + * + * @name AttachmentViewer.getBlobUrl + * @private + * @function + * @param {string} data - raw data of attachment + * @param {string} data - mime type of attachment + * @return {string} objectURL + */ + function getBlobUrl(data, mimeType) + { + // Transform into a Blob + const buf = new Uint8Array(data.length); + for (let i = 0; i < data.length; ++i) { + buf[i] = data.charCodeAt(i); + } + const blob = new window.Blob( + [buf], + { + type: mimeType + } + ); + + // Get Blob URL + return window.URL.createObjectURL(blob); + } + + /** * sets the attachment but does not yet show it * * @name AttachmentViewer.setAttachment @@ -2761,44 +2816,39 @@ jQuery.PrivateBin = (function($, RawDeflate) { */ me.setAttachment = function(attachmentData, fileName) { - // data URI format: data:[][;base64], + // data URI format: data:[][;base64], // position in data URI string of where data begins const base64Start = attachmentData.indexOf(',') + 1; - // position in data URI string of where mediaType ends - const mediaTypeEnd = attachmentData.indexOf(';'); + // position in data URI string of where mimeType ends + const mimeTypeEnd = attachmentData.indexOf(';'); - // extract mediaType - const mediaType = attachmentData.substring(5, mediaTypeEnd); + // extract mimeType + const mimeType = attachmentData.substring(5, mimeTypeEnd); // extract data and convert to binary const rawData = attachmentData.substring(base64Start); const decodedData = rawData.length > 0 ? atob(rawData) : ''; - // Transform into a Blob - const buf = new Uint8Array(decodedData.length); - for (let i = 0; i < decodedData.length; ++i) { - buf[i] = decodedData.charCodeAt(i); - } - const blob = new window.Blob([ buf ], { type: mediaType }); - - // Get Blob URL - const blobUrl = window.URL.createObjectURL(blob); - - // IE does not support setting a data URI on an a element - // Using msSaveBlob to download - if (window.Blob && navigator.msSaveBlob) { - $attachmentLink.off('click').on('click', function () { - navigator.msSaveBlob(blob, fileName); - }); - } else { - $attachmentLink.attr('href', blobUrl); - } + let blobUrl = getBlobUrl(decodedData, mimeType); + $attachmentLink.attr('href', blobUrl); if (typeof fileName !== 'undefined') { $attachmentLink.attr('download', fileName); } - me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mediaType); + // sanitize SVG preview + // prevents executing embedded scripts when CSP is not set and user + // right-clicks/long-taps and opens the SVG in a new tab - prevented + // in the preview by use of an img tag, which disables scripts, too + if (mimeType.match(/image\/svg/i)) { + const sanitizedData = DOMPurify.sanitize( + decodedData, + purifySvgConfig + ); + blobUrl = getBlobUrl(sanitizedData, mimeType); + } + + me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mimeType); }; /** @@ -3665,7 +3715,14 @@ jQuery.PrivateBin = (function($, RawDeflate) { for (let i = 0; i < $head.length; ++i) { newDoc.write($head[i].outerHTML); } - newDoc.write('
' + DOMPurify.sanitize(Helper.htmlEntities(paste)) + '
'); + newDoc.write( + '
' +
+                DOMPurify.sanitize(
+                    Helper.htmlEntities(paste),
+                    purifyHtmlConfig
+                ) +
+                '
' + ); newDoc.close(); } @@ -5394,11 +5451,6 @@ jQuery.PrivateBin = (function($, RawDeflate) { // first load translations I18n.loadTranslations(); - DOMPurify.setConfig({ - ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i, - SAFE_FOR_JQUERY: true - }); - // Add a hook to make all links open a new window DOMPurify.addHook('afterSanitizeAttributes', function(node) { // set all elements owning target to target=_blank diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 1fc8feb2..4c688784 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -73,7 +73,7 @@ endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index 74254c8e..ca84e8f6 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -51,7 +51,7 @@ endif; ?> - + From 5617612eb304c3165a97f2f5986c535f7efbaf7a Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 13 Mar 2022 20:05:38 +0100 Subject: [PATCH 43/68] upgrade to showdown 2.0.3 --- CHANGELOG.md | 2 +- js/common.js | 2 +- js/showdown-2.0.0.js | 2 -- js/showdown-2.0.3.js | 2 ++ tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 js/showdown-2.0.0.js create mode 100644 js/showdown-2.0.3.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fb3ae22..2c6137f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * ADDED: Set CSP also as meta tag, to deal with misconfigured webservers mangling the HTTP header * ADDED: Sanitize SVG preview, preventing script execution in instance context * CHANGED: Language selection cookie only transmitted over HTTPS (#472) - * CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.0 + * CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.3 * CHANGED: Removed automatic `.ini` configuration file migration (#808) * CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419) * CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419) diff --git a/js/common.js b/js/common.js index 920e36a1..290c3996 100644 --- a/js/common.js +++ b/js/common.js @@ -16,7 +16,7 @@ global.zlib = require('./zlib-1.2.11').zlib; require('./prettify'); global.prettyPrint = window.PR.prettyPrint; global.prettyPrintOne = window.PR.prettyPrintOne; -global.showdown = require('./showdown-2.0.0'); +global.showdown = require('./showdown-2.0.3'); global.DOMPurify = require('./purify-2.3.6'); global.baseX = require('./base-x-4.0.0').baseX; global.Legacy = require('./legacy').Legacy; diff --git a/js/showdown-2.0.0.js b/js/showdown-2.0.0.js deleted file mode 100644 index 7c3840c2..00000000 --- a/js/showdown-2.0.0.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! showdown v 2.0.0 - 15-02-2022 */ -!function(){function a(e){"use strict";var r={omitExtraWLInCodeBlocks:{defaultValue:!1,describe:"Omit the default extra whiteline added to code blocks",type:"boolean"},noHeaderId:{defaultValue:!1,describe:"Turn on/off generated header id",type:"boolean"},prefixHeaderId:{defaultValue:!1,describe:"Add a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to true will add a generic 'section-' prefix",type:"string"},rawPrefixHeaderId:{defaultValue:!1,describe:'Setting this option to true will prevent showdown from modifying the prefix. This might result in malformed IDs (if, for instance, the " char is used in the prefix)',type:"boolean"},ghCompatibleHeaderId:{defaultValue:!1,describe:"Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)",type:"boolean"},rawHeaderId:{defaultValue:!1,describe:"Remove only spaces, ' and \" from generated header ids (including prefixes), replacing them with dashes (-). WARNING: This might result in malformed ids",type:"boolean"},headerLevelStart:{defaultValue:!1,describe:"The header blocks level start",type:"integer"},parseImgDimensions:{defaultValue:!1,describe:"Turn on/off image dimension parsing",type:"boolean"},simplifiedAutoLink:{defaultValue:!1,describe:"Turn on/off GFM autolink style",type:"boolean"},excludeTrailingPunctuationFromURLs:{defaultValue:!1,describe:"Excludes trailing punctuation from links generated with autoLinking",type:"boolean"},literalMidWordUnderscores:{defaultValue:!1,describe:"Parse midword underscores as literal underscores",type:"boolean"},literalMidWordAsterisks:{defaultValue:!1,describe:"Parse midword asterisks as literal asterisks",type:"boolean"},strikethrough:{defaultValue:!1,describe:"Turn on/off strikethrough support",type:"boolean"},tables:{defaultValue:!1,describe:"Turn on/off tables support",type:"boolean"},tablesHeaderId:{defaultValue:!1,describe:"Add an id to table headers",type:"boolean"},ghCodeBlocks:{defaultValue:!0,describe:"Turn on/off GFM fenced code blocks support",type:"boolean"},tasklists:{defaultValue:!1,describe:"Turn on/off GFM tasklist support",type:"boolean"},smoothLivePreview:{defaultValue:!1,describe:"Prevents weird effects in live previews due to incomplete input",type:"boolean"},smartIndentationFix:{defaultValue:!1,description:"Tries to smartly fix indentation in es6 strings",type:"boolean"},disableForced4SpacesIndentedSublists:{defaultValue:!1,description:"Disables the requirement of indenting nested sublists by 4 spaces",type:"boolean"},simpleLineBreaks:{defaultValue:!1,description:"Parses simple line breaks as
(GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,description:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,description:"Enables github @mentions",type:"boolean"},ghMentionsLink:{defaultValue:"https://github.com/{u}",description:"Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",type:"string"},encodeEmails:{defaultValue:!0,description:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"},openLinksInNewWindow:{defaultValue:!1,description:"Open all links in new windows",type:"boolean"},backslashEscapesHTMLTags:{defaultValue:!1,description:"Support for HTML Tag escaping. ex:
foo
",type:"boolean"},emoji:{defaultValue:!1,description:"Enable emoji support. Ex: `this is a :smile: emoji`",type:"boolean"},underline:{defaultValue:!1,description:"Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `` and ``",type:"boolean"},ellipsis:{defaultValue:!0,description:"Replaces three dots with the ellipsis unicode character",type:"boolean"},completeHTMLDocument:{defaultValue:!1,description:"Outputs a complete html document, including ``, `` and `` tags",type:"boolean"},metadata:{defaultValue:!1,description:"Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).",type:"boolean"},splitAdjacentBlockquotes:{defaultValue:!1,description:"Split adjacent blockquote blocks",type:"boolean"}};if(!1===e)return JSON.parse(JSON.stringify(r));var t,a={};for(t in r)r.hasOwnProperty(t)&&(a[t]=r[t].defaultValue);return a}var x={},t={},d={},p=a(!0),h="vanilla",_={github:{omitExtraWLInCodeBlocks:!0,simplifiedAutoLink:!0,excludeTrailingPunctuationFromURLs:!0,literalMidWordUnderscores:!0,strikethrough:!0,tables:!0,tablesHeaderId:!0,ghCodeBlocks:!0,tasklists:!0,disableForced4SpacesIndentedSublists:!0,simpleLineBreaks:!0,requireSpaceBeforeHeadingText:!0,ghCompatibleHeaderId:!0,ghMentions:!0,backslashEscapesHTMLTags:!0,emoji:!0,splitAdjacentBlockquotes:!0},original:{noHeaderId:!0,ghCodeBlocks:!1},ghost:{omitExtraWLInCodeBlocks:!0,parseImgDimensions:!0,simplifiedAutoLink:!0,excludeTrailingPunctuationFromURLs:!0,literalMidWordUnderscores:!0,strikethrough:!0,tables:!0,tablesHeaderId:!0,ghCodeBlocks:!0,tasklists:!0,smoothLivePreview:!0,simpleLineBreaks:!0,requireSpaceBeforeHeadingText:!0,ghMentions:!1,encodeEmails:!0},vanilla:a(!0),allOn:function(){"use strict";var e,r=a(!0),t={};for(e in r)r.hasOwnProperty(e)&&(t[e]=!0);return t}()};function g(e,r){"use strict";var t=r?"Error in "+r+" extension->":"Error in unnamed extension",a={valid:!0,error:""};x.helper.isArray(e)||(e=[e]);for(var n=0;n").replace(/&/g,"&")};function u(e,r,u,t){"use strict";var a,n,s,d=-1<(t=t||"").indexOf("g"),o=new RegExp(r+"|"+u,"g"+t.replace(/g/g,"")),p=new RegExp(r,t.replace(/g/g,"")),i=[];do{for(a=0;c=o.exec(e);)if(p.test(c[0]))a++||(s=(n=o.lastIndex)-c[0].length);else if(a&&!--a){var l=c.index+c[0].length,c={left:{start:s,end:n},match:{start:n,end:c.index},right:{start:c.index,end:l},wholeMatch:{start:s,end:l}};if(i.push(c),!d)return i}}while(a&&(o.lastIndex=n));return i}function s(u){"use strict";return function(e,r,t,a,c,n,s){var o=t=t.replace(x.helper.regexes.asteriskDashAndColon,x.helper.escapeCharactersCallback),i="",l="",r=r||"",s=s||"";return/^www\./i.test(t)&&(t=t.replace(/^www\./i,"http://www.")),u.excludeTrailingPunctuationFromURLs&&n&&(i=n),r+'"+o+""+i+s}}function o(n,s){"use strict";return function(e,r,t){var a="mailto:";return r=r||"",t=x.subParser("unescapeSpecialChars")(t,n,s),n.encodeEmails?(a=x.helper.encodeEmailAddress(a+t),t=x.helper.encodeEmailAddress(t)):a+=t,r+''+t+""}}x.helper.matchRecursiveRegExp=function(e,r,t,a){"use strict";for(var n=u(e,r,t,a),s=[],o=0;o>=0,t=String(t||" "),e.length>r?String(e):((r-=e.length)>t.length&&(t+=t.repeat(r/t.length)),String(e)+t.slice(0,r))},"undefined"==typeof console&&(console={warn:function(e){"use strict";alert(e)},log:function(e){"use strict";alert(e)},error:function(e){"use strict";throw e}}),x.helper.regexes={asteriskDashAndColon:/([*_:~])/g},x.helper.emojis={"+1":"👍","-1":"👎",100:"💯",1234:"🔢","1st_place_medal":"🥇","2nd_place_medal":"🥈","3rd_place_medal":"🥉","8ball":"🎱",a:"🅰️",ab:"🆎",abc:"🔤",abcd:"🔡",accept:"🉑",aerial_tramway:"🚡",airplane:"✈️",alarm_clock:"⏰",alembic:"⚗️",alien:"👽",ambulance:"🚑",amphora:"🏺",anchor:"⚓️",angel:"👼",anger:"💢",angry:"😠",anguished:"😧",ant:"🐜",apple:"🍎",aquarius:"♒️",aries:"♈️",arrow_backward:"◀️",arrow_double_down:"⏬",arrow_double_up:"⏫",arrow_down:"⬇️",arrow_down_small:"🔽",arrow_forward:"▶️",arrow_heading_down:"⤵️",arrow_heading_up:"⤴️",arrow_left:"⬅️",arrow_lower_left:"↙️",arrow_lower_right:"↘️",arrow_right:"➡️",arrow_right_hook:"↪️",arrow_up:"⬆️",arrow_up_down:"↕️",arrow_up_small:"🔼",arrow_upper_left:"↖️",arrow_upper_right:"↗️",arrows_clockwise:"🔃",arrows_counterclockwise:"🔄",art:"🎨",articulated_lorry:"🚛",artificial_satellite:"🛰",astonished:"😲",athletic_shoe:"👟",atm:"🏧",atom_symbol:"⚛️",avocado:"🥑",b:"🅱️",baby:"👶",baby_bottle:"🍼",baby_chick:"🐤",baby_symbol:"🚼",back:"🔙",bacon:"🥓",badminton:"🏸",baggage_claim:"🛄",baguette_bread:"🥖",balance_scale:"⚖️",balloon:"🎈",ballot_box:"🗳",ballot_box_with_check:"☑️",bamboo:"🎍",banana:"🍌",bangbang:"‼️",bank:"🏦",bar_chart:"📊",barber:"💈",baseball:"⚾️",basketball:"🏀",basketball_man:"⛹️",basketball_woman:"⛹️‍♀️",bat:"🦇",bath:"🛀",bathtub:"🛁",battery:"🔋",beach_umbrella:"🏖",bear:"🐻",bed:"🛏",bee:"🐝",beer:"🍺",beers:"🍻",beetle:"🐞",beginner:"🔰",bell:"🔔",bellhop_bell:"🛎",bento:"🍱",biking_man:"🚴",bike:"🚲",biking_woman:"🚴‍♀️",bikini:"👙",biohazard:"☣️",bird:"🐦",birthday:"🎂",black_circle:"⚫️",black_flag:"🏴",black_heart:"🖤",black_joker:"🃏",black_large_square:"⬛️",black_medium_small_square:"◾️",black_medium_square:"◼️",black_nib:"✒️",black_small_square:"▪️",black_square_button:"🔲",blonde_man:"👱",blonde_woman:"👱‍♀️",blossom:"🌼",blowfish:"🐡",blue_book:"📘",blue_car:"🚙",blue_heart:"💙",blush:"😊",boar:"🐗",boat:"⛵️",bomb:"💣",book:"📖",bookmark:"🔖",bookmark_tabs:"📑",books:"📚",boom:"💥",boot:"👢",bouquet:"💐",bowing_man:"🙇",bow_and_arrow:"🏹",bowing_woman:"🙇‍♀️",bowling:"🎳",boxing_glove:"🥊",boy:"👦",bread:"🍞",bride_with_veil:"👰",bridge_at_night:"🌉",briefcase:"💼",broken_heart:"💔",bug:"🐛",building_construction:"🏗",bulb:"💡",bullettrain_front:"🚅",bullettrain_side:"🚄",burrito:"🌯",bus:"🚌",business_suit_levitating:"🕴",busstop:"🚏",bust_in_silhouette:"👤",busts_in_silhouette:"👥",butterfly:"🦋",cactus:"🌵",cake:"🍰",calendar:"📆",call_me_hand:"🤙",calling:"📲",camel:"🐫",camera:"📷",camera_flash:"📸",camping:"🏕",cancer:"♋️",candle:"🕯",candy:"🍬",canoe:"🛶",capital_abcd:"🔠",capricorn:"♑️",car:"🚗",card_file_box:"🗃",card_index:"📇",card_index_dividers:"🗂",carousel_horse:"🎠",carrot:"🥕",cat:"🐱",cat2:"🐈",cd:"💿",chains:"⛓",champagne:"🍾",chart:"💹",chart_with_downwards_trend:"📉",chart_with_upwards_trend:"📈",checkered_flag:"🏁",cheese:"🧀",cherries:"🍒",cherry_blossom:"🌸",chestnut:"🌰",chicken:"🐔",children_crossing:"🚸",chipmunk:"🐿",chocolate_bar:"🍫",christmas_tree:"🎄",church:"⛪️",cinema:"🎦",circus_tent:"🎪",city_sunrise:"🌇",city_sunset:"🌆",cityscape:"🏙",cl:"🆑",clamp:"🗜",clap:"👏",clapper:"🎬",classical_building:"🏛",clinking_glasses:"🥂",clipboard:"📋",clock1:"🕐",clock10:"🕙",clock1030:"🕥",clock11:"🕚",clock1130:"🕦",clock12:"🕛",clock1230:"🕧",clock130:"🕜",clock2:"🕑",clock230:"🕝",clock3:"🕒",clock330:"🕞",clock4:"🕓",clock430:"🕟",clock5:"🕔",clock530:"🕠",clock6:"🕕",clock630:"🕡",clock7:"🕖",clock730:"🕢",clock8:"🕗",clock830:"🕣",clock9:"🕘",clock930:"🕤",closed_book:"📕",closed_lock_with_key:"🔐",closed_umbrella:"🌂",cloud:"☁️",cloud_with_lightning:"🌩",cloud_with_lightning_and_rain:"⛈",cloud_with_rain:"🌧",cloud_with_snow:"🌨",clown_face:"🤡",clubs:"♣️",cocktail:"🍸",coffee:"☕️",coffin:"⚰️",cold_sweat:"😰",comet:"☄️",computer:"💻",computer_mouse:"🖱",confetti_ball:"🎊",confounded:"😖",confused:"😕",congratulations:"㊗️",construction:"🚧",construction_worker_man:"👷",construction_worker_woman:"👷‍♀️",control_knobs:"🎛",convenience_store:"🏪",cookie:"🍪",cool:"🆒",policeman:"👮",copyright:"©️",corn:"🌽",couch_and_lamp:"🛋",couple:"👫",couple_with_heart_woman_man:"💑",couple_with_heart_man_man:"👨‍❤️‍👨",couple_with_heart_woman_woman:"👩‍❤️‍👩",couplekiss_man_man:"👨‍❤️‍💋‍👨",couplekiss_man_woman:"💏",couplekiss_woman_woman:"👩‍❤️‍💋‍👩",cow:"🐮",cow2:"🐄",cowboy_hat_face:"🤠",crab:"🦀",crayon:"🖍",credit_card:"💳",crescent_moon:"🌙",cricket:"🏏",crocodile:"🐊",croissant:"🥐",crossed_fingers:"🤞",crossed_flags:"🎌",crossed_swords:"⚔️",crown:"👑",cry:"😢",crying_cat_face:"😿",crystal_ball:"🔮",cucumber:"🥒",cupid:"💘",curly_loop:"➰",currency_exchange:"💱",curry:"🍛",custard:"🍮",customs:"🛃",cyclone:"🌀",dagger:"🗡",dancer:"💃",dancing_women:"👯",dancing_men:"👯‍♂️",dango:"🍡",dark_sunglasses:"🕶",dart:"🎯",dash:"💨",date:"📅",deciduous_tree:"🌳",deer:"🦌",department_store:"🏬",derelict_house:"🏚",desert:"🏜",desert_island:"🏝",desktop_computer:"🖥",male_detective:"🕵️",diamond_shape_with_a_dot_inside:"💠",diamonds:"♦️",disappointed:"😞",disappointed_relieved:"😥",dizzy:"💫",dizzy_face:"😵",do_not_litter:"🚯",dog:"🐶",dog2:"🐕",dollar:"💵",dolls:"🎎",dolphin:"🐬",door:"🚪",doughnut:"🍩",dove:"🕊",dragon:"🐉",dragon_face:"🐲",dress:"👗",dromedary_camel:"🐪",drooling_face:"🤤",droplet:"💧",drum:"🥁",duck:"🦆",dvd:"📀","e-mail":"📧",eagle:"🦅",ear:"👂",ear_of_rice:"🌾",earth_africa:"🌍",earth_americas:"🌎",earth_asia:"🌏",egg:"🥚",eggplant:"🍆",eight_pointed_black_star:"✴️",eight_spoked_asterisk:"✳️",electric_plug:"🔌",elephant:"🐘",email:"✉️",end:"🔚",envelope_with_arrow:"📩",euro:"💶",european_castle:"🏰",european_post_office:"🏤",evergreen_tree:"🌲",exclamation:"❗️",expressionless:"😑",eye:"👁",eye_speech_bubble:"👁‍🗨",eyeglasses:"👓",eyes:"👀",face_with_head_bandage:"🤕",face_with_thermometer:"🤒",fist_oncoming:"👊",factory:"🏭",fallen_leaf:"🍂",family_man_woman_boy:"👪",family_man_boy:"👨‍👦",family_man_boy_boy:"👨‍👦‍👦",family_man_girl:"👨‍👧",family_man_girl_boy:"👨‍👧‍👦",family_man_girl_girl:"👨‍👧‍👧",family_man_man_boy:"👨‍👨‍👦",family_man_man_boy_boy:"👨‍👨‍👦‍👦",family_man_man_girl:"👨‍👨‍👧",family_man_man_girl_boy:"👨‍👨‍👧‍👦",family_man_man_girl_girl:"👨‍👨‍👧‍👧",family_man_woman_boy_boy:"👨‍👩‍👦‍👦",family_man_woman_girl:"👨‍👩‍👧",family_man_woman_girl_boy:"👨‍👩‍👧‍👦",family_man_woman_girl_girl:"👨‍👩‍👧‍👧",family_woman_boy:"👩‍👦",family_woman_boy_boy:"👩‍👦‍👦",family_woman_girl:"👩‍👧",family_woman_girl_boy:"👩‍👧‍👦",family_woman_girl_girl:"👩‍👧‍👧",family_woman_woman_boy:"👩‍👩‍👦",family_woman_woman_boy_boy:"👩‍👩‍👦‍👦",family_woman_woman_girl:"👩‍👩‍👧",family_woman_woman_girl_boy:"👩‍👩‍👧‍👦",family_woman_woman_girl_girl:"👩‍👩‍👧‍👧",fast_forward:"⏩",fax:"📠",fearful:"😨",feet:"🐾",female_detective:"🕵️‍♀️",ferris_wheel:"🎡",ferry:"⛴",field_hockey:"🏑",file_cabinet:"🗄",file_folder:"📁",film_projector:"📽",film_strip:"🎞",fire:"🔥",fire_engine:"🚒",fireworks:"🎆",first_quarter_moon:"🌓",first_quarter_moon_with_face:"🌛",fish:"🐟",fish_cake:"🍥",fishing_pole_and_fish:"🎣",fist_raised:"✊",fist_left:"🤛",fist_right:"🤜",flags:"🎏",flashlight:"🔦",fleur_de_lis:"⚜️",flight_arrival:"🛬",flight_departure:"🛫",floppy_disk:"💾",flower_playing_cards:"🎴",flushed:"😳",fog:"🌫",foggy:"🌁",football:"🏈",footprints:"👣",fork_and_knife:"🍴",fountain:"⛲️",fountain_pen:"🖋",four_leaf_clover:"🍀",fox_face:"🦊",framed_picture:"🖼",free:"🆓",fried_egg:"🍳",fried_shrimp:"🍤",fries:"🍟",frog:"🐸",frowning:"😦",frowning_face:"☹️",frowning_man:"🙍‍♂️",frowning_woman:"🙍",middle_finger:"🖕",fuelpump:"⛽️",full_moon:"🌕",full_moon_with_face:"🌝",funeral_urn:"⚱️",game_die:"🎲",gear:"⚙️",gem:"💎",gemini:"♊️",ghost:"👻",gift:"🎁",gift_heart:"💝",girl:"👧",globe_with_meridians:"🌐",goal_net:"🥅",goat:"🐐",golf:"⛳️",golfing_man:"🏌️",golfing_woman:"🏌️‍♀️",gorilla:"🦍",grapes:"🍇",green_apple:"🍏",green_book:"📗",green_heart:"💚",green_salad:"🥗",grey_exclamation:"❕",grey_question:"❔",grimacing:"😬",grin:"😁",grinning:"😀",guardsman:"💂",guardswoman:"💂‍♀️",guitar:"🎸",gun:"🔫",haircut_woman:"💇",haircut_man:"💇‍♂️",hamburger:"🍔",hammer:"🔨",hammer_and_pick:"⚒",hammer_and_wrench:"🛠",hamster:"🐹",hand:"✋",handbag:"👜",handshake:"🤝",hankey:"💩",hatched_chick:"🐥",hatching_chick:"🐣",headphones:"🎧",hear_no_evil:"🙉",heart:"❤️",heart_decoration:"💟",heart_eyes:"😍",heart_eyes_cat:"😻",heartbeat:"💓",heartpulse:"💗",hearts:"♥️",heavy_check_mark:"✔️",heavy_division_sign:"➗",heavy_dollar_sign:"💲",heavy_heart_exclamation:"❣️",heavy_minus_sign:"➖",heavy_multiplication_x:"✖️",heavy_plus_sign:"➕",helicopter:"🚁",herb:"🌿",hibiscus:"🌺",high_brightness:"🔆",high_heel:"👠",hocho:"🔪",hole:"🕳",honey_pot:"🍯",horse:"🐴",horse_racing:"🏇",hospital:"🏥",hot_pepper:"🌶",hotdog:"🌭",hotel:"🏨",hotsprings:"♨️",hourglass:"⌛️",hourglass_flowing_sand:"⏳",house:"🏠",house_with_garden:"🏡",houses:"🏘",hugs:"🤗",hushed:"😯",ice_cream:"🍨",ice_hockey:"🏒",ice_skate:"⛸",icecream:"🍦",id:"🆔",ideograph_advantage:"🉐",imp:"👿",inbox_tray:"📥",incoming_envelope:"📨",tipping_hand_woman:"💁",information_source:"ℹ️",innocent:"😇",interrobang:"⁉️",iphone:"📱",izakaya_lantern:"🏮",jack_o_lantern:"🎃",japan:"🗾",japanese_castle:"🏯",japanese_goblin:"👺",japanese_ogre:"👹",jeans:"👖",joy:"😂",joy_cat:"😹",joystick:"🕹",kaaba:"🕋",key:"🔑",keyboard:"⌨️",keycap_ten:"🔟",kick_scooter:"🛴",kimono:"👘",kiss:"💋",kissing:"😗",kissing_cat:"😽",kissing_closed_eyes:"😚",kissing_heart:"😘",kissing_smiling_eyes:"😙",kiwi_fruit:"🥝",koala:"🐨",koko:"🈁",label:"🏷",large_blue_circle:"🔵",large_blue_diamond:"🔷",large_orange_diamond:"🔶",last_quarter_moon:"🌗",last_quarter_moon_with_face:"🌜",latin_cross:"✝️",laughing:"😆",leaves:"🍃",ledger:"📒",left_luggage:"🛅",left_right_arrow:"↔️",leftwards_arrow_with_hook:"↩️",lemon:"🍋",leo:"♌️",leopard:"🐆",level_slider:"🎚",libra:"♎️",light_rail:"🚈",link:"🔗",lion:"🦁",lips:"👄",lipstick:"💄",lizard:"🦎",lock:"🔒",lock_with_ink_pen:"🔏",lollipop:"🍭",loop:"➿",loud_sound:"🔊",loudspeaker:"📢",love_hotel:"🏩",love_letter:"💌",low_brightness:"🔅",lying_face:"🤥",m:"Ⓜ️",mag:"🔍",mag_right:"🔎",mahjong:"🀄️",mailbox:"📫",mailbox_closed:"📪",mailbox_with_mail:"📬",mailbox_with_no_mail:"📭",man:"👨",man_artist:"👨‍🎨",man_astronaut:"👨‍🚀",man_cartwheeling:"🤸‍♂️",man_cook:"👨‍🍳",man_dancing:"🕺",man_facepalming:"🤦‍♂️",man_factory_worker:"👨‍🏭",man_farmer:"👨‍🌾",man_firefighter:"👨‍🚒",man_health_worker:"👨‍⚕️",man_in_tuxedo:"🤵",man_judge:"👨‍⚖️",man_juggling:"🤹‍♂️",man_mechanic:"👨‍🔧",man_office_worker:"👨‍💼",man_pilot:"👨‍✈️",man_playing_handball:"🤾‍♂️",man_playing_water_polo:"🤽‍♂️",man_scientist:"👨‍🔬",man_shrugging:"🤷‍♂️",man_singer:"👨‍🎤",man_student:"👨‍🎓",man_teacher:"👨‍🏫",man_technologist:"👨‍💻",man_with_gua_pi_mao:"👲",man_with_turban:"👳",tangerine:"🍊",mans_shoe:"👞",mantelpiece_clock:"🕰",maple_leaf:"🍁",martial_arts_uniform:"🥋",mask:"😷",massage_woman:"💆",massage_man:"💆‍♂️",meat_on_bone:"🍖",medal_military:"🎖",medal_sports:"🏅",mega:"📣",melon:"🍈",memo:"📝",men_wrestling:"🤼‍♂️",menorah:"🕎",mens:"🚹",metal:"🤘",metro:"🚇",microphone:"🎤",microscope:"🔬",milk_glass:"🥛",milky_way:"🌌",minibus:"🚐",minidisc:"💽",mobile_phone_off:"📴",money_mouth_face:"🤑",money_with_wings:"💸",moneybag:"💰",monkey:"🐒",monkey_face:"🐵",monorail:"🚝",moon:"🌔",mortar_board:"🎓",mosque:"🕌",motor_boat:"🛥",motor_scooter:"🛵",motorcycle:"🏍",motorway:"🛣",mount_fuji:"🗻",mountain:"⛰",mountain_biking_man:"🚵",mountain_biking_woman:"🚵‍♀️",mountain_cableway:"🚠",mountain_railway:"🚞",mountain_snow:"🏔",mouse:"🐭",mouse2:"🐁",movie_camera:"🎥",moyai:"🗿",mrs_claus:"🤶",muscle:"💪",mushroom:"🍄",musical_keyboard:"🎹",musical_note:"🎵",musical_score:"🎼",mute:"🔇",nail_care:"💅",name_badge:"📛",national_park:"🏞",nauseated_face:"🤢",necktie:"👔",negative_squared_cross_mark:"❎",nerd_face:"🤓",neutral_face:"😐",new:"🆕",new_moon:"🌑",new_moon_with_face:"🌚",newspaper:"📰",newspaper_roll:"🗞",next_track_button:"⏭",ng:"🆖",no_good_man:"🙅‍♂️",no_good_woman:"🙅",night_with_stars:"🌃",no_bell:"🔕",no_bicycles:"🚳",no_entry:"⛔️",no_entry_sign:"🚫",no_mobile_phones:"📵",no_mouth:"😶",no_pedestrians:"🚷",no_smoking:"🚭","non-potable_water":"🚱",nose:"👃",notebook:"📓",notebook_with_decorative_cover:"📔",notes:"🎶",nut_and_bolt:"🔩",o:"⭕️",o2:"🅾️",ocean:"🌊",octopus:"🐙",oden:"🍢",office:"🏢",oil_drum:"🛢",ok:"🆗",ok_hand:"👌",ok_man:"🙆‍♂️",ok_woman:"🙆",old_key:"🗝",older_man:"👴",older_woman:"👵",om:"🕉",on:"🔛",oncoming_automobile:"🚘",oncoming_bus:"🚍",oncoming_police_car:"🚔",oncoming_taxi:"🚖",open_file_folder:"📂",open_hands:"👐",open_mouth:"😮",open_umbrella:"☂️",ophiuchus:"⛎",orange_book:"📙",orthodox_cross:"☦️",outbox_tray:"📤",owl:"🦉",ox:"🐂",package:"📦",page_facing_up:"📄",page_with_curl:"📃",pager:"📟",paintbrush:"🖌",palm_tree:"🌴",pancakes:"🥞",panda_face:"🐼",paperclip:"📎",paperclips:"🖇",parasol_on_ground:"⛱",parking:"🅿️",part_alternation_mark:"〽️",partly_sunny:"⛅️",passenger_ship:"🛳",passport_control:"🛂",pause_button:"⏸",peace_symbol:"☮️",peach:"🍑",peanuts:"🥜",pear:"🍐",pen:"🖊",pencil2:"✏️",penguin:"🐧",pensive:"😔",performing_arts:"🎭",persevere:"😣",person_fencing:"🤺",pouting_woman:"🙎",phone:"☎️",pick:"⛏",pig:"🐷",pig2:"🐖",pig_nose:"🐽",pill:"💊",pineapple:"🍍",ping_pong:"🏓",pisces:"♓️",pizza:"🍕",place_of_worship:"🛐",plate_with_cutlery:"🍽",play_or_pause_button:"⏯",point_down:"👇",point_left:"👈",point_right:"👉",point_up:"☝️",point_up_2:"👆",police_car:"🚓",policewoman:"👮‍♀️",poodle:"🐩",popcorn:"🍿",post_office:"🏣",postal_horn:"📯",postbox:"📮",potable_water:"🚰",potato:"🥔",pouch:"👝",poultry_leg:"🍗",pound:"💷",rage:"😡",pouting_cat:"😾",pouting_man:"🙎‍♂️",pray:"🙏",prayer_beads:"📿",pregnant_woman:"🤰",previous_track_button:"⏮",prince:"🤴",princess:"👸",printer:"🖨",purple_heart:"💜",purse:"👛",pushpin:"📌",put_litter_in_its_place:"🚮",question:"❓",rabbit:"🐰",rabbit2:"🐇",racehorse:"🐎",racing_car:"🏎",radio:"📻",radio_button:"🔘",radioactive:"☢️",railway_car:"🚃",railway_track:"🛤",rainbow:"🌈",rainbow_flag:"🏳️‍🌈",raised_back_of_hand:"🤚",raised_hand_with_fingers_splayed:"🖐",raised_hands:"🙌",raising_hand_woman:"🙋",raising_hand_man:"🙋‍♂️",ram:"🐏",ramen:"🍜",rat:"🐀",record_button:"⏺",recycle:"♻️",red_circle:"🔴",registered:"®️",relaxed:"☺️",relieved:"😌",reminder_ribbon:"🎗",repeat:"🔁",repeat_one:"🔂",rescue_worker_helmet:"⛑",restroom:"🚻",revolving_hearts:"💞",rewind:"⏪",rhinoceros:"🦏",ribbon:"🎀",rice:"🍚",rice_ball:"🍙",rice_cracker:"🍘",rice_scene:"🎑",right_anger_bubble:"🗯",ring:"💍",robot:"🤖",rocket:"🚀",rofl:"🤣",roll_eyes:"🙄",roller_coaster:"🎢",rooster:"🐓",rose:"🌹",rosette:"🏵",rotating_light:"🚨",round_pushpin:"📍",rowing_man:"🚣",rowing_woman:"🚣‍♀️",rugby_football:"🏉",running_man:"🏃",running_shirt_with_sash:"🎽",running_woman:"🏃‍♀️",sa:"🈂️",sagittarius:"♐️",sake:"🍶",sandal:"👡",santa:"🎅",satellite:"📡",saxophone:"🎷",school:"🏫",school_satchel:"🎒",scissors:"✂️",scorpion:"🦂",scorpius:"♏️",scream:"😱",scream_cat:"🙀",scroll:"📜",seat:"💺",secret:"㊙️",see_no_evil:"🙈",seedling:"🌱",selfie:"🤳",shallow_pan_of_food:"🥘",shamrock:"☘️",shark:"🦈",shaved_ice:"🍧",sheep:"🐑",shell:"🐚",shield:"🛡",shinto_shrine:"⛩",ship:"🚢",shirt:"👕",shopping:"🛍",shopping_cart:"🛒",shower:"🚿",shrimp:"🦐",signal_strength:"📶",six_pointed_star:"🔯",ski:"🎿",skier:"⛷",skull:"💀",skull_and_crossbones:"☠️",sleeping:"😴",sleeping_bed:"🛌",sleepy:"😪",slightly_frowning_face:"🙁",slightly_smiling_face:"🙂",slot_machine:"🎰",small_airplane:"🛩",small_blue_diamond:"🔹",small_orange_diamond:"🔸",small_red_triangle:"🔺",small_red_triangle_down:"🔻",smile:"😄",smile_cat:"😸",smiley:"😃",smiley_cat:"😺",smiling_imp:"😈",smirk:"😏",smirk_cat:"😼",smoking:"🚬",snail:"🐌",snake:"🐍",sneezing_face:"🤧",snowboarder:"🏂",snowflake:"❄️",snowman:"⛄️",snowman_with_snow:"☃️",sob:"😭",soccer:"⚽️",soon:"🔜",sos:"🆘",sound:"🔉",space_invader:"👾",spades:"♠️",spaghetti:"🍝",sparkle:"❇️",sparkler:"🎇",sparkles:"✨",sparkling_heart:"💖",speak_no_evil:"🙊",speaker:"🔈",speaking_head:"🗣",speech_balloon:"💬",speedboat:"🚤",spider:"🕷",spider_web:"🕸",spiral_calendar:"🗓",spiral_notepad:"🗒",spoon:"🥄",squid:"🦑",stadium:"🏟",star:"⭐️",star2:"🌟",star_and_crescent:"☪️",star_of_david:"✡️",stars:"🌠",station:"🚉",statue_of_liberty:"🗽",steam_locomotive:"🚂",stew:"🍲",stop_button:"⏹",stop_sign:"🛑",stopwatch:"⏱",straight_ruler:"📏",strawberry:"🍓",stuck_out_tongue:"😛",stuck_out_tongue_closed_eyes:"😝",stuck_out_tongue_winking_eye:"😜",studio_microphone:"🎙",stuffed_flatbread:"🥙",sun_behind_large_cloud:"🌥",sun_behind_rain_cloud:"🌦",sun_behind_small_cloud:"🌤",sun_with_face:"🌞",sunflower:"🌻",sunglasses:"😎",sunny:"☀️",sunrise:"🌅",sunrise_over_mountains:"🌄",surfing_man:"🏄",surfing_woman:"🏄‍♀️",sushi:"🍣",suspension_railway:"🚟",sweat:"😓",sweat_drops:"💦",sweat_smile:"😅",sweet_potato:"🍠",swimming_man:"🏊",swimming_woman:"🏊‍♀️",symbols:"🔣",synagogue:"🕍",syringe:"💉",taco:"🌮",tada:"🎉",tanabata_tree:"🎋",taurus:"♉️",taxi:"🚕",tea:"🍵",telephone_receiver:"📞",telescope:"🔭",tennis:"🎾",tent:"⛺️",thermometer:"🌡",thinking:"🤔",thought_balloon:"💭",ticket:"🎫",tickets:"🎟",tiger:"🐯",tiger2:"🐅",timer_clock:"⏲",tipping_hand_man:"💁‍♂️",tired_face:"😫",tm:"™️",toilet:"🚽",tokyo_tower:"🗼",tomato:"🍅",tongue:"👅",top:"🔝",tophat:"🎩",tornado:"🌪",trackball:"🖲",tractor:"🚜",traffic_light:"🚥",train:"🚋",train2:"🚆",tram:"🚊",triangular_flag_on_post:"🚩",triangular_ruler:"📐",trident:"🔱",triumph:"😤",trolleybus:"🚎",trophy:"🏆",tropical_drink:"🍹",tropical_fish:"🐠",truck:"🚚",trumpet:"🎺",tulip:"🌷",tumbler_glass:"🥃",turkey:"🦃",turtle:"🐢",tv:"📺",twisted_rightwards_arrows:"🔀",two_hearts:"💕",two_men_holding_hands:"👬",two_women_holding_hands:"👭",u5272:"🈹",u5408:"🈴",u55b6:"🈺",u6307:"🈯️",u6708:"🈷️",u6709:"🈶",u6e80:"🈵",u7121:"🈚️",u7533:"🈸",u7981:"🈲",u7a7a:"🈳",umbrella:"☔️",unamused:"😒",underage:"🔞",unicorn:"🦄",unlock:"🔓",up:"🆙",upside_down_face:"🙃",v:"✌️",vertical_traffic_light:"🚦",vhs:"📼",vibration_mode:"📳",video_camera:"📹",video_game:"🎮",violin:"🎻",virgo:"♍️",volcano:"🌋",volleyball:"🏐",vs:"🆚",vulcan_salute:"🖖",walking_man:"🚶",walking_woman:"🚶‍♀️",waning_crescent_moon:"🌘",waning_gibbous_moon:"🌖",warning:"⚠️",wastebasket:"🗑",watch:"⌚️",water_buffalo:"🐃",watermelon:"🍉",wave:"👋",wavy_dash:"〰️",waxing_crescent_moon:"🌒",wc:"🚾",weary:"😩",wedding:"💒",weight_lifting_man:"🏋️",weight_lifting_woman:"🏋️‍♀️",whale:"🐳",whale2:"🐋",wheel_of_dharma:"☸️",wheelchair:"♿️",white_check_mark:"✅",white_circle:"⚪️",white_flag:"🏳️",white_flower:"💮",white_large_square:"⬜️",white_medium_small_square:"◽️",white_medium_square:"◻️",white_small_square:"▫️",white_square_button:"🔳",wilted_flower:"🥀",wind_chime:"🎐",wind_face:"🌬",wine_glass:"🍷",wink:"😉",wolf:"🐺",woman:"👩",woman_artist:"👩‍🎨",woman_astronaut:"👩‍🚀",woman_cartwheeling:"🤸‍♀️",woman_cook:"👩‍🍳",woman_facepalming:"🤦‍♀️",woman_factory_worker:"👩‍🏭",woman_farmer:"👩‍🌾",woman_firefighter:"👩‍🚒",woman_health_worker:"👩‍⚕️",woman_judge:"👩‍⚖️",woman_juggling:"🤹‍♀️",woman_mechanic:"👩‍🔧",woman_office_worker:"👩‍💼",woman_pilot:"👩‍✈️",woman_playing_handball:"🤾‍♀️",woman_playing_water_polo:"🤽‍♀️",woman_scientist:"👩‍🔬",woman_shrugging:"🤷‍♀️",woman_singer:"👩‍🎤",woman_student:"👩‍🎓",woman_teacher:"👩‍🏫",woman_technologist:"👩‍💻",woman_with_turban:"👳‍♀️",womans_clothes:"👚",womans_hat:"👒",women_wrestling:"🤼‍♀️",womens:"🚺",world_map:"🗺",worried:"😟",wrench:"🔧",writing_hand:"✍️",x:"❌",yellow_heart:"💛",yen:"💴",yin_yang:"☯️",yum:"😋",zap:"⚡️",zipper_mouth_face:"🤐",zzz:"💤",octocat:':octocat:',showdown:"S"},x.Converter=function(e){"use strict";var r,t,n={},i=[],l=[],o={},a=h,s={parsed:{},raw:"",format:""};for(r in e=e||{},p)p.hasOwnProperty(r)&&(n[r]=p[r]);if("object"!=typeof e)throw Error("Converter expects the passed parameter to be an object, but "+typeof e+" was passed instead.");for(t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);function c(e,r){if(r=r||null,x.helper.isString(e)){if(r=e=x.helper.stdExtName(e),x.extensions[e]){console.warn("DEPRECATION WARNING: "+e+" is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!");var t=x.extensions[e],a=e;if("function"==typeof t&&(t=t(new x.Converter)),x.helper.isArray(t)||(t=[t]),!(a=g(t,a)).valid)throw Error(a.error);for(var n=0;n[ \t]+¨NBSP;<"),!r){if(!window||!window.document)throw new Error("HTMLParser is undefined. If in a webworker or nodejs environment, you need to provide a WHATWG DOM and HTML such as JSDOM");r=window.document}for(var r=r.createElement("div"),t=(r.innerHTML=e,{preList:function(e){for(var r=e.querySelectorAll("pre"),t=[],a=0;a'}else t.push(r[a].innerHTML),r[a].innerHTML="",r[a].setAttribute("prenum",a.toString());return t}(r)}),a=(!function e(r){for(var t=0;t? ?(['"].*['"])?\)$/m))a="";else if(!a){if(a="#"+(t=t||r.toLowerCase().replace(/ ?\n/g," ")),x.helper.isUndefined(l.gUrls[t]))return e;a=l.gUrls[t],x.helper.isUndefined(l.gTitles[t])||(o=l.gTitles[t])}return e='"}return e=(e=(e=(e=(e=l.converter._dispatch("anchors.before",e,i,l)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g,r)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,r)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]??(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,r)).replace(/\[([^\[\]]+)]()()()()()/g,r),i.ghMentions&&(e=e.replace(/(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d.-]+?[a-z\d]+)*))/gim,function(e,r,t,a,n){if("\\"===t)return r+a;if(!x.helper.isString(i.ghMentionsLink))throw new Error("ghMentionsLink option must be a string");t="";return r+'"+a+""})),e=l.converter._dispatch("anchors.after",e,i,l)});var i=/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+?\.[^'">\s]+?)()(\1)?(?=\s|$)(?!["<>])/gi,l=/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]])?(\1)?(?=\s|$)(?!["<>])/gi,c=/()<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>()/gi,m=/(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gim,f=/<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;x.subParser("autoLinks",function(e,r,t){"use strict";return e=(e=(e=t.converter._dispatch("autoLinks.before",e,r,t)).replace(c,s(r))).replace(f,o(r,t)),e=t.converter._dispatch("autoLinks.after",e,r,t)}),x.subParser("simplifiedAutoLinks",function(e,r,t){"use strict";return r.simplifiedAutoLink?(e=t.converter._dispatch("simplifiedAutoLinks.before",e,r,t),e=(e=r.excludeTrailingPunctuationFromURLs?e.replace(l,s(r)):e.replace(i,s(r))).replace(m,o(r,t)),t.converter._dispatch("simplifiedAutoLinks.after",e,r,t)):e}),x.subParser("blockGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("blockGamut.before",e,r,t),e=x.subParser("blockQuotes")(e,r,t),e=x.subParser("headers")(e,r,t),e=x.subParser("horizontalRule")(e,r,t),e=x.subParser("lists")(e,r,t),e=x.subParser("codeBlocks")(e,r,t),e=x.subParser("tables")(e,r,t),e=x.subParser("hashHTMLBlocks")(e,r,t),e=x.subParser("paragraphs")(e,r,t),e=t.converter._dispatch("blockGamut.after",e,r,t)}),x.subParser("blockQuotes",function(e,r,t){"use strict";e=t.converter._dispatch("blockQuotes.before",e,r,t);var a=/(^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+/gm;return r.splitAdjacentBlockquotes&&(a=/^ {0,3}>[\s\S]*?(?:\n\n)/gm),e=(e+="\n\n").replace(a,function(e){return e=(e=(e=e.replace(/^[ \t]*>[ \t]?/gm,"")).replace(/¨0/g,"")).replace(/^[ \t]+$/gm,""),e=x.subParser("githubCodeBlocks")(e,r,t),e=(e=(e=x.subParser("blockGamut")(e,r,t)).replace(/(^|\n)/g,"$1 ")).replace(/(\s*
[^\r]+?<\/pre>)/gm,function(e,r){return r.replace(/^  /gm,"¨0").replace(/¨0/g,"")}),x.subParser("hashBlock")("
\n"+e+"\n
",r,t)}),e=t.converter._dispatch("blockQuotes.after",e,r,t)}),x.subParser("codeBlocks",function(e,n,s){"use strict";e=s.converter._dispatch("codeBlocks.before",e,n,s);return e=(e=(e+="¨0").replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g,function(e,r,t){var a="\n",r=x.subParser("outdent")(r,n,s);return r=x.subParser("encodeCode")(r,n,s),r="
"+(r=(r=(r=x.subParser("detab")(r,n,s)).replace(/^\n+/g,"")).replace(/\n+$/g,""))+(a=n.omitExtraWLInCodeBlocks?"":a)+"
",x.subParser("hashBlock")(r,n,s)+t})).replace(/¨0/,""),e=s.converter._dispatch("codeBlocks.after",e,n,s)}),x.subParser("codeSpans",function(e,n,s){"use strict";return e=(e=void 0===(e=s.converter._dispatch("codeSpans.before",e,n,s))?"":e).replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(e,r,t,a){return a=(a=a.replace(/^([ \t]*)/g,"")).replace(/[ \t]*$/g,""),a=r+""+(a=x.subParser("encodeCode")(a,n,s))+"",a=x.subParser("hashHTMLSpans")(a,n,s)}),e=s.converter._dispatch("codeSpans.after",e,n,s)}),x.subParser("completeHTMLDocument",function(e,r,t){"use strict";if(!r.completeHTMLDocument)return e;e=t.converter._dispatch("completeHTMLDocument.before",e,r,t);var a,n="html",s="\n",o="",i='\n',l="",c="";for(a in void 0!==t.metadata.parsed.doctype&&(s="\n","html"!==(n=t.metadata.parsed.doctype.toString().toLowerCase())&&"html5"!==n||(i='')),t.metadata.parsed)if(t.metadata.parsed.hasOwnProperty(a))switch(a.toLowerCase()){case"doctype":break;case"title":o=""+t.metadata.parsed.title+"\n";break;case"charset":i="html"===n||"html5"===n?'\n':'\n';break;case"language":case"lang":l=' lang="'+t.metadata.parsed[a]+'"',c+='\n';break;default:c+='\n'}return e=s+"\n\n"+o+i+c+"\n\n"+e.trim()+"\n\n",e=t.converter._dispatch("completeHTMLDocument.after",e,r,t)}),x.subParser("detab",function(e,r,t){"use strict";return e=(e=(e=(e=(e=(e=t.converter._dispatch("detab.before",e,r,t)).replace(/\t(?=\t)/g," ")).replace(/\t/g,"¨A¨B")).replace(/¨B(.+?)¨A/g,function(e,r){for(var t=r,a=4-t.length%4,n=0;n/g,">"),e=t.converter._dispatch("encodeAmpsAndAngles.after",e,r,t)}),x.subParser("encodeBackslashEscapes",function(e,r,t){"use strict";return e=(e=(e=t.converter._dispatch("encodeBackslashEscapes.before",e,r,t)).replace(/\\(\\)/g,x.helper.escapeCharactersCallback)).replace(/\\([`*_{}\[\]()>#+.!~=|:-])/g,x.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeBackslashEscapes.after",e,r,t)}),x.subParser("encodeCode",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("encodeCode.before",e,r,t)).replace(/&/g,"&").replace(//g,">").replace(/([*_{}\[\]\\=~-])/g,x.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeCode.after",e,r,t)}),x.subParser("escapeSpecialCharsWithinTagAttributes",function(e,r,t){"use strict";return e=(e=(e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.before",e,r,t)).replace(/<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,function(e){return e.replace(/(.)<\/?code>(?=.)/g,"$1`").replace(/([\\`*_~=|])/g,x.helper.escapeCharactersCallback)})).replace(/-]|-[^>])(?:[^-]|-[^-])*)--)>/gi,function(e){return e.replace(/([\\`*_~=|])/g,x.helper.escapeCharactersCallback)}),e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.after",e,r,t)}),x.subParser("githubCodeBlocks",function(e,s,o){"use strict";return s.ghCodeBlocks?(e=o.converter._dispatch("githubCodeBlocks.before",e,s,o),e=(e=(e+="¨0").replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g,function(e,r,t,a){var n=s.omitExtraWLInCodeBlocks?"":"\n";return a=x.subParser("encodeCode")(a,s,o),a="
"+(a=(a=(a=x.subParser("detab")(a,s,o)).replace(/^\n+/g,"")).replace(/\n+$/g,""))+n+"
",a=x.subParser("hashBlock")(a,s,o),"\n\n¨G"+(o.ghCodeBlocks.push({text:e,codeblock:a})-1)+"G\n\n"})).replace(/¨0/,""),o.converter._dispatch("githubCodeBlocks.after",e,s,o)):e}),x.subParser("hashBlock",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("hashBlock.before",e,r,t)).replace(/(^\n+|\n+$)/g,""),e="\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n",e=t.converter._dispatch("hashBlock.after",e,r,t)}),x.subParser("hashCodeTags",function(e,n,s){"use strict";e=s.converter._dispatch("hashCodeTags.before",e,n,s);return e=x.helper.replaceRecursiveRegExp(e,function(e,r,t,a){t=t+x.subParser("encodeCode")(r,n,s)+a;return"¨C"+(s.gHtmlSpans.push(t)-1)+"C"},"]*>","","gim"),e=s.converter._dispatch("hashCodeTags.after",e,n,s)}),x.subParser("hashElement",function(e,r,t){"use strict";return function(e,r){return r=(r=(r=r.replace(/\n\n/g,"\n")).replace(/^\n/,"")).replace(/\n+$/g,""),r="\n\n¨K"+(t.gHtmlBlocks.push(r)-1)+"K\n\n"}}),x.subParser("hashHTMLBlocks",function(e,r,n){"use strict";e=n.converter._dispatch("hashHTMLBlocks.before",e,r,n);function t(e,r,t,a){return-1!==t.search(/\bmarkdown\b/)&&(e=t+n.converter.makeHtml(r)+a),"\n\n¨K"+(n.gHtmlBlocks.push(e)-1)+"K\n\n"}var a=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"];r.backslashEscapesHTMLTags&&(e=e.replace(/\\<(\/?[^>]+?)>/g,function(e,r){return"<"+r+">"}));for(var s=0;s]*>)","im"),i="<"+a[s]+"\\b[^>]*>",u="";-1!==(l=x.helper.regexIndexOf(e,o));){var l=x.helper.splitAtIndex(e,l),c=x.helper.replaceRecursiveRegExp(l[1],t,i,u,"im");if(c===l[1])break;e=l[0].concat(c)}return e=e.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,x.subParser("hashElement")(e,r,n)),e=(e=x.helper.replaceRecursiveRegExp(e,function(e){return"\n\n¨K"+(n.gHtmlBlocks.push(e)-1)+"K\n\n"},"^ {0,3}\x3c!--","--\x3e","gm")).replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,x.subParser("hashElement")(e,r,n)),e=n.converter._dispatch("hashHTMLBlocks.after",e,r,n)}),x.subParser("hashHTMLSpans",function(e,r,t){"use strict";function a(e){return"¨C"+(t.gHtmlSpans.push(e)-1)+"C"}return e=(e=(e=(e=(e=t.converter._dispatch("hashHTMLSpans.before",e,r,t)).replace(/<[^>]+?\/>/gi,a)).replace(/<([^>]+?)>[\s\S]*?<\/\1>/g,a)).replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g,a)).replace(/<[^>]+?>/gi,a),e=t.converter._dispatch("hashHTMLSpans.after",e,r,t)}),x.subParser("unhashHTMLSpans",function(e,r,t){"use strict";e=t.converter._dispatch("unhashHTMLSpans.before",e,r,t);for(var a=0;a]*>\\s*]*>","^ {0,3}\\s*
","gim"),e=s.converter._dispatch("hashPreCodeTags.after",e,n,s)}),x.subParser("headers",function(e,n,s){"use strict";e=s.converter._dispatch("headers.before",e,n,s);var o=isNaN(parseInt(n.headerLevelStart))?1:parseInt(n.headerLevelStart),r=n.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,t=n.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm,r=(e=(e=e.replace(r,function(e,r){var t=x.subParser("spanGamut")(r,n,s),r=n.noHeaderId?"":' id="'+i(r)+'"',r=""+t+"";return x.subParser("hashBlock")(r,n,s)})).replace(t,function(e,r){var t=x.subParser("spanGamut")(r,n,s),r=n.noHeaderId?"":' id="'+i(r)+'"',a=o+1,r=""+t+"";return x.subParser("hashBlock")(r,n,s)}),n.requireSpaceBeforeHeadingText?/^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm:/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm);function i(e){var r;return!n.customizedHeaderId||(r=e.match(/\{([^{]+?)}\s*$/))&&r[1]&&(e=r[1]),r=e,e=x.helper.isString(n.prefixHeaderId)?n.prefixHeaderId:!0===n.prefixHeaderId?"section-":"",n.rawPrefixHeaderId||(r=e+r),r=(n.ghCompatibleHeaderId?r.replace(/ /g,"-").replace(/&/g,"").replace(/¨T/g,"").replace(/¨D/g,"").replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g,""):n.rawHeaderId?r.replace(/ /g,"-").replace(/&/g,"&").replace(/¨T/g,"¨").replace(/¨D/g,"$").replace(/["']/g,"-"):r.replace(/[^\w]/g,"")).toLowerCase(),n.rawPrefixHeaderId&&(r=e+r),s.hashLinkCounts[r]?r=r+"-"+s.hashLinkCounts[r]++:s.hashLinkCounts[r]=1,r}return e=e.replace(r,function(e,r,t){var a=t,a=(n.customizedHeaderId&&(a=t.replace(/\s?\{([^{]+?)}\s*$/,"")),x.subParser("spanGamut")(a,n,s)),t=n.noHeaderId?"":' id="'+i(t)+'"',r=o-1+r.length,t=""+a+"";return x.subParser("hashBlock")(t,n,s)}),e=s.converter._dispatch("headers.after",e,n,s)}),x.subParser("horizontalRule",function(e,r,t){"use strict";e=t.converter._dispatch("horizontalRule.before",e,r,t);var a=x.subParser("hashBlock")("
",r,t);return e=(e=(e=e.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm,a)).replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm,a)).replace(/^ {0,2}( ?_){3,}[ \t]*$/gm,a),e=t.converter._dispatch("horizontalRule.after",e,r,t)}),x.subParser("images",function(e,r,d){"use strict";function l(e,r,t,a,n,s,u,o){var i=d.gUrls,l=d.gTitles,c=d.gDimensions;if(t=t.toLowerCase(),o=o||"",-1? ?(['"].*['"])?\)$/m))a="";else if(""===a||null===a){if(a="#"+(t=""!==t&&null!==t?t:r.toLowerCase().replace(/ ?\n/g," ")),x.helper.isUndefined(i[t]))return e;a=i[t],x.helper.isUndefined(l[t])||(o=l[t]),x.helper.isUndefined(c[t])||(n=c[t].width,s=c[t].height)}r=r.replace(/"/g,""").replace(x.helper.regexes.asteriskDashAndColon,x.helper.escapeCharactersCallback);e=''+r+'"}return e=(e=(e=(e=(e=(e=d.converter._dispatch("images.before",e,r,d)).replace(/!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,l)).replace(/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,function(e,r,t,a,n,s,o,i){return l(e,r,t,a=a.replace(/\s/g,""),n,s,0,i)})).replace(/!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,l)).replace(/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,l)).replace(/!\[([^\[\]]+)]()()()()()/g,l),e=d.converter._dispatch("images.after",e,r,d)}),x.subParser("italicsAndBold",function(e,r,t){"use strict";return e=t.converter._dispatch("italicsAndBold.before",e,r,t),e=r.literalMidWordUnderscores?(e=(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""})).replace(/\b_(\S[\s\S]*?)_\b/g,function(e,r){return""+r+""}):(e=(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/_([^\s_][\s\S]*?)_/g,function(e,r){return/\S$/.test(r)?""+r+"":e}),e=r.literalMidWordAsterisks?(e=(e=e.replace(/([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g,function(e,r,t){return r+""+t+""})).replace(/([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g,function(e,r,t){return r+""+t+""})).replace(/([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g,function(e,r,t){return r+""+t+""}):(e=(e=e.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/\*\*(\S[\s\S]*?)\*\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/\*([^\s*][\s\S]*?)\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e}),e=t.converter._dispatch("italicsAndBold.after",e,r,t)}),x.subParser("lists",function(e,d,c){"use strict";function p(e,r){c.gListLevel++,e=e.replace(/\n{2,}$/,"\n");var t=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,l=/\n[ \t]*\n(?!¨0)/.test(e+="¨0");return d.disableForced4SpacesIndentedSublists&&(t=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm),e=(e=e.replace(t,function(e,r,t,a,n,s,o){o=o&&""!==o.trim();var n=x.subParser("outdent")(n,d,c),i="";return s&&d.tasklists&&(i=' class="task-list-item" style="list-style-type: none;"',n=n.replace(/^[ \t]*\[(x|X| )?]/m,function(){var e='"+(n=(n=r||-1\n"})).replace(/¨0/g,""),c.gListLevel--,e=r?e.replace(/\s+$/,""):e}function h(e,r){if("ol"===r){r=e.match(/^ *(\d+)\./);if(r&&"1"!==r[1])return' start="'+r[1]+'"'}return""}function n(n,s,o){var e,i=d.disableForced4SpacesIndentedSublists?/^ ?\d+\.[ \t]/gm:/^ {0,3}\d+\.[ \t]/gm,l=d.disableForced4SpacesIndentedSublists?/^ ?[*+-][ \t]/gm:/^ {0,3}[*+-][ \t]/gm,c="ul"===s?i:l,u="";return-1!==n.search(c)?function e(r){var t=r.search(c),a=h(n,s);-1!==t?(u+="\n\n<"+s+a+">\n"+p(r.slice(0,t),!!o)+"\n",c="ul"===(s="ul"===s?"ol":"ul")?i:l,e(r.slice(t))):u+="\n\n<"+s+a+">\n"+p(r,!!o)+"\n"}(n):(e=h(n,s),u="\n\n<"+s+e+">\n"+p(n,!!o)+"\n"),u}return e=c.converter._dispatch("lists.before",e,d,c),e+="¨0",e=(e=c.gListLevel?e.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(e,r,t){return n(r,-1"),o+="

",a.push(o))}for(n=a.length,s=0;s]*>\s*]*>/.test(l)&&(d=!0)}a[s]=l}return e=(e=(e=a.join("\n")).replace(/^\n+/g,"")).replace(/\n+$/g,""),t.converter._dispatch("paragraphs.after",e,r,t)}),x.subParser("runExtension",function(e,r,t,a){"use strict";return e.filter?r=e.filter(r,a.converter,t):e.regex&&((a=e.regex)instanceof RegExp||(a=new RegExp(a,"g")),r=r.replace(a,e.replace)),r}),x.subParser("spanGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("spanGamut.before",e,r,t),e=x.subParser("codeSpans")(e,r,t),e=x.subParser("escapeSpecialCharsWithinTagAttributes")(e,r,t),e=x.subParser("encodeBackslashEscapes")(e,r,t),e=x.subParser("images")(e,r,t),e=x.subParser("anchors")(e,r,t),e=x.subParser("autoLinks")(e,r,t),e=x.subParser("simplifiedAutoLinks")(e,r,t),e=x.subParser("emoji")(e,r,t),e=x.subParser("underline")(e,r,t),e=x.subParser("italicsAndBold")(e,r,t),e=x.subParser("strikethrough")(e,r,t),e=x.subParser("ellipsis")(e,r,t),e=x.subParser("hashHTMLSpans")(e,r,t),e=x.subParser("encodeAmpsAndAngles")(e,r,t),r.simpleLineBreaks?/\n\n¨K/.test(e)||(e=e.replace(/\n+/g,"
\n")):e=e.replace(/ +\n/g,"
\n"),e=t.converter._dispatch("spanGamut.after",e,r,t)}),x.subParser("strikethrough",function(e,t,a){"use strict";return t.strikethrough&&(e=(e=a.converter._dispatch("strikethrough.before",e,t,a)).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(e,r){return r=r,""+(r=t.simplifiedAutoLink?x.subParser("simplifiedAutoLinks")(r,t,a):r)+""}),e=a.converter._dispatch("strikethrough.after",e,t,a)),e}),x.subParser("stripLinkDefinitions",function(i,l,c){"use strict";function e(e,r,t,a,n,s,o){return r=r.toLowerCase(),i.toLowerCase().split(r).length-1<2?e:(t.match(/^data:.+?\/.+?;base64,/)?c.gUrls[r]=t.replace(/\s/g,""):c.gUrls[r]=x.subParser("encodeAmpsAndAngles")(t,l,c),s?s+o:(o&&(c.gTitles[r]=o.replace(/"|'/g,""")),l.parseImgDimensions&&a&&n&&(c.gDimensions[r]={width:a,height:n}),""))}return i=(i=(i=(i+="¨0").replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm,e)).replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,e)).replace(/¨0/,"")}),x.subParser("tables",function(e,y,P){"use strict";if(!y.tables)return e;function r(u){for(var e=u.split("\n"),r=0;r"+(a=x.subParser("spanGamut")(a,y,P))+"\n"));for(r=0;r"+x.subParser("spanGamut")(p,y,P)+"\n"));m.push(f)}for(var b=g,w=m,l="\n\n\n",k=b.length,c=0;c\n\n\n",c=0;c\n";for(var v=0;v\n"}return l+="\n
\n"}return e=(e=(e=(e=P.converter._dispatch("tables.before",e,y,P)).replace(/\\(\|)/g,x.helper.escapeCharactersCallback)).replace(/^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,r)).replace(/^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm,r),e=P.converter._dispatch("tables.after",e,y,P)}),x.subParser("underline",function(e,r,t){"use strict";return r.underline?(e=t.converter._dispatch("underline.before",e,r,t),e=(e=r.literalMidWordUnderscores?(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""}):(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/(_)/g,x.helper.escapeCharactersCallback),t.converter._dispatch("underline.after",e,r,t)):e}),x.subParser("unescapeSpecialChars",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("unescapeSpecialChars.before",e,r,t)).replace(/¨E(\d+)E/g,function(e,r){r=parseInt(r);return String.fromCharCode(r)}),e=t.converter._dispatch("unescapeSpecialChars.after",e,r,t)}),x.subParser("makeMarkdown.blockquote",function(e,r){"use strict";var t="";if(e.hasChildNodes())for(var a=e.childNodes,n=a.length,s=0;s ")}),x.subParser("makeMarkdown.codeBlock",function(e,r){"use strict";var t=e.getAttribute("language"),e=e.getAttribute("precodenum");return"```"+t+"\n"+r.preList[e]+"\n```"}),x.subParser("makeMarkdown.codeSpan",function(e){"use strict";return"`"+e.innerHTML+"`"}),x.subParser("makeMarkdown.emphasis",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="*";for(var a=e.childNodes,n=a.length,s=0;s",e.hasAttribute("width")&&e.hasAttribute("height")&&(r+=" ="+e.getAttribute("width")+"x"+e.getAttribute("height")),e.hasAttribute("title")&&(r+=' "'+e.getAttribute("title")+'"'),r+=")"),r}),x.subParser("makeMarkdown.links",function(e,r){"use strict";var t="";if(e.hasChildNodes()&&e.hasAttribute("href")){for(var a=e.childNodes,n=a.length,t="[",s=0;s"),e.hasAttribute("title")&&(t+=' "'+e.getAttribute("title")+'"'),t+=")"}return t}),x.subParser("makeMarkdown.list",function(e,r,t){"use strict";var a="";if(!e.hasChildNodes())return"";for(var n=e.childNodes,s=n.length,o=e.getAttribute("start")||1,i=0;i"+r.preList[e]+""}),x.subParser("makeMarkdown.strikethrough",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="~~";for(var a=e.childNodes,n=a.length,s=0;str>th"),s=e.querySelectorAll("tbody>tr"),o=0;o/g,"\\$1>")).replace(/^#/gm,"\\#")).replace(/^(\s*)([-=]{3,})(\s*)$/,"$1\\$2$3")).replace(/^( {0,3}\d+)\./gm,"$1\\.")).replace(/^( {0,3})([+-])/gm,"$1\\$2")).replace(/]([\s]*)\(/g,"\\]$1\\(")).replace(/^ {0,3}\[([\S \t]*?)]:/gm,"\\[$1]:")});"function"==typeof define&&define.amd?define(function(){"use strict";return x}):"undefined"!=typeof module&&module.exports?module.exports=x:this.showdown=x}.call(this); diff --git a/js/showdown-2.0.3.js b/js/showdown-2.0.3.js new file mode 100644 index 00000000..6c25ae4d --- /dev/null +++ b/js/showdown-2.0.3.js @@ -0,0 +1,2 @@ +/*! showdown v 2.0.3 - 08-03-2022 */ +!function(){function a(e){"use strict";var r={omitExtraWLInCodeBlocks:{defaultValue:!1,describe:"Omit the default extra whiteline added to code blocks",type:"boolean"},noHeaderId:{defaultValue:!1,describe:"Turn on/off generated header id",type:"boolean"},prefixHeaderId:{defaultValue:!1,describe:"Add a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to true will add a generic 'section-' prefix",type:"string"},rawPrefixHeaderId:{defaultValue:!1,describe:'Setting this option to true will prevent showdown from modifying the prefix. This might result in malformed IDs (if, for instance, the " char is used in the prefix)',type:"boolean"},ghCompatibleHeaderId:{defaultValue:!1,describe:"Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)",type:"boolean"},rawHeaderId:{defaultValue:!1,describe:"Remove only spaces, ' and \" from generated header ids (including prefixes), replacing them with dashes (-). WARNING: This might result in malformed ids",type:"boolean"},headerLevelStart:{defaultValue:!1,describe:"The header blocks level start",type:"integer"},parseImgDimensions:{defaultValue:!1,describe:"Turn on/off image dimension parsing",type:"boolean"},simplifiedAutoLink:{defaultValue:!1,describe:"Turn on/off GFM autolink style",type:"boolean"},excludeTrailingPunctuationFromURLs:{defaultValue:!1,describe:"Excludes trailing punctuation from links generated with autoLinking",type:"boolean"},literalMidWordUnderscores:{defaultValue:!1,describe:"Parse midword underscores as literal underscores",type:"boolean"},literalMidWordAsterisks:{defaultValue:!1,describe:"Parse midword asterisks as literal asterisks",type:"boolean"},strikethrough:{defaultValue:!1,describe:"Turn on/off strikethrough support",type:"boolean"},tables:{defaultValue:!1,describe:"Turn on/off tables support",type:"boolean"},tablesHeaderId:{defaultValue:!1,describe:"Add an id to table headers",type:"boolean"},ghCodeBlocks:{defaultValue:!0,describe:"Turn on/off GFM fenced code blocks support",type:"boolean"},tasklists:{defaultValue:!1,describe:"Turn on/off GFM tasklist support",type:"boolean"},smoothLivePreview:{defaultValue:!1,describe:"Prevents weird effects in live previews due to incomplete input",type:"boolean"},smartIndentationFix:{defaultValue:!1,describe:"Tries to smartly fix indentation in es6 strings",type:"boolean"},disableForced4SpacesIndentedSublists:{defaultValue:!1,describe:"Disables the requirement of indenting nested sublists by 4 spaces",type:"boolean"},simpleLineBreaks:{defaultValue:!1,describe:"Parses simple line breaks as
(GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,describe:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,describe:"Enables github @mentions",type:"boolean"},ghMentionsLink:{defaultValue:"https://github.com/{u}",describe:"Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",type:"string"},encodeEmails:{defaultValue:!0,describe:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"},openLinksInNewWindow:{defaultValue:!1,describe:"Open all links in new windows",type:"boolean"},backslashEscapesHTMLTags:{defaultValue:!1,describe:"Support for HTML Tag escaping. ex:
foo
",type:"boolean"},emoji:{defaultValue:!1,describe:"Enable emoji support. Ex: `this is a :smile: emoji`",type:"boolean"},underline:{defaultValue:!1,describe:"Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `` and ``",type:"boolean"},ellipsis:{defaultValue:!0,describe:"Replaces three dots with the ellipsis unicode character",type:"boolean"},completeHTMLDocument:{defaultValue:!1,describe:"Outputs a complete html document, including ``, `` and `` tags",type:"boolean"},metadata:{defaultValue:!1,describe:"Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).",type:"boolean"},splitAdjacentBlockquotes:{defaultValue:!1,describe:"Split adjacent blockquote blocks",type:"boolean"}};if(!1===e)return JSON.parse(JSON.stringify(r));var t,a={};for(t in r)r.hasOwnProperty(t)&&(a[t]=r[t].defaultValue);return a}var x={},t={},d={},p=a(!0),h="vanilla",_={github:{omitExtraWLInCodeBlocks:!0,simplifiedAutoLink:!0,excludeTrailingPunctuationFromURLs:!0,literalMidWordUnderscores:!0,strikethrough:!0,tables:!0,tablesHeaderId:!0,ghCodeBlocks:!0,tasklists:!0,disableForced4SpacesIndentedSublists:!0,simpleLineBreaks:!0,requireSpaceBeforeHeadingText:!0,ghCompatibleHeaderId:!0,ghMentions:!0,backslashEscapesHTMLTags:!0,emoji:!0,splitAdjacentBlockquotes:!0},original:{noHeaderId:!0,ghCodeBlocks:!1},ghost:{omitExtraWLInCodeBlocks:!0,parseImgDimensions:!0,simplifiedAutoLink:!0,excludeTrailingPunctuationFromURLs:!0,literalMidWordUnderscores:!0,strikethrough:!0,tables:!0,tablesHeaderId:!0,ghCodeBlocks:!0,tasklists:!0,smoothLivePreview:!0,simpleLineBreaks:!0,requireSpaceBeforeHeadingText:!0,ghMentions:!1,encodeEmails:!0},vanilla:a(!0),allOn:function(){"use strict";var e,r=a(!0),t={};for(e in r)r.hasOwnProperty(e)&&(t[e]=!0);return t}()};function g(e,r){"use strict";var t=r?"Error in "+r+" extension->":"Error in unnamed extension",a={valid:!0,error:""};x.helper.isArray(e)||(e=[e]);for(var n=0;n").replace(/&/g,"&")};function u(e,r,t,a){"use strict";var n,s,o,i=-1<(a=a||"").indexOf("g"),l=new RegExp(r+"|"+t,"g"+a.replace(/g/g,"")),c=new RegExp(r,a.replace(/g/g,"")),u=[];do{for(n=0;p=l.exec(e);)if(c.test(p[0]))n++||(o=(s=l.lastIndex)-p[0].length);else if(n&&!--n){var d=p.index+p[0].length,p={left:{start:o,end:s},match:{start:s,end:p.index},right:{start:p.index,end:d},wholeMatch:{start:o,end:d}};if(u.push(p),!i)return u}}while(n&&(l.lastIndex=s));return u}function s(u){"use strict";return function(e,r,t,a,n,s,o){var i=t=t.replace(x.helper.regexes.asteriskDashAndColon,x.helper.escapeCharactersCallback),l="",c="",r=r||"",o=o||"";return/^www\./i.test(t)&&(t=t.replace(/^www\./i,"http://www.")),u.excludeTrailingPunctuationFromURLs&&s&&(l=s),r+'"+i+""+l+o}}function o(n,s){"use strict";return function(e,r,t){var a="mailto:";return r=r||"",t=x.subParser("unescapeSpecialChars")(t,n,s),n.encodeEmails?(a=x.helper.encodeEmailAddress(a+t),t=x.helper.encodeEmailAddress(t)):a+=t,r+''+t+""}}x.helper.matchRecursiveRegExp=function(e,r,t,a){"use strict";for(var n=u(e,r,t,a),s=[],o=0;o>=0,t=String(t||" "),e.length>r?String(e):((r-=e.length)>t.length&&(t+=t.repeat(r/t.length)),String(e)+t.slice(0,r))},"undefined"==typeof console&&(console={warn:function(e){"use strict";alert(e)},log:function(e){"use strict";alert(e)},error:function(e){"use strict";throw e}}),x.helper.regexes={asteriskDashAndColon:/([*_:~])/g},x.helper.emojis={"+1":"👍","-1":"👎",100:"💯",1234:"🔢","1st_place_medal":"🥇","2nd_place_medal":"🥈","3rd_place_medal":"🥉","8ball":"🎱",a:"🅰️",ab:"🆎",abc:"🔤",abcd:"🔡",accept:"🉑",aerial_tramway:"🚡",airplane:"✈️",alarm_clock:"⏰",alembic:"⚗️",alien:"👽",ambulance:"🚑",amphora:"🏺",anchor:"⚓️",angel:"👼",anger:"💢",angry:"😠",anguished:"😧",ant:"🐜",apple:"🍎",aquarius:"♒️",aries:"♈️",arrow_backward:"◀️",arrow_double_down:"⏬",arrow_double_up:"⏫",arrow_down:"⬇️",arrow_down_small:"🔽",arrow_forward:"▶️",arrow_heading_down:"⤵️",arrow_heading_up:"⤴️",arrow_left:"⬅️",arrow_lower_left:"↙️",arrow_lower_right:"↘️",arrow_right:"➡️",arrow_right_hook:"↪️",arrow_up:"⬆️",arrow_up_down:"↕️",arrow_up_small:"🔼",arrow_upper_left:"↖️",arrow_upper_right:"↗️",arrows_clockwise:"🔃",arrows_counterclockwise:"🔄",art:"🎨",articulated_lorry:"🚛",artificial_satellite:"🛰",astonished:"😲",athletic_shoe:"👟",atm:"🏧",atom_symbol:"⚛️",avocado:"🥑",b:"🅱️",baby:"👶",baby_bottle:"🍼",baby_chick:"🐤",baby_symbol:"🚼",back:"🔙",bacon:"🥓",badminton:"🏸",baggage_claim:"🛄",baguette_bread:"🥖",balance_scale:"⚖️",balloon:"🎈",ballot_box:"🗳",ballot_box_with_check:"☑️",bamboo:"🎍",banana:"🍌",bangbang:"‼️",bank:"🏦",bar_chart:"📊",barber:"💈",baseball:"⚾️",basketball:"🏀",basketball_man:"⛹️",basketball_woman:"⛹️‍♀️",bat:"🦇",bath:"🛀",bathtub:"🛁",battery:"🔋",beach_umbrella:"🏖",bear:"🐻",bed:"🛏",bee:"🐝",beer:"🍺",beers:"🍻",beetle:"🐞",beginner:"🔰",bell:"🔔",bellhop_bell:"🛎",bento:"🍱",biking_man:"🚴",bike:"🚲",biking_woman:"🚴‍♀️",bikini:"👙",biohazard:"☣️",bird:"🐦",birthday:"🎂",black_circle:"⚫️",black_flag:"🏴",black_heart:"🖤",black_joker:"🃏",black_large_square:"⬛️",black_medium_small_square:"◾️",black_medium_square:"◼️",black_nib:"✒️",black_small_square:"▪️",black_square_button:"🔲",blonde_man:"👱",blonde_woman:"👱‍♀️",blossom:"🌼",blowfish:"🐡",blue_book:"📘",blue_car:"🚙",blue_heart:"💙",blush:"😊",boar:"🐗",boat:"⛵️",bomb:"💣",book:"📖",bookmark:"🔖",bookmark_tabs:"📑",books:"📚",boom:"💥",boot:"👢",bouquet:"💐",bowing_man:"🙇",bow_and_arrow:"🏹",bowing_woman:"🙇‍♀️",bowling:"🎳",boxing_glove:"🥊",boy:"👦",bread:"🍞",bride_with_veil:"👰",bridge_at_night:"🌉",briefcase:"💼",broken_heart:"💔",bug:"🐛",building_construction:"🏗",bulb:"💡",bullettrain_front:"🚅",bullettrain_side:"🚄",burrito:"🌯",bus:"🚌",business_suit_levitating:"🕴",busstop:"🚏",bust_in_silhouette:"👤",busts_in_silhouette:"👥",butterfly:"🦋",cactus:"🌵",cake:"🍰",calendar:"📆",call_me_hand:"🤙",calling:"📲",camel:"🐫",camera:"📷",camera_flash:"📸",camping:"🏕",cancer:"♋️",candle:"🕯",candy:"🍬",canoe:"🛶",capital_abcd:"🔠",capricorn:"♑️",car:"🚗",card_file_box:"🗃",card_index:"📇",card_index_dividers:"🗂",carousel_horse:"🎠",carrot:"🥕",cat:"🐱",cat2:"🐈",cd:"💿",chains:"⛓",champagne:"🍾",chart:"💹",chart_with_downwards_trend:"📉",chart_with_upwards_trend:"📈",checkered_flag:"🏁",cheese:"🧀",cherries:"🍒",cherry_blossom:"🌸",chestnut:"🌰",chicken:"🐔",children_crossing:"🚸",chipmunk:"🐿",chocolate_bar:"🍫",christmas_tree:"🎄",church:"⛪️",cinema:"🎦",circus_tent:"🎪",city_sunrise:"🌇",city_sunset:"🌆",cityscape:"🏙",cl:"🆑",clamp:"🗜",clap:"👏",clapper:"🎬",classical_building:"🏛",clinking_glasses:"🥂",clipboard:"📋",clock1:"🕐",clock10:"🕙",clock1030:"🕥",clock11:"🕚",clock1130:"🕦",clock12:"🕛",clock1230:"🕧",clock130:"🕜",clock2:"🕑",clock230:"🕝",clock3:"🕒",clock330:"🕞",clock4:"🕓",clock430:"🕟",clock5:"🕔",clock530:"🕠",clock6:"🕕",clock630:"🕡",clock7:"🕖",clock730:"🕢",clock8:"🕗",clock830:"🕣",clock9:"🕘",clock930:"🕤",closed_book:"📕",closed_lock_with_key:"🔐",closed_umbrella:"🌂",cloud:"☁️",cloud_with_lightning:"🌩",cloud_with_lightning_and_rain:"⛈",cloud_with_rain:"🌧",cloud_with_snow:"🌨",clown_face:"🤡",clubs:"♣️",cocktail:"🍸",coffee:"☕️",coffin:"⚰️",cold_sweat:"😰",comet:"☄️",computer:"💻",computer_mouse:"🖱",confetti_ball:"🎊",confounded:"😖",confused:"😕",congratulations:"㊗️",construction:"🚧",construction_worker_man:"👷",construction_worker_woman:"👷‍♀️",control_knobs:"🎛",convenience_store:"🏪",cookie:"🍪",cool:"🆒",policeman:"👮",copyright:"©️",corn:"🌽",couch_and_lamp:"🛋",couple:"👫",couple_with_heart_woman_man:"💑",couple_with_heart_man_man:"👨‍❤️‍👨",couple_with_heart_woman_woman:"👩‍❤️‍👩",couplekiss_man_man:"👨‍❤️‍💋‍👨",couplekiss_man_woman:"💏",couplekiss_woman_woman:"👩‍❤️‍💋‍👩",cow:"🐮",cow2:"🐄",cowboy_hat_face:"🤠",crab:"🦀",crayon:"🖍",credit_card:"💳",crescent_moon:"🌙",cricket:"🏏",crocodile:"🐊",croissant:"🥐",crossed_fingers:"🤞",crossed_flags:"🎌",crossed_swords:"⚔️",crown:"👑",cry:"😢",crying_cat_face:"😿",crystal_ball:"🔮",cucumber:"🥒",cupid:"💘",curly_loop:"➰",currency_exchange:"💱",curry:"🍛",custard:"🍮",customs:"🛃",cyclone:"🌀",dagger:"🗡",dancer:"💃",dancing_women:"👯",dancing_men:"👯‍♂️",dango:"🍡",dark_sunglasses:"🕶",dart:"🎯",dash:"💨",date:"📅",deciduous_tree:"🌳",deer:"🦌",department_store:"🏬",derelict_house:"🏚",desert:"🏜",desert_island:"🏝",desktop_computer:"🖥",male_detective:"🕵️",diamond_shape_with_a_dot_inside:"💠",diamonds:"♦️",disappointed:"😞",disappointed_relieved:"😥",dizzy:"💫",dizzy_face:"😵",do_not_litter:"🚯",dog:"🐶",dog2:"🐕",dollar:"💵",dolls:"🎎",dolphin:"🐬",door:"🚪",doughnut:"🍩",dove:"🕊",dragon:"🐉",dragon_face:"🐲",dress:"👗",dromedary_camel:"🐪",drooling_face:"🤤",droplet:"💧",drum:"🥁",duck:"🦆",dvd:"📀","e-mail":"📧",eagle:"🦅",ear:"👂",ear_of_rice:"🌾",earth_africa:"🌍",earth_americas:"🌎",earth_asia:"🌏",egg:"🥚",eggplant:"🍆",eight_pointed_black_star:"✴️",eight_spoked_asterisk:"✳️",electric_plug:"🔌",elephant:"🐘",email:"✉️",end:"🔚",envelope_with_arrow:"📩",euro:"💶",european_castle:"🏰",european_post_office:"🏤",evergreen_tree:"🌲",exclamation:"❗️",expressionless:"😑",eye:"👁",eye_speech_bubble:"👁‍🗨",eyeglasses:"👓",eyes:"👀",face_with_head_bandage:"🤕",face_with_thermometer:"🤒",fist_oncoming:"👊",factory:"🏭",fallen_leaf:"🍂",family_man_woman_boy:"👪",family_man_boy:"👨‍👦",family_man_boy_boy:"👨‍👦‍👦",family_man_girl:"👨‍👧",family_man_girl_boy:"👨‍👧‍👦",family_man_girl_girl:"👨‍👧‍👧",family_man_man_boy:"👨‍👨‍👦",family_man_man_boy_boy:"👨‍👨‍👦‍👦",family_man_man_girl:"👨‍👨‍👧",family_man_man_girl_boy:"👨‍👨‍👧‍👦",family_man_man_girl_girl:"👨‍👨‍👧‍👧",family_man_woman_boy_boy:"👨‍👩‍👦‍👦",family_man_woman_girl:"👨‍👩‍👧",family_man_woman_girl_boy:"👨‍👩‍👧‍👦",family_man_woman_girl_girl:"👨‍👩‍👧‍👧",family_woman_boy:"👩‍👦",family_woman_boy_boy:"👩‍👦‍👦",family_woman_girl:"👩‍👧",family_woman_girl_boy:"👩‍👧‍👦",family_woman_girl_girl:"👩‍👧‍👧",family_woman_woman_boy:"👩‍👩‍👦",family_woman_woman_boy_boy:"👩‍👩‍👦‍👦",family_woman_woman_girl:"👩‍👩‍👧",family_woman_woman_girl_boy:"👩‍👩‍👧‍👦",family_woman_woman_girl_girl:"👩‍👩‍👧‍👧",fast_forward:"⏩",fax:"📠",fearful:"😨",feet:"🐾",female_detective:"🕵️‍♀️",ferris_wheel:"🎡",ferry:"⛴",field_hockey:"🏑",file_cabinet:"🗄",file_folder:"📁",film_projector:"📽",film_strip:"🎞",fire:"🔥",fire_engine:"🚒",fireworks:"🎆",first_quarter_moon:"🌓",first_quarter_moon_with_face:"🌛",fish:"🐟",fish_cake:"🍥",fishing_pole_and_fish:"🎣",fist_raised:"✊",fist_left:"🤛",fist_right:"🤜",flags:"🎏",flashlight:"🔦",fleur_de_lis:"⚜️",flight_arrival:"🛬",flight_departure:"🛫",floppy_disk:"💾",flower_playing_cards:"🎴",flushed:"😳",fog:"🌫",foggy:"🌁",football:"🏈",footprints:"👣",fork_and_knife:"🍴",fountain:"⛲️",fountain_pen:"🖋",four_leaf_clover:"🍀",fox_face:"🦊",framed_picture:"🖼",free:"🆓",fried_egg:"🍳",fried_shrimp:"🍤",fries:"🍟",frog:"🐸",frowning:"😦",frowning_face:"☹️",frowning_man:"🙍‍♂️",frowning_woman:"🙍",middle_finger:"🖕",fuelpump:"⛽️",full_moon:"🌕",full_moon_with_face:"🌝",funeral_urn:"⚱️",game_die:"🎲",gear:"⚙️",gem:"💎",gemini:"♊️",ghost:"👻",gift:"🎁",gift_heart:"💝",girl:"👧",globe_with_meridians:"🌐",goal_net:"🥅",goat:"🐐",golf:"⛳️",golfing_man:"🏌️",golfing_woman:"🏌️‍♀️",gorilla:"🦍",grapes:"🍇",green_apple:"🍏",green_book:"📗",green_heart:"💚",green_salad:"🥗",grey_exclamation:"❕",grey_question:"❔",grimacing:"😬",grin:"😁",grinning:"😀",guardsman:"💂",guardswoman:"💂‍♀️",guitar:"🎸",gun:"🔫",haircut_woman:"💇",haircut_man:"💇‍♂️",hamburger:"🍔",hammer:"🔨",hammer_and_pick:"⚒",hammer_and_wrench:"🛠",hamster:"🐹",hand:"✋",handbag:"👜",handshake:"🤝",hankey:"💩",hatched_chick:"🐥",hatching_chick:"🐣",headphones:"🎧",hear_no_evil:"🙉",heart:"❤️",heart_decoration:"💟",heart_eyes:"😍",heart_eyes_cat:"😻",heartbeat:"💓",heartpulse:"💗",hearts:"♥️",heavy_check_mark:"✔️",heavy_division_sign:"➗",heavy_dollar_sign:"💲",heavy_heart_exclamation:"❣️",heavy_minus_sign:"➖",heavy_multiplication_x:"✖️",heavy_plus_sign:"➕",helicopter:"🚁",herb:"🌿",hibiscus:"🌺",high_brightness:"🔆",high_heel:"👠",hocho:"🔪",hole:"🕳",honey_pot:"🍯",horse:"🐴",horse_racing:"🏇",hospital:"🏥",hot_pepper:"🌶",hotdog:"🌭",hotel:"🏨",hotsprings:"♨️",hourglass:"⌛️",hourglass_flowing_sand:"⏳",house:"🏠",house_with_garden:"🏡",houses:"🏘",hugs:"🤗",hushed:"😯",ice_cream:"🍨",ice_hockey:"🏒",ice_skate:"⛸",icecream:"🍦",id:"🆔",ideograph_advantage:"🉐",imp:"👿",inbox_tray:"📥",incoming_envelope:"📨",tipping_hand_woman:"💁",information_source:"ℹ️",innocent:"😇",interrobang:"⁉️",iphone:"📱",izakaya_lantern:"🏮",jack_o_lantern:"🎃",japan:"🗾",japanese_castle:"🏯",japanese_goblin:"👺",japanese_ogre:"👹",jeans:"👖",joy:"😂",joy_cat:"😹",joystick:"🕹",kaaba:"🕋",key:"🔑",keyboard:"⌨️",keycap_ten:"🔟",kick_scooter:"🛴",kimono:"👘",kiss:"💋",kissing:"😗",kissing_cat:"😽",kissing_closed_eyes:"😚",kissing_heart:"😘",kissing_smiling_eyes:"😙",kiwi_fruit:"🥝",koala:"🐨",koko:"🈁",label:"🏷",large_blue_circle:"🔵",large_blue_diamond:"🔷",large_orange_diamond:"🔶",last_quarter_moon:"🌗",last_quarter_moon_with_face:"🌜",latin_cross:"✝️",laughing:"😆",leaves:"🍃",ledger:"📒",left_luggage:"🛅",left_right_arrow:"↔️",leftwards_arrow_with_hook:"↩️",lemon:"🍋",leo:"♌️",leopard:"🐆",level_slider:"🎚",libra:"♎️",light_rail:"🚈",link:"🔗",lion:"🦁",lips:"👄",lipstick:"💄",lizard:"🦎",lock:"🔒",lock_with_ink_pen:"🔏",lollipop:"🍭",loop:"➿",loud_sound:"🔊",loudspeaker:"📢",love_hotel:"🏩",love_letter:"💌",low_brightness:"🔅",lying_face:"🤥",m:"Ⓜ️",mag:"🔍",mag_right:"🔎",mahjong:"🀄️",mailbox:"📫",mailbox_closed:"📪",mailbox_with_mail:"📬",mailbox_with_no_mail:"📭",man:"👨",man_artist:"👨‍🎨",man_astronaut:"👨‍🚀",man_cartwheeling:"🤸‍♂️",man_cook:"👨‍🍳",man_dancing:"🕺",man_facepalming:"🤦‍♂️",man_factory_worker:"👨‍🏭",man_farmer:"👨‍🌾",man_firefighter:"👨‍🚒",man_health_worker:"👨‍⚕️",man_in_tuxedo:"🤵",man_judge:"👨‍⚖️",man_juggling:"🤹‍♂️",man_mechanic:"👨‍🔧",man_office_worker:"👨‍💼",man_pilot:"👨‍✈️",man_playing_handball:"🤾‍♂️",man_playing_water_polo:"🤽‍♂️",man_scientist:"👨‍🔬",man_shrugging:"🤷‍♂️",man_singer:"👨‍🎤",man_student:"👨‍🎓",man_teacher:"👨‍🏫",man_technologist:"👨‍💻",man_with_gua_pi_mao:"👲",man_with_turban:"👳",tangerine:"🍊",mans_shoe:"👞",mantelpiece_clock:"🕰",maple_leaf:"🍁",martial_arts_uniform:"🥋",mask:"😷",massage_woman:"💆",massage_man:"💆‍♂️",meat_on_bone:"🍖",medal_military:"🎖",medal_sports:"🏅",mega:"📣",melon:"🍈",memo:"📝",men_wrestling:"🤼‍♂️",menorah:"🕎",mens:"🚹",metal:"🤘",metro:"🚇",microphone:"🎤",microscope:"🔬",milk_glass:"🥛",milky_way:"🌌",minibus:"🚐",minidisc:"💽",mobile_phone_off:"📴",money_mouth_face:"🤑",money_with_wings:"💸",moneybag:"💰",monkey:"🐒",monkey_face:"🐵",monorail:"🚝",moon:"🌔",mortar_board:"🎓",mosque:"🕌",motor_boat:"🛥",motor_scooter:"🛵",motorcycle:"🏍",motorway:"🛣",mount_fuji:"🗻",mountain:"⛰",mountain_biking_man:"🚵",mountain_biking_woman:"🚵‍♀️",mountain_cableway:"🚠",mountain_railway:"🚞",mountain_snow:"🏔",mouse:"🐭",mouse2:"🐁",movie_camera:"🎥",moyai:"🗿",mrs_claus:"🤶",muscle:"💪",mushroom:"🍄",musical_keyboard:"🎹",musical_note:"🎵",musical_score:"🎼",mute:"🔇",nail_care:"💅",name_badge:"📛",national_park:"🏞",nauseated_face:"🤢",necktie:"👔",negative_squared_cross_mark:"❎",nerd_face:"🤓",neutral_face:"😐",new:"🆕",new_moon:"🌑",new_moon_with_face:"🌚",newspaper:"📰",newspaper_roll:"🗞",next_track_button:"⏭",ng:"🆖",no_good_man:"🙅‍♂️",no_good_woman:"🙅",night_with_stars:"🌃",no_bell:"🔕",no_bicycles:"🚳",no_entry:"⛔️",no_entry_sign:"🚫",no_mobile_phones:"📵",no_mouth:"😶",no_pedestrians:"🚷",no_smoking:"🚭","non-potable_water":"🚱",nose:"👃",notebook:"📓",notebook_with_decorative_cover:"📔",notes:"🎶",nut_and_bolt:"🔩",o:"⭕️",o2:"🅾️",ocean:"🌊",octopus:"🐙",oden:"🍢",office:"🏢",oil_drum:"🛢",ok:"🆗",ok_hand:"👌",ok_man:"🙆‍♂️",ok_woman:"🙆",old_key:"🗝",older_man:"👴",older_woman:"👵",om:"🕉",on:"🔛",oncoming_automobile:"🚘",oncoming_bus:"🚍",oncoming_police_car:"🚔",oncoming_taxi:"🚖",open_file_folder:"📂",open_hands:"👐",open_mouth:"😮",open_umbrella:"☂️",ophiuchus:"⛎",orange_book:"📙",orthodox_cross:"☦️",outbox_tray:"📤",owl:"🦉",ox:"🐂",package:"📦",page_facing_up:"📄",page_with_curl:"📃",pager:"📟",paintbrush:"🖌",palm_tree:"🌴",pancakes:"🥞",panda_face:"🐼",paperclip:"📎",paperclips:"🖇",parasol_on_ground:"⛱",parking:"🅿️",part_alternation_mark:"〽️",partly_sunny:"⛅️",passenger_ship:"🛳",passport_control:"🛂",pause_button:"⏸",peace_symbol:"☮️",peach:"🍑",peanuts:"🥜",pear:"🍐",pen:"🖊",pencil2:"✏️",penguin:"🐧",pensive:"😔",performing_arts:"🎭",persevere:"😣",person_fencing:"🤺",pouting_woman:"🙎",phone:"☎️",pick:"⛏",pig:"🐷",pig2:"🐖",pig_nose:"🐽",pill:"💊",pineapple:"🍍",ping_pong:"🏓",pisces:"♓️",pizza:"🍕",place_of_worship:"🛐",plate_with_cutlery:"🍽",play_or_pause_button:"⏯",point_down:"👇",point_left:"👈",point_right:"👉",point_up:"☝️",point_up_2:"👆",police_car:"🚓",policewoman:"👮‍♀️",poodle:"🐩",popcorn:"🍿",post_office:"🏣",postal_horn:"📯",postbox:"📮",potable_water:"🚰",potato:"🥔",pouch:"👝",poultry_leg:"🍗",pound:"💷",rage:"😡",pouting_cat:"😾",pouting_man:"🙎‍♂️",pray:"🙏",prayer_beads:"📿",pregnant_woman:"🤰",previous_track_button:"⏮",prince:"🤴",princess:"👸",printer:"🖨",purple_heart:"💜",purse:"👛",pushpin:"📌",put_litter_in_its_place:"🚮",question:"❓",rabbit:"🐰",rabbit2:"🐇",racehorse:"🐎",racing_car:"🏎",radio:"📻",radio_button:"🔘",radioactive:"☢️",railway_car:"🚃",railway_track:"🛤",rainbow:"🌈",rainbow_flag:"🏳️‍🌈",raised_back_of_hand:"🤚",raised_hand_with_fingers_splayed:"🖐",raised_hands:"🙌",raising_hand_woman:"🙋",raising_hand_man:"🙋‍♂️",ram:"🐏",ramen:"🍜",rat:"🐀",record_button:"⏺",recycle:"♻️",red_circle:"🔴",registered:"®️",relaxed:"☺️",relieved:"😌",reminder_ribbon:"🎗",repeat:"🔁",repeat_one:"🔂",rescue_worker_helmet:"⛑",restroom:"🚻",revolving_hearts:"💞",rewind:"⏪",rhinoceros:"🦏",ribbon:"🎀",rice:"🍚",rice_ball:"🍙",rice_cracker:"🍘",rice_scene:"🎑",right_anger_bubble:"🗯",ring:"💍",robot:"🤖",rocket:"🚀",rofl:"🤣",roll_eyes:"🙄",roller_coaster:"🎢",rooster:"🐓",rose:"🌹",rosette:"🏵",rotating_light:"🚨",round_pushpin:"📍",rowing_man:"🚣",rowing_woman:"🚣‍♀️",rugby_football:"🏉",running_man:"🏃",running_shirt_with_sash:"🎽",running_woman:"🏃‍♀️",sa:"🈂️",sagittarius:"♐️",sake:"🍶",sandal:"👡",santa:"🎅",satellite:"📡",saxophone:"🎷",school:"🏫",school_satchel:"🎒",scissors:"✂️",scorpion:"🦂",scorpius:"♏️",scream:"😱",scream_cat:"🙀",scroll:"📜",seat:"💺",secret:"㊙️",see_no_evil:"🙈",seedling:"🌱",selfie:"🤳",shallow_pan_of_food:"🥘",shamrock:"☘️",shark:"🦈",shaved_ice:"🍧",sheep:"🐑",shell:"🐚",shield:"🛡",shinto_shrine:"⛩",ship:"🚢",shirt:"👕",shopping:"🛍",shopping_cart:"🛒",shower:"🚿",shrimp:"🦐",signal_strength:"📶",six_pointed_star:"🔯",ski:"🎿",skier:"⛷",skull:"💀",skull_and_crossbones:"☠️",sleeping:"😴",sleeping_bed:"🛌",sleepy:"😪",slightly_frowning_face:"🙁",slightly_smiling_face:"🙂",slot_machine:"🎰",small_airplane:"🛩",small_blue_diamond:"🔹",small_orange_diamond:"🔸",small_red_triangle:"🔺",small_red_triangle_down:"🔻",smile:"😄",smile_cat:"😸",smiley:"😃",smiley_cat:"😺",smiling_imp:"😈",smirk:"😏",smirk_cat:"😼",smoking:"🚬",snail:"🐌",snake:"🐍",sneezing_face:"🤧",snowboarder:"🏂",snowflake:"❄️",snowman:"⛄️",snowman_with_snow:"☃️",sob:"😭",soccer:"⚽️",soon:"🔜",sos:"🆘",sound:"🔉",space_invader:"👾",spades:"♠️",spaghetti:"🍝",sparkle:"❇️",sparkler:"🎇",sparkles:"✨",sparkling_heart:"💖",speak_no_evil:"🙊",speaker:"🔈",speaking_head:"🗣",speech_balloon:"💬",speedboat:"🚤",spider:"🕷",spider_web:"🕸",spiral_calendar:"🗓",spiral_notepad:"🗒",spoon:"🥄",squid:"🦑",stadium:"🏟",star:"⭐️",star2:"🌟",star_and_crescent:"☪️",star_of_david:"✡️",stars:"🌠",station:"🚉",statue_of_liberty:"🗽",steam_locomotive:"🚂",stew:"🍲",stop_button:"⏹",stop_sign:"🛑",stopwatch:"⏱",straight_ruler:"📏",strawberry:"🍓",stuck_out_tongue:"😛",stuck_out_tongue_closed_eyes:"😝",stuck_out_tongue_winking_eye:"😜",studio_microphone:"🎙",stuffed_flatbread:"🥙",sun_behind_large_cloud:"🌥",sun_behind_rain_cloud:"🌦",sun_behind_small_cloud:"🌤",sun_with_face:"🌞",sunflower:"🌻",sunglasses:"😎",sunny:"☀️",sunrise:"🌅",sunrise_over_mountains:"🌄",surfing_man:"🏄",surfing_woman:"🏄‍♀️",sushi:"🍣",suspension_railway:"🚟",sweat:"😓",sweat_drops:"💦",sweat_smile:"😅",sweet_potato:"🍠",swimming_man:"🏊",swimming_woman:"🏊‍♀️",symbols:"🔣",synagogue:"🕍",syringe:"💉",taco:"🌮",tada:"🎉",tanabata_tree:"🎋",taurus:"♉️",taxi:"🚕",tea:"🍵",telephone_receiver:"📞",telescope:"🔭",tennis:"🎾",tent:"⛺️",thermometer:"🌡",thinking:"🤔",thought_balloon:"💭",ticket:"🎫",tickets:"🎟",tiger:"🐯",tiger2:"🐅",timer_clock:"⏲",tipping_hand_man:"💁‍♂️",tired_face:"😫",tm:"™️",toilet:"🚽",tokyo_tower:"🗼",tomato:"🍅",tongue:"👅",top:"🔝",tophat:"🎩",tornado:"🌪",trackball:"🖲",tractor:"🚜",traffic_light:"🚥",train:"🚋",train2:"🚆",tram:"🚊",triangular_flag_on_post:"🚩",triangular_ruler:"📐",trident:"🔱",triumph:"😤",trolleybus:"🚎",trophy:"🏆",tropical_drink:"🍹",tropical_fish:"🐠",truck:"🚚",trumpet:"🎺",tulip:"🌷",tumbler_glass:"🥃",turkey:"🦃",turtle:"🐢",tv:"📺",twisted_rightwards_arrows:"🔀",two_hearts:"💕",two_men_holding_hands:"👬",two_women_holding_hands:"👭",u5272:"🈹",u5408:"🈴",u55b6:"🈺",u6307:"🈯️",u6708:"🈷️",u6709:"🈶",u6e80:"🈵",u7121:"🈚️",u7533:"🈸",u7981:"🈲",u7a7a:"🈳",umbrella:"☔️",unamused:"😒",underage:"🔞",unicorn:"🦄",unlock:"🔓",up:"🆙",upside_down_face:"🙃",v:"✌️",vertical_traffic_light:"🚦",vhs:"📼",vibration_mode:"📳",video_camera:"📹",video_game:"🎮",violin:"🎻",virgo:"♍️",volcano:"🌋",volleyball:"🏐",vs:"🆚",vulcan_salute:"🖖",walking_man:"🚶",walking_woman:"🚶‍♀️",waning_crescent_moon:"🌘",waning_gibbous_moon:"🌖",warning:"⚠️",wastebasket:"🗑",watch:"⌚️",water_buffalo:"🐃",watermelon:"🍉",wave:"👋",wavy_dash:"〰️",waxing_crescent_moon:"🌒",wc:"🚾",weary:"😩",wedding:"💒",weight_lifting_man:"🏋️",weight_lifting_woman:"🏋️‍♀️",whale:"🐳",whale2:"🐋",wheel_of_dharma:"☸️",wheelchair:"♿️",white_check_mark:"✅",white_circle:"⚪️",white_flag:"🏳️",white_flower:"💮",white_large_square:"⬜️",white_medium_small_square:"◽️",white_medium_square:"◻️",white_small_square:"▫️",white_square_button:"🔳",wilted_flower:"🥀",wind_chime:"🎐",wind_face:"🌬",wine_glass:"🍷",wink:"😉",wolf:"🐺",woman:"👩",woman_artist:"👩‍🎨",woman_astronaut:"👩‍🚀",woman_cartwheeling:"🤸‍♀️",woman_cook:"👩‍🍳",woman_facepalming:"🤦‍♀️",woman_factory_worker:"👩‍🏭",woman_farmer:"👩‍🌾",woman_firefighter:"👩‍🚒",woman_health_worker:"👩‍⚕️",woman_judge:"👩‍⚖️",woman_juggling:"🤹‍♀️",woman_mechanic:"👩‍🔧",woman_office_worker:"👩‍💼",woman_pilot:"👩‍✈️",woman_playing_handball:"🤾‍♀️",woman_playing_water_polo:"🤽‍♀️",woman_scientist:"👩‍🔬",woman_shrugging:"🤷‍♀️",woman_singer:"👩‍🎤",woman_student:"👩‍🎓",woman_teacher:"👩‍🏫",woman_technologist:"👩‍💻",woman_with_turban:"👳‍♀️",womans_clothes:"👚",womans_hat:"👒",women_wrestling:"🤼‍♀️",womens:"🚺",world_map:"🗺",worried:"😟",wrench:"🔧",writing_hand:"✍️",x:"❌",yellow_heart:"💛",yen:"💴",yin_yang:"☯️",yum:"😋",zap:"⚡️",zipper_mouth_face:"🤐",zzz:"💤",octocat:':octocat:',showdown:"S"},x.Converter=function(e){"use strict";var r,t,n={},i=[],l=[],o={},a=h,s={parsed:{},raw:"",format:""};for(r in e=e||{},p)p.hasOwnProperty(r)&&(n[r]=p[r]);if("object"!=typeof e)throw Error("Converter expects the passed parameter to be an object, but "+typeof e+" was passed instead.");for(t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);function c(e,r){if(r=r||null,x.helper.isString(e)){if(r=e=x.helper.stdExtName(e),x.extensions[e]){console.warn("DEPRECATION WARNING: "+e+" is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!");var t=x.extensions[e],a=e;if("function"==typeof t&&(t=t(new x.Converter)),x.helper.isArray(t)||(t=[t]),!(a=g(t,a)).valid)throw Error(a.error);for(var n=0;n[ \t]+¨NBSP;<"),!r){if(!window||!window.document)throw new Error("HTMLParser is undefined. If in a webworker or nodejs environment, you need to provide a WHATWG DOM and HTML such as JSDOM");r=window.document}for(var r=r.createElement("div"),t=(r.innerHTML=e,{preList:function(e){for(var r=e.querySelectorAll("pre"),t=[],a=0;a'}else t.push(r[a].innerHTML),r[a].innerHTML="",r[a].setAttribute("prenum",a.toString());return t}(r)}),a=(!function e(r){for(var t=0;t? ?(['"].*['"])?\)$/m))a="";else if(!a){if(a="#"+(t=t||r.toLowerCase().replace(/ ?\n/g," ")),x.helper.isUndefined(l.gUrls[t]))return e;a=l.gUrls[t],x.helper.isUndefined(l.gTitles[t])||(o=l.gTitles[t])}return e='"}return e=(e=(e=(e=(e=l.converter._dispatch("anchors.before",e,i,l)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g,r)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,r)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]??(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,r)).replace(/\[([^\[\]]+)]()()()()()/g,r),i.ghMentions&&(e=e.replace(/(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d.-]+?[a-z\d]+)*))/gim,function(e,r,t,a,n){if("\\"===t)return r+a;if(!x.helper.isString(i.ghMentionsLink))throw new Error("ghMentionsLink option must be a string");t="";return r+'"+a+""})),e=l.converter._dispatch("anchors.after",e,i,l)});var i=/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+?\.[^'">\s]+?)()(\1)?(?=\s|$)(?!["<>])/gi,l=/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]])?(\1)?(?=\s|$)(?!["<>])/gi,c=/()<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>()/gi,m=/(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gim,f=/<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;x.subParser("autoLinks",function(e,r,t){"use strict";return e=(e=(e=t.converter._dispatch("autoLinks.before",e,r,t)).replace(c,s(r))).replace(f,o(r,t)),e=t.converter._dispatch("autoLinks.after",e,r,t)}),x.subParser("simplifiedAutoLinks",function(e,r,t){"use strict";return r.simplifiedAutoLink?(e=t.converter._dispatch("simplifiedAutoLinks.before",e,r,t),e=(e=r.excludeTrailingPunctuationFromURLs?e.replace(l,s(r)):e.replace(i,s(r))).replace(m,o(r,t)),t.converter._dispatch("simplifiedAutoLinks.after",e,r,t)):e}),x.subParser("blockGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("blockGamut.before",e,r,t),e=x.subParser("blockQuotes")(e,r,t),e=x.subParser("headers")(e,r,t),e=x.subParser("horizontalRule")(e,r,t),e=x.subParser("lists")(e,r,t),e=x.subParser("codeBlocks")(e,r,t),e=x.subParser("tables")(e,r,t),e=x.subParser("hashHTMLBlocks")(e,r,t),e=x.subParser("paragraphs")(e,r,t),e=t.converter._dispatch("blockGamut.after",e,r,t)}),x.subParser("blockQuotes",function(e,r,t){"use strict";e=t.converter._dispatch("blockQuotes.before",e,r,t);var a=/(^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+/gm;return r.splitAdjacentBlockquotes&&(a=/^ {0,3}>[\s\S]*?(?:\n\n)/gm),e=(e+="\n\n").replace(a,function(e){return e=(e=(e=e.replace(/^[ \t]*>[ \t]?/gm,"")).replace(/¨0/g,"")).replace(/^[ \t]+$/gm,""),e=x.subParser("githubCodeBlocks")(e,r,t),e=(e=(e=x.subParser("blockGamut")(e,r,t)).replace(/(^|\n)/g,"$1 ")).replace(/(\s*
[^\r]+?<\/pre>)/gm,function(e,r){return r.replace(/^  /gm,"¨0").replace(/¨0/g,"")}),x.subParser("hashBlock")("
\n"+e+"\n
",r,t)}),e=t.converter._dispatch("blockQuotes.after",e,r,t)}),x.subParser("codeBlocks",function(e,n,s){"use strict";e=s.converter._dispatch("codeBlocks.before",e,n,s);return e=(e=(e+="¨0").replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g,function(e,r,t){var a="\n",r=x.subParser("outdent")(r,n,s);return r=x.subParser("encodeCode")(r,n,s),r="
"+(r=(r=(r=x.subParser("detab")(r,n,s)).replace(/^\n+/g,"")).replace(/\n+$/g,""))+(a=n.omitExtraWLInCodeBlocks?"":a)+"
",x.subParser("hashBlock")(r,n,s)+t})).replace(/¨0/,""),e=s.converter._dispatch("codeBlocks.after",e,n,s)}),x.subParser("codeSpans",function(e,n,s){"use strict";return e=(e=void 0===(e=s.converter._dispatch("codeSpans.before",e,n,s))?"":e).replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(e,r,t,a){return a=(a=a.replace(/^([ \t]*)/g,"")).replace(/[ \t]*$/g,""),a=r+""+(a=x.subParser("encodeCode")(a,n,s))+"",a=x.subParser("hashHTMLSpans")(a,n,s)}),e=s.converter._dispatch("codeSpans.after",e,n,s)}),x.subParser("completeHTMLDocument",function(e,r,t){"use strict";if(!r.completeHTMLDocument)return e;e=t.converter._dispatch("completeHTMLDocument.before",e,r,t);var a,n="html",s="\n",o="",i='\n',l="",c="";for(a in void 0!==t.metadata.parsed.doctype&&(s="\n","html"!==(n=t.metadata.parsed.doctype.toString().toLowerCase())&&"html5"!==n||(i='')),t.metadata.parsed)if(t.metadata.parsed.hasOwnProperty(a))switch(a.toLowerCase()){case"doctype":break;case"title":o=""+t.metadata.parsed.title+"\n";break;case"charset":i="html"===n||"html5"===n?'\n':'\n';break;case"language":case"lang":l=' lang="'+t.metadata.parsed[a]+'"',c+='\n';break;default:c+='\n'}return e=s+"\n\n"+o+i+c+"\n\n"+e.trim()+"\n\n",e=t.converter._dispatch("completeHTMLDocument.after",e,r,t)}),x.subParser("detab",function(e,r,t){"use strict";return e=(e=(e=(e=(e=(e=t.converter._dispatch("detab.before",e,r,t)).replace(/\t(?=\t)/g," ")).replace(/\t/g,"¨A¨B")).replace(/¨B(.+?)¨A/g,function(e,r){for(var t=r,a=4-t.length%4,n=0;n/g,">"),e=t.converter._dispatch("encodeAmpsAndAngles.after",e,r,t)}),x.subParser("encodeBackslashEscapes",function(e,r,t){"use strict";return e=(e=(e=t.converter._dispatch("encodeBackslashEscapes.before",e,r,t)).replace(/\\(\\)/g,x.helper.escapeCharactersCallback)).replace(/\\([`*_{}\[\]()>#+.!~=|:-])/g,x.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeBackslashEscapes.after",e,r,t)}),x.subParser("encodeCode",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("encodeCode.before",e,r,t)).replace(/&/g,"&").replace(//g,">").replace(/([*_{}\[\]\\=~-])/g,x.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeCode.after",e,r,t)}),x.subParser("escapeSpecialCharsWithinTagAttributes",function(e,r,t){"use strict";return e=(e=(e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.before",e,r,t)).replace(/<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,function(e){return e.replace(/(.)<\/?code>(?=.)/g,"$1`").replace(/([\\`*_~=|])/g,x.helper.escapeCharactersCallback)})).replace(/-]|-[^>])(?:[^-]|-[^-])*)--)>/gi,function(e){return e.replace(/([\\`*_~=|])/g,x.helper.escapeCharactersCallback)}),e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.after",e,r,t)}),x.subParser("githubCodeBlocks",function(e,s,o){"use strict";return s.ghCodeBlocks?(e=o.converter._dispatch("githubCodeBlocks.before",e,s,o),e=(e=(e+="¨0").replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g,function(e,r,t,a){var n=s.omitExtraWLInCodeBlocks?"":"\n";return a=x.subParser("encodeCode")(a,s,o),a="
"+(a=(a=(a=x.subParser("detab")(a,s,o)).replace(/^\n+/g,"")).replace(/\n+$/g,""))+n+"
",a=x.subParser("hashBlock")(a,s,o),"\n\n¨G"+(o.ghCodeBlocks.push({text:e,codeblock:a})-1)+"G\n\n"})).replace(/¨0/,""),o.converter._dispatch("githubCodeBlocks.after",e,s,o)):e}),x.subParser("hashBlock",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("hashBlock.before",e,r,t)).replace(/(^\n+|\n+$)/g,""),e="\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n",e=t.converter._dispatch("hashBlock.after",e,r,t)}),x.subParser("hashCodeTags",function(e,n,s){"use strict";e=s.converter._dispatch("hashCodeTags.before",e,n,s);return e=x.helper.replaceRecursiveRegExp(e,function(e,r,t,a){t=t+x.subParser("encodeCode")(r,n,s)+a;return"¨C"+(s.gHtmlSpans.push(t)-1)+"C"},"]*>","","gim"),e=s.converter._dispatch("hashCodeTags.after",e,n,s)}),x.subParser("hashElement",function(e,r,t){"use strict";return function(e,r){return r=(r=(r=r.replace(/\n\n/g,"\n")).replace(/^\n/,"")).replace(/\n+$/g,""),r="\n\n¨K"+(t.gHtmlBlocks.push(r)-1)+"K\n\n"}}),x.subParser("hashHTMLBlocks",function(e,r,n){"use strict";e=n.converter._dispatch("hashHTMLBlocks.before",e,r,n);function t(e,r,t,a){return-1!==t.search(/\bmarkdown\b/)&&(e=t+n.converter.makeHtml(r)+a),"\n\n¨K"+(n.gHtmlBlocks.push(e)-1)+"K\n\n"}var a=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"];r.backslashEscapesHTMLTags&&(e=e.replace(/\\<(\/?[^>]+?)>/g,function(e,r){return"<"+r+">"}));for(var s=0;s]*>)","im"),i="<"+a[s]+"\\b[^>]*>",l="";-1!==(c=x.helper.regexIndexOf(e,o));){var c=x.helper.splitAtIndex(e,c),u=x.helper.replaceRecursiveRegExp(c[1],t,i,l,"im");if(u===c[1])break;e=c[0].concat(u)}return e=e.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,x.subParser("hashElement")(e,r,n)),e=(e=x.helper.replaceRecursiveRegExp(e,function(e){return"\n\n¨K"+(n.gHtmlBlocks.push(e)-1)+"K\n\n"},"^ {0,3}\x3c!--","--\x3e","gm")).replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,x.subParser("hashElement")(e,r,n)),e=n.converter._dispatch("hashHTMLBlocks.after",e,r,n)}),x.subParser("hashHTMLSpans",function(e,r,t){"use strict";function a(e){return"¨C"+(t.gHtmlSpans.push(e)-1)+"C"}return e=(e=(e=(e=(e=t.converter._dispatch("hashHTMLSpans.before",e,r,t)).replace(/<[^>]+?\/>/gi,a)).replace(/<([^>]+?)>[\s\S]*?<\/\1>/g,a)).replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g,a)).replace(/<[^>]+?>/gi,a),e=t.converter._dispatch("hashHTMLSpans.after",e,r,t)}),x.subParser("unhashHTMLSpans",function(e,r,t){"use strict";e=t.converter._dispatch("unhashHTMLSpans.before",e,r,t);for(var a=0;a]*>\\s*]*>","^ {0,3}\\s*
","gim"),e=s.converter._dispatch("hashPreCodeTags.after",e,n,s)}),x.subParser("headers",function(e,n,s){"use strict";e=s.converter._dispatch("headers.before",e,n,s);var o=isNaN(parseInt(n.headerLevelStart))?1:parseInt(n.headerLevelStart),r=n.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,t=n.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm,r=(e=(e=e.replace(r,function(e,r){var t=x.subParser("spanGamut")(r,n,s),r=n.noHeaderId?"":' id="'+i(r)+'"',r=""+t+"";return x.subParser("hashBlock")(r,n,s)})).replace(t,function(e,r){var t=x.subParser("spanGamut")(r,n,s),r=n.noHeaderId?"":' id="'+i(r)+'"',a=o+1,r=""+t+"";return x.subParser("hashBlock")(r,n,s)}),n.requireSpaceBeforeHeadingText?/^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm:/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm);function i(e){var r=e=n.customizedHeaderId&&(r=e.match(/\{([^{]+?)}\s*$/))&&r[1]?r[1]:e,e=x.helper.isString(n.prefixHeaderId)?n.prefixHeaderId:!0===n.prefixHeaderId?"section-":"";return n.rawPrefixHeaderId||(r=e+r),r=(n.ghCompatibleHeaderId?r.replace(/ /g,"-").replace(/&/g,"").replace(/¨T/g,"").replace(/¨D/g,"").replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g,""):n.rawHeaderId?r.replace(/ /g,"-").replace(/&/g,"&").replace(/¨T/g,"¨").replace(/¨D/g,"$").replace(/["']/g,"-"):r.replace(/[^\w]/g,"")).toLowerCase(),n.rawPrefixHeaderId&&(r=e+r),s.hashLinkCounts[r]?r=r+"-"+s.hashLinkCounts[r]++:s.hashLinkCounts[r]=1,r}return e=e.replace(r,function(e,r,t){var a=t,a=(n.customizedHeaderId&&(a=t.replace(/\s?\{([^{]+?)}\s*$/,"")),x.subParser("spanGamut")(a,n,s)),t=n.noHeaderId?"":' id="'+i(t)+'"',r=o-1+r.length,t=""+a+"";return x.subParser("hashBlock")(t,n,s)}),e=s.converter._dispatch("headers.after",e,n,s)}),x.subParser("horizontalRule",function(e,r,t){"use strict";e=t.converter._dispatch("horizontalRule.before",e,r,t);var a=x.subParser("hashBlock")("
",r,t);return e=(e=(e=e.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm,a)).replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm,a)).replace(/^ {0,2}( ?_){3,}[ \t]*$/gm,a),e=t.converter._dispatch("horizontalRule.after",e,r,t)}),x.subParser("images",function(e,r,d){"use strict";function l(e,r,t,a,n,s,o,i){var l=d.gUrls,c=d.gTitles,u=d.gDimensions;if(t=t.toLowerCase(),i=i||"",-1? ?(['"].*['"])?\)$/m))a="";else if(""===a||null===a){if(a="#"+(t=""!==t&&null!==t?t:r.toLowerCase().replace(/ ?\n/g," ")),x.helper.isUndefined(l[t]))return e;a=l[t],x.helper.isUndefined(c[t])||(i=c[t]),x.helper.isUndefined(u[t])||(n=u[t].width,s=u[t].height)}r=r.replace(/"/g,""").replace(x.helper.regexes.asteriskDashAndColon,x.helper.escapeCharactersCallback);e=''+r+'"}return e=(e=(e=(e=(e=(e=d.converter._dispatch("images.before",e,r,d)).replace(/!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,l)).replace(/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,function(e,r,t,a,n,s,o,i){return l(e,r,t,a=a.replace(/\s/g,""),n,s,0,i)})).replace(/!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,l)).replace(/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,l)).replace(/!\[([^\[\]]+)]()()()()()/g,l),e=d.converter._dispatch("images.after",e,r,d)}),x.subParser("italicsAndBold",function(e,r,t){"use strict";return e=t.converter._dispatch("italicsAndBold.before",e,r,t),e=r.literalMidWordUnderscores?(e=(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""})).replace(/\b_(\S[\s\S]*?)_\b/g,function(e,r){return""+r+""}):(e=(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/_([^\s_][\s\S]*?)_/g,function(e,r){return/\S$/.test(r)?""+r+"":e}),e=r.literalMidWordAsterisks?(e=(e=e.replace(/([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g,function(e,r,t){return r+""+t+""})).replace(/([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g,function(e,r,t){return r+""+t+""})).replace(/([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g,function(e,r,t){return r+""+t+""}):(e=(e=e.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/\*\*(\S[\s\S]*?)\*\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/\*([^\s*][\s\S]*?)\*/g,function(e,r){return/\S$/.test(r)?""+r+"":e}),e=t.converter._dispatch("italicsAndBold.after",e,r,t)}),x.subParser("lists",function(e,d,c){"use strict";function p(e,r){c.gListLevel++,e=e.replace(/\n{2,}$/,"\n");var t=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,l=/\n[ \t]*\n(?!¨0)/.test(e+="¨0");return d.disableForced4SpacesIndentedSublists&&(t=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm),e=(e=e.replace(t,function(e,r,t,a,n,s,o){o=o&&""!==o.trim();var n=x.subParser("outdent")(n,d,c),i="";return s&&d.tasklists&&(i=' class="task-list-item" style="list-style-type: none;"',n=n.replace(/^[ \t]*\[(x|X| )?]/m,function(){var e='"+(n=(n=r||-1\n"})).replace(/¨0/g,""),c.gListLevel--,e=r?e.replace(/\s+$/,""):e}function h(e,r){if("ol"===r){r=e.match(/^ *(\d+)\./);if(r&&"1"!==r[1])return' start="'+r[1]+'"'}return""}function n(n,s,o){var e,i=d.disableForced4SpacesIndentedSublists?/^ ?\d+\.[ \t]/gm:/^ {0,3}\d+\.[ \t]/gm,l=d.disableForced4SpacesIndentedSublists?/^ ?[*+-][ \t]/gm:/^ {0,3}[*+-][ \t]/gm,c="ul"===s?i:l,u="";return-1!==n.search(c)?function e(r){var t=r.search(c),a=h(n,s);-1!==t?(u+="\n\n<"+s+a+">\n"+p(r.slice(0,t),!!o)+"\n",c="ul"===(s="ul"===s?"ol":"ul")?i:l,e(r.slice(t))):u+="\n\n<"+s+a+">\n"+p(r,!!o)+"\n"}(n):(e=h(n,s),u="\n\n<"+s+e+">\n"+p(n,!!o)+"\n"),u}return e=c.converter._dispatch("lists.before",e,d,c),e+="¨0",e=(e=c.gListLevel?e.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(e,r,t){return n(r,-1"),i+="

",n.push(i))}for(s=n.length,o=0;o]*>\s*]*>/.test(c)&&(u=!0)}n[o]=c}return e=(e=(e=n.join("\n")).replace(/^\n+/g,"")).replace(/\n+$/g,""),t.converter._dispatch("paragraphs.after",e,r,t)}),x.subParser("runExtension",function(e,r,t,a){"use strict";return e.filter?r=e.filter(r,a.converter,t):e.regex&&((a=e.regex)instanceof RegExp||(a=new RegExp(a,"g")),r=r.replace(a,e.replace)),r}),x.subParser("spanGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("spanGamut.before",e,r,t),e=x.subParser("codeSpans")(e,r,t),e=x.subParser("escapeSpecialCharsWithinTagAttributes")(e,r,t),e=x.subParser("encodeBackslashEscapes")(e,r,t),e=x.subParser("images")(e,r,t),e=x.subParser("anchors")(e,r,t),e=x.subParser("autoLinks")(e,r,t),e=x.subParser("simplifiedAutoLinks")(e,r,t),e=x.subParser("emoji")(e,r,t),e=x.subParser("underline")(e,r,t),e=x.subParser("italicsAndBold")(e,r,t),e=x.subParser("strikethrough")(e,r,t),e=x.subParser("ellipsis")(e,r,t),e=x.subParser("hashHTMLSpans")(e,r,t),e=x.subParser("encodeAmpsAndAngles")(e,r,t),r.simpleLineBreaks?/\n\n¨K/.test(e)||(e=e.replace(/\n+/g,"
\n")):e=e.replace(/ +\n/g,"
\n"),e=t.converter._dispatch("spanGamut.after",e,r,t)}),x.subParser("strikethrough",function(e,t,a){"use strict";return t.strikethrough&&(e=(e=a.converter._dispatch("strikethrough.before",e,t,a)).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(e,r){return r=r,""+(r=t.simplifiedAutoLink?x.subParser("simplifiedAutoLinks")(r,t,a):r)+""}),e=a.converter._dispatch("strikethrough.after",e,t,a)),e}),x.subParser("stripLinkDefinitions",function(i,l,c){"use strict";function e(e,r,t,a,n,s,o){return r=r.toLowerCase(),i.toLowerCase().split(r).length-1<2?e:(t.match(/^data:.+?\/.+?;base64,/)?c.gUrls[r]=t.replace(/\s/g,""):c.gUrls[r]=x.subParser("encodeAmpsAndAngles")(t,l,c),s?s+o:(o&&(c.gTitles[r]=o.replace(/"|'/g,""")),l.parseImgDimensions&&a&&n&&(c.gDimensions[r]={width:a,height:n}),""))}return i=(i=(i=(i+="¨0").replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm,e)).replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,e)).replace(/¨0/,"")}),x.subParser("tables",function(e,y,P){"use strict";if(!y.tables)return e;function r(e){for(var r=e.split("\n"),t=0;t"+(n=x.subParser("spanGamut")(n,y,P))+"\n"));for(t=0;t"+x.subParser("spanGamut")(i,y,P)+"\n"));h.push(_)}for(var m=d,f=h,b="\n\n\n",w=m.length,k=0;k\n\n\n",k=0;k\n";for(var v=0;v\n"}return b+="\n
\n"}return e=(e=(e=(e=P.converter._dispatch("tables.before",e,y,P)).replace(/\\(\|)/g,x.helper.escapeCharactersCallback)).replace(/^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,r)).replace(/^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm,r),e=P.converter._dispatch("tables.after",e,y,P)}),x.subParser("underline",function(e,r,t){"use strict";return r.underline?(e=t.converter._dispatch("underline.before",e,r,t),e=(e=r.literalMidWordUnderscores?(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""}):(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/(_)/g,x.helper.escapeCharactersCallback),t.converter._dispatch("underline.after",e,r,t)):e}),x.subParser("unescapeSpecialChars",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("unescapeSpecialChars.before",e,r,t)).replace(/¨E(\d+)E/g,function(e,r){r=parseInt(r);return String.fromCharCode(r)}),e=t.converter._dispatch("unescapeSpecialChars.after",e,r,t)}),x.subParser("makeMarkdown.blockquote",function(e,r){"use strict";var t="";if(e.hasChildNodes())for(var a=e.childNodes,n=a.length,s=0;s ")}),x.subParser("makeMarkdown.codeBlock",function(e,r){"use strict";var t=e.getAttribute("language"),e=e.getAttribute("precodenum");return"```"+t+"\n"+r.preList[e]+"\n```"}),x.subParser("makeMarkdown.codeSpan",function(e){"use strict";return"`"+e.innerHTML+"`"}),x.subParser("makeMarkdown.emphasis",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="*";for(var a=e.childNodes,n=a.length,s=0;s",e.hasAttribute("width")&&e.hasAttribute("height")&&(r+=" ="+e.getAttribute("width")+"x"+e.getAttribute("height")),e.hasAttribute("title")&&(r+=' "'+e.getAttribute("title")+'"'),r+=")"),r}),x.subParser("makeMarkdown.links",function(e,r){"use strict";var t="";if(e.hasChildNodes()&&e.hasAttribute("href")){for(var a=e.childNodes,n=a.length,t="[",s=0;s"),e.hasAttribute("title")&&(t+=' "'+e.getAttribute("title")+'"'),t+=")"}return t}),x.subParser("makeMarkdown.list",function(e,r,t){"use strict";var a="";if(!e.hasChildNodes())return"";for(var n=e.childNodes,s=n.length,o=e.getAttribute("start")||1,i=0;i"+r.preList[e]+""}),x.subParser("makeMarkdown.strikethrough",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="~~";for(var a=e.childNodes,n=a.length,s=0;str>th"),s=e.querySelectorAll("tbody>tr"),o=0;o/g,"\\$1>")).replace(/^#/gm,"\\#")).replace(/^(\s*)([-=]{3,})(\s*)$/,"$1\\$2$3")).replace(/^( {0,3}\d+)\./gm,"$1\\.")).replace(/^( {0,3})([+-])/gm,"$1\\$2")).replace(/]([\s]*)\(/g,"\\]$1\\(")).replace(/^ {0,3}\[([\S \t]*?)]:/gm,"\\[$1]:")});"function"==typeof define&&define.amd?define(function(){"use strict";return x}):"undefined"!=typeof module&&module.exports?module.exports=x:this.showdown=x}.call(this); diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 4c688784..fac75df3 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -67,7 +67,7 @@ if ($SYNTAXHIGHLIGHTING) : endif; if ($MARKDOWN) : ?> - + diff --git a/tpl/page.php b/tpl/page.php index ca84e8f6..2ff6ea7e 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -45,7 +45,7 @@ if ($SYNTAXHIGHLIGHTING): endif; if ($MARKDOWN): ?> - + From 36cb37c02963ca822903d571c97c9a6004c79746 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 13 Mar 2022 20:18:51 +0100 Subject: [PATCH 44/68] prevent error when attachments are disabled, but paste with attachment gets displayed --- CHANGELOG.md | 1 + js/privatebin.js | 6 ++++++ tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c6137f3..a3188569 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419) * CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419) * CHANGED: Drop support for attachment download in IE + * FIXED: Error when attachments are disabled, but paste with attachment gets displayed * **1.3.5 (2021-04-05)** * ADDED: Translations for Hebrew, Lithuanian, Indonesian and Catalan * ADDED: Make the project info configurable (#681) diff --git a/js/privatebin.js b/js/privatebin.js index 53474bd1..a1308620 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -2816,6 +2816,9 @@ jQuery.PrivateBin = (function($, RawDeflate) { */ me.setAttachment = function(attachmentData, fileName) { + // skip, if attachments got disabled + if (!$attachmentLink || !$attachmentPreview) return; + // data URI format: data:[][;base64], // position in data URI string of where data begins @@ -2859,6 +2862,9 @@ jQuery.PrivateBin = (function($, RawDeflate) { */ me.showAttachment = function() { + // skip, if attachments got disabled + if (!$attachment || !$attachmentPreview) return; + $attachment.removeClass('hidden'); if (attachmentHasPreview) { diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index fac75df3..7fe82e04 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -73,7 +73,7 @@ endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index 2ff6ea7e..5823f71b 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -51,7 +51,7 @@ endif; ?> - + From b6db556b3416b1c9cc4fc6631ab021db9ab12851 Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Mon, 21 Mar 2022 19:11:47 +0100 Subject: [PATCH 45/68] New translations en.json (Finnish) --- i18n/fi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/fi.json b/i18n/fi.json index 0ad8c410..ce343e25 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -186,5 +186,5 @@ "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Käy tässä linkissä nähdäksesi viestin. URL:n antaminen kenellekään antaa heidänkin päästä katsomeen viestiä. ", "URL shortener may expose your decrypt key in URL.": "URL-lyhentäjä voi paljastaa purkuavaimesi URL:ssä.", "Save paste": "Tallenna paste", - "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." + "Your IP is not authorized to create pastes.": "IP:llesi ei ole annettu oikeutta luoda pasteja." } From 960faf44173846d9a8bd14b562626f24fd835bae Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 27 Mar 2022 07:58:25 +0200 Subject: [PATCH 46/68] wording Co-authored-by: rugk --- js/privatebin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/privatebin.js b/js/privatebin.js index a1308620..acef8f65 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -2802,7 +2802,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { } ); - // Get Blob URL + // Get blob URL return window.URL.createObjectURL(blob); } From 75dc346f0fbe81c843ab0aadbfc697ded33c14bd Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 27 Mar 2022 08:27:24 +0200 Subject: [PATCH 47/68] be more specific on the base type match and less specific on the subtype, in order to fail-safe (avoid being tricked into not sanitizing - the mime type is a user provided input) --- js/privatebin.js | 8 ++++---- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index a1308620..5f61de13 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -2843,7 +2843,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { // prevents executing embedded scripts when CSP is not set and user // right-clicks/long-taps and opens the SVG in a new tab - prevented // in the preview by use of an img tag, which disables scripts, too - if (mimeType.match(/image\/svg/i)) { + if (mimeType.match(/^image\/.*svg/i)) { const sanitizedData = DOMPurify.sanitize( decodedData, purifySvgConfig @@ -3072,13 +3072,13 @@ jQuery.PrivateBin = (function($, RawDeflate) { me.handleBlobAttachmentPreview = function ($targetElement, blobUrl, mimeType) { if (blobUrl) { attachmentHasPreview = true; - if (mimeType.match(/image\//i)) { + if (mimeType.match(/^image\//i)) { $targetElement.html( $(document.createElement('img')) .attr('src', blobUrl) .attr('class', 'img-thumbnail') ); - } else if (mimeType.match(/video\//i)) { + } else if (mimeType.match(/^video\//i)) { $targetElement.html( $(document.createElement('video')) .attr('controls', 'true') @@ -3089,7 +3089,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { .attr('type', mimeType) .attr('src', blobUrl)) ); - } else if (mimeType.match(/audio\//i)) { + } else if (mimeType.match(/^audio\//i)) { $targetElement.html( $(document.createElement('audio')) .attr('controls', 'true') diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 7fe82e04..e044bbb4 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -73,7 +73,7 @@ endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index 5823f71b..20792551 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -51,7 +51,7 @@ endif; ?> - + From 40d35ab3c20d674fc9bfc75af4b9a283071dd66b Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 27 Mar 2022 08:28:54 +0200 Subject: [PATCH 48/68] update SRI-hashes --- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index e044bbb4..ec39effb 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -73,7 +73,7 @@ endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index 20792551..0beda2cd 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -51,7 +51,7 @@ endif; ?> - + From 11b16fc6fd5eca59cabf49ae65fa0a122ed6e669 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 27 Mar 2022 08:45:33 +0200 Subject: [PATCH 49/68] removed directive needed for the PDF preview in FireFox < 78 fixed in https://bugzilla.mozilla.org/show_bug.cgi?id=1582115 and https://bugzilla.mozilla.org/show_bug.cgi?id=1638826 for FF 78 --- cfg/conf.sample.php | 2 +- lib/Configuration.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index 4d21dc3e..e4586854 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -87,7 +87,7 @@ languageselection = false ; async functions and display an error if not and for Chrome to enable ; webassembly support (used for zlib compression). You can remove it if Chrome ; doesn't need to be supported and old browsers don't need to be warned. -; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval' resource:; 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" +; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' '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" ; 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 diff --git a/lib/Configuration.php b/lib/Configuration.php index eac6854f..de065219 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -54,7 +54,7 @@ class Configuration 'urlshortener' => '', 'qrcode' => true, 'icon' => 'identicon', - 'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\' resource:; 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', + 'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'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, 'httpwarning' => true, 'compression' => 'zlib', From 835fbe0e2f62246fdb49c4a3e418c8c851b0e504 Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Sun, 27 Mar 2022 10:56:23 +0200 Subject: [PATCH 50/68] New translations en.json (Finnish) --- i18n/fi.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/fi.json b/i18n/fi.json index ce343e25..90b56733 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -15,11 +15,11 @@ ], "Paste is limited to %s of encrypted data.": "Paste on rajoitettu kokoon %s salattua dataa.", "Invalid data.": "Virheellinen data.", - "You are unlucky. Try again.": "Olet epäonnekas. Yritä uudelleen", + "You are unlucky. Try again.": "Olet epäonnekas. Yritä uudelleen.", "Error saving comment. Sorry.": "Virhe kommenttia tallentaessa. Anteeksi.", "Error saving paste. Sorry.": "Virhe pastea tallentaessa. Anteeksi.", "Invalid paste ID.": "Virheellinen paste ID.", - "Paste is not of burn-after-reading type.": "Paste ei ole polta-lukemisen-jälkeen-tyyppiä", + "Paste is not of burn-after-reading type.": "Paste ei ole polta-lukemisen-jälkeen-tyyppiä.", "Wrong deletion token. Paste was not deleted.": "Virheellinen poistotunniste. Pastea ei poistettu.", "Paste was properly deleted.": "Paste poistettiin kunnolla.", "JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScriptiä tarvitaan jotta %s toimisi. Anteeksi haitasta.", From f2e0c1a70100033fa97c150453fb8df672033a26 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Wed, 30 Mar 2022 06:05:37 +0200 Subject: [PATCH 51/68] upgrade to zlib 1.2.12 --- CHANGELOG.md | 2 +- js/common.js | 2 +- js/zlib-1.2.11.wasm | Bin 61388 -> 0 bytes js/{zlib-1.2.11.js => zlib-1.2.12.js} | 4 ++-- js/zlib-1.2.12.wasm | Bin 0 -> 58814 bytes tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 js/zlib-1.2.11.wasm rename js/{zlib-1.2.11.js => zlib-1.2.12.js} (97%) create mode 100644 js/zlib-1.2.12.wasm diff --git a/CHANGELOG.md b/CHANGELOG.md index a3188569..af5b7238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * ADDED: Set CSP also as meta tag, to deal with misconfigured webservers mangling the HTTP header * ADDED: Sanitize SVG preview, preventing script execution in instance context * CHANGED: Language selection cookie only transmitted over HTTPS (#472) - * CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.3 + * CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21, Showdown 2.0.3 & zlib 1.2.12 * CHANGED: Removed automatic `.ini` configuration file migration (#808) * CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419) * CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419) diff --git a/js/common.js b/js/common.js index 290c3996..7a68c7ed 100644 --- a/js/common.js +++ b/js/common.js @@ -12,7 +12,7 @@ global.WebCrypto = require('@peculiar/webcrypto').Crypto; // application libraries to test global.$ = global.jQuery = require('./jquery-3.6.0'); global.RawDeflate = require('./rawinflate-0.3').RawDeflate; -global.zlib = require('./zlib-1.2.11').zlib; +global.zlib = require('./zlib-1.2.12').zlib; require('./prettify'); global.prettyPrint = window.PR.prettyPrint; global.prettyPrintOne = window.PR.prettyPrintOne; diff --git a/js/zlib-1.2.11.wasm b/js/zlib-1.2.11.wasm deleted file mode 100644 index d98ea382eaee10be7c971f914b704f9bd455b79a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61388 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 WD352BD0QslvC@eeWU;a7-v0n>j(+F> literal 0 HcmV?d00001 diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index ec39effb..41fa2deb 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -55,7 +55,7 @@ if ($ZEROBINCOMPATIBILITY) : - + diff --git a/tpl/page.php b/tpl/page.php index 0beda2cd..df2031b9 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -34,7 +34,7 @@ if ($ZEROBINCOMPATIBILITY): - + Date: Mon, 4 Apr 2022 14:17:40 +0200 Subject: [PATCH 52/68] Fix wrong env name for GCS bucket --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index da6812e0..73f5adc6 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -203,7 +203,7 @@ and the `value` column in the `config` table need to be `VARCHAR2(4000)`. If you want to deploy PrivateBin in a serverless manner in the Google Cloud, you can choose the `GoogleCloudStorage` as backend. To use this backend, you create a GCS bucket and specify the name as the model option `bucket`. Alternatively, -you can set the name through the environment variable PASTEBIN_GCS_BUCKET. +you can set the name through the environment variable `PRIVATEBIN_GCS_BUCKET`. The default prefix for pastes stored in the bucket is `pastes`. To change the prefix, specify the option `prefix`. From f0d0daffcc295bc8081dda9f3116e67651b63fa4 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 5 Apr 2022 07:22:07 +0200 Subject: [PATCH 53/68] enable and credit new Finnish translation --- CHANGELOG.md | 4 ++-- CREDITS.md | 12 ++++++------ js/privatebin.js | 6 +++--- lib/I18n.php | 4 ++-- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af5b7238..9526c7df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # PrivateBin version history - * **1.4 (not yet released)** - * ADDED: Translations for Corsican, Estonian and Lojban + * **1.4 (2022-04-09)** + * ADDED: Translations for Corsican, Estonian, Finnish and Lojban * ADDED: new HTTP headers improving security (#765) * ADDED: Download button for paste text (#774) * ADDED: Opt-out of federated learning of cohorts (FLoC) (#776) diff --git a/CREDITS.md b/CREDITS.md index 536042b1..e40e45ab 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -2,18 +2,17 @@ ## Active contributors -Simon Rupf - current developer and maintainer -rugk - security review, doc improvment, JS refactoring & various other stuff -R4SAS - python client, compression, blob URI to support larger attachments +* Simon Rupf - current developer and maintainer +* rugk - security review, doc improvment, JS refactoring & various other stuff +* R4SAS - python client, compression, blob URI to support larger attachments ## Past contributions -Sébastien Sauvage - original idea and main developer - +* Sébastien Sauvage - original idea and main developer * Alexey Gladkov - syntax highlighting * Greg Knaddison - robots.txt * MrKooky - HTML5 markup, CSS cleanup -* Simon Rupf - WebCrypto, unit tests, containers images, database backend, MVC, configuration, i18n +* Simon Rupf - WebCrypto, unit tests, container images, database backend, MVC, configuration, i18n * Hexalyse - Password protection * Viktor Stanchev - File upload support * azlux - Tab character input support @@ -56,3 +55,4 @@ Sébastien Sauvage - original idea and main developer * sarnane - Estonian * foxsouns - Lojban * Patriccollu di Santa Maria è Sichè - Corsican +* Markus Mikkonen - Finnish diff --git a/js/privatebin.js b/js/privatebin.js index f302818b..21ba5438 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -627,7 +627,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { * @prop {string[]} * @readonly */ - const supportedLanguages = ['bg', 'ca', 'co', 'cs', 'de', 'es', 'et', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh']; + const supportedLanguages = ['bg', 'ca', 'co', 'cs', 'de', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh']; /** * built in language @@ -793,7 +793,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { /** * per language functions to use to determine the plural form * - * @see {@link https://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html} + * @see {@link https://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html} * @name I18n.getPluralForm * @function * @param {int} n @@ -823,7 +823,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { return n % 10 === 1 && n % 100 !== 11 ? 0 : (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2); case 'sl': return n % 100 === 1 ? 1 : (n % 100 === 2 ? 2 : (n % 100 === 3 || n % 100 === 4 ? 3 : 0)); - // bg, ca, de, en, es, et, hu, it, nl, no, pt + // bg, ca, de, en, es, et, fi, hu, it, nl, no, pt default: return n !== 1 ? 1 : 0; } diff --git a/lib/I18n.php b/lib/I18n.php index 1321c4a2..7b64655e 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -305,7 +305,7 @@ class I18n /** * determines the plural form to use based on current language and given number * - * From: https://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html + * From: https://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html * * @access protected * @static @@ -336,7 +336,7 @@ class I18n return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2); case 'sl': return $n % 100 == 1 ? 1 : ($n % 100 == 2 ? 2 : ($n % 100 == 3 || $n % 100 == 4 ? 3 : 0)); - // bg, ca, de, en, es, et, hu, it, nl, no, pt + // bg, ca, de, en, es, et, fi, hu, it, nl, no, pt default: return $n != 1 ? 1 : 0; } diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 41fa2deb..581ab81b 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -73,7 +73,7 @@ endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index df2031b9..49d111b1 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -51,7 +51,7 @@ endif; ?> - + From 3a7350c5c4ed4d6b8e2d501b0c5bf9fec8133867 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 5 Apr 2022 07:28:25 +0200 Subject: [PATCH 54/68] reword and reformat documents --- INSTALL.md | 124 ++++++++++++++++++++++++++++++----------------------- README.md | 51 +++++++++++----------- 2 files changed, 97 insertions(+), 78 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 73f5adc6..ecdbb2dc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,39 +1,47 @@ # Installation **TL;DR:** Download the -[latest release archive](https://github.com/PrivateBin/PrivateBin/releases/latest) (with the link labelled as „Source code (…)“) -and extract it in your web hosts folder where you want to install your PrivateBin -instance. We try to provide a mostly safe default configuration, but we urge you to -check the [security section](#hardening-and-security) below and the [configuration -options](#configuration) to adjust as you see fit. +[latest release archive](https://github.com/PrivateBin/PrivateBin/releases/latest) +(with the link labelled as "Source code (…)") and extract it in your web hosts +folder where you want to install your PrivateBin instance. We try to provide a +mostly safe default configuration, but we urge you to check the +[security section](#hardening-and-security) below and the +[configuration options](#configuration) to adjust as you see fit. -**NOTE:** See [our FAQ](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-can-i-securely-clonedownload-your-project) for information how to securely download the PrivateBin release files. +**NOTE:** See our [FAQ entry on securely downloading release files](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-can-i-securely-clonedownload-your-project) +for more information. -**NOTE:** There is a [ansible](https://ansible.com) role by @e1mo available to install and configure PrivateBin on your server. It's available on [ansible galaxy](https://galaxy.ansible.com/e1mo/privatebin) ([source code](https://git.sr.ht/~e1mo/ansible-role-privatebin)). +**NOTE:** There is a [ansible](https://ansible.com) role by @e1mo available to +install and configure PrivateBin on your server. It's available on +[ansible galaxy](https://galaxy.ansible.com/e1mo/privatebin) +([source code](https://git.sr.ht/~e1mo/ansible-role-privatebin)). -### Minimal requirements +### Minimal Requirements - PHP version 7.0 or above - - Or PHP version 5.6 AND _one_ of the following sources of cryptographically safe randomness: - - [Libsodium](https://download.libsodium.org/libsodium/content/installation/) and it's [PHP extension](https://paragonie.com/book/pecl-libsodium/read/00-intro.md#installing-libsodium) - - open_basedir access to `/dev/urandom` - - mcrypt extension (mcrypt needs to be able to access `/dev/urandom`. This means if `open_basedir` is set, it must include this file.) + - Or PHP version 5.6 AND _one_ of the following sources of cryptographically + safe randomness: + - [Libsodium](https://download.libsodium.org/libsodium/content/installation/) + and it's [PHP extension](https://paragonie.com/book/pecl-libsodium/read/00-intro.md#installing-libsodium) + - `open_basedir` access to `/dev/urandom` + - mcrypt extension AND `open_basedir` access to `/dev/urandom` - com_dotnet extension - GD extension - zlib extension -- some disk space or (optionally) a database supported by [PDO](https://php.net/manual/book.pdo.php) -- ability to create files and folders in the installation directory and the PATH defined in index.php -- A web browser with JavaScript support +- some disk space or a database supported by [PDO](https://php.net/manual/book.pdo.php) +- ability to create files and folders in the installation directory and the PATH + defined in index.php +- A web browser with JavaScript and (optional) WebAssembly support -## Hardening and security +## Hardening and Security -### Changing the path +### Changing the Path -In the index.php you can define a different `PATH`. This is useful to secure your -installation. You can move the configuration, data files, templates and PHP +In the index.php you can define a different `PATH`. This is useful to secure +your installation. You can move the configuration, data files, templates and PHP libraries (directories cfg, doc, data, lib, tpl, tst and vendor) outside of your -document root. This new location must still be accessible to your webserver / PHP -process (see also +document root. This new location must still be accessible to your webserver and +PHP process (see also [open_basedir setting](https://secure.php.net/manual/en/ini.core.php#ini.open-basedir)). > #### PATH Example @@ -42,24 +50,25 @@ process (see also > http://example.com/paste/ > > The full path of PrivateBin on your webserver is: -> /home/example.com/htdocs/paste +> /srv/example.com/htdocs/paste > > When setting the path like this: > define('PATH', '../../secret/privatebin/'); > -> PrivateBin will look for your includes / data here: -> /home/example.com/secret/privatebin +> PrivateBin will look for your includes and data here: +> /srv/example.com/secret/privatebin ### Changing the config path only In situations where you want to keep the PrivateBin static files separate from the rest of your data, or you want to reuse the installation files on multiple vhosts, -you may only want to change the `conf.php`. In this instance, you can set the +you may only want to change the `conf.php`. In this case, you can set the `CONFIG_PATH` environment variable to the absolute path to the `conf.php` file. This can be done in your web server's virtual host config, the PHP config, or in -the index.php if you choose to customize it. +the index.php, if you choose to customize it. -Note that your PHP process will need read access to the config wherever it may be. +Note that your PHP process will need read access to the configuration file, +wherever it may be. > #### CONFIG_PATH example > Setting the value in an Apache Vhost: @@ -73,23 +82,27 @@ Note that your PHP process will need read access to the config wherever it may b ### Transport security -When setting up PrivateBin, also set up HTTPS, if you haven't already. Without HTTPS -PrivateBin is not secure, as the JavaScript files could be manipulated during transmission. -For more information on this, see our [FAQ entry on HTTPS setup](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-should-i-setup-https). +When setting up PrivateBin, also set up HTTPS, if you haven't already. Without +HTTPS PrivateBin is not secure, as the JavaScript or WebAssembly files could be +manipulated during transmission. For more information on this, see our +[FAQ entry on HTTPS setup recommendations](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-should-i-setup-https). ### File-level permissions -After completing the installation, you should make sure, other users on the system cannot read the config file or the `data/` directory, as – depending on your configuration – potential secret information are saved there. +After completing the installation, you should make sure, that other users on the +system cannot read the config file or the `data/` directory, as – depending on +your configuration – potentially sensitive information may be stored in there. -See [this FAQ item](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#what-are-the-recommended-file-and-folder-permissions-for-privatebin) for a detailed guide on how to "harden" the permissions of files and folders. +See our [FAQ entry on permissions](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#what-are-the-recommended-file-and-folder-permissions-for-privatebin) +for a detailed guide on how to "harden" access to files and folders. ## Configuration In the file `cfg/conf.php` you can configure PrivateBin. A `cfg/conf.sample.php` -is provided containing all options and default values. You can copy it to -`cfg/conf.php` and adapt it as needed. Alternatively you can copy it anywhere and -set the `CONFIG_PATH` environment variable (see above notes). The config file is -divided into multiple sections, which are enclosed in square brackets. +is provided containing all options and their default values. You can copy it to +`cfg/conf.php` and change it as needed. Alternatively you can copy it anywhere +and set the `CONFIG_PATH` environment variable (see above notes). The config +file is divided into multiple sections, which are enclosed in square brackets. In the `[main]` section you can enable or disable the discussion feature, set the limit of stored pastes and comments in bytes. The `[traffic]` section lets @@ -107,28 +120,28 @@ A `robots.txt` file is provided in the root dir of PrivateBin. It disallows all robots from accessing your pastes. It is recommend to place it into the root of your web directory if you have installed PrivateBin in a subdirectory. Make sure to adjust it, so that the file paths match your installation. Of course also -adjust the file if you already use a `robots.txt`. +adjust the file, if you already use a `robots.txt`. A `.htaccess.disabled` file is provided in the root dir of PrivateBin. It blocks some known robots and link-scanning bots. If you use Apache, you can rename the file to `.htaccess` to enable this feature. If you use another webserver, you have to configure it manually to do the same. -### When using Cloudflare +### On using Cloudflare -If you want to use PrivateBin behind Cloudflare, make sure you have disabled the Rocket -loader and unchecked "Javascript" for Auto Minify, found in your domain settings, -under "Speed". (More information -[in this FAQ entry](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#user-content-how-to-make-privatebin-work-when-using-cloudflare-for-ddos-protection)) +If you want to use PrivateBin behind Cloudflare, make sure you have disabled the +Rocket loader and unchecked "Javascript" for Auto Minify, found in your domain +settings, under "Speed". More information can be found in our +[FAQ entry on Cloudflare related issues](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#user-content-how-to-make-privatebin-work-when-using-cloudflare-for-ddos-protection). -### Using a database instead of flat files +### Using a Database Instead of Flat Files In the configuration file the `[model]` and `[model_options]` sections let you configure your favourite way of storing the pastes and discussions on your server. `Filesystem` is the default model, which stores everything in files in the -data folder. This is the recommended setup for most sites. +data folder. This is the recommended setup for most sites on single hosts. Under high load, in distributed setups or if you are not allowed to store files locally, you might want to switch to the `Database` model. This lets you @@ -142,21 +155,26 @@ to use a prefix for The table prefix option is called `tbl`. > #### Note -> The `Database` model has only been tested with SQLite, MySQL and PostgreSQL, -> although it would not be recommended to use SQLite in a production environment. -> If you gain any experience running PrivateBin on other RDBMS, please let us -> know. +> The `Database` model has only been tested with SQLite, MariaDB/MySQL and +> PostgreSQL, although it would not be recommended to use SQLite in a production +> environment. If you gain any experience running PrivateBin on other RDBMS, +> please let us know. -The following GRANTs (privileges) are required for the PrivateBin user in **MySQL**. In normal operation: +The following GRANTs (privileges) are required for the PrivateBin user in +**MariaDB/MySQL**. In normal operation: - INSERT, SELECT, DELETE on the paste and comment tables - SELECT on the config table -If you want PrivateBin to handle table creation (when you create the first paste) and updates (after you update PrivateBin to a new release), you need to give the user these additional privileges: +If you want PrivateBin to handle table creation (when you create the first paste) +and updates (after you update PrivateBin to a new release), you need to give the +user these additional privileges: - CREATE, INDEX and ALTER on the database - INSERT and UPDATE on the config table -For reference or if you want to create the table schema for yourself to avoid having to give PrivateBin too many permissions (replace -`prefix_` with your own table prefix and create the table schema with your favourite MySQL console): +For reference or if you want to create the table schema for yourself to avoid +having to give PrivateBin too many permissions (replace `prefix_` with your own +table prefix and create the table schema with your favourite MariaDB/MySQL +client): ```sql CREATE TABLE prefix_paste ( @@ -199,7 +217,7 @@ to be `CLOB` and not `BLOB` or `MEDIUMBLOB`, the `id` column in the `config` table needs to be `VARCHAR2(16)` and the `meta` column in the `paste` table and the `value` column in the `config` table need to be `VARCHAR2(4000)`. -### Using Google Cloud Storage +#### Using Google Cloud Storage If you want to deploy PrivateBin in a serverless manner in the Google Cloud, you can choose the `GoogleCloudStorage` as backend. To use this backend, you create a GCS bucket and specify the name as the model option `bucket`. Alternatively, diff --git a/README.md b/README.md index 7d39952c..b89dbd79 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,26 @@ *Current version: 1.3.5* -**PrivateBin** is a minimalist, open source online [pastebin](https://en.wikipedia.org/wiki/Pastebin) +**PrivateBin** is a minimalist, open source online +[pastebin](https://en.wikipedia.org/wiki/Pastebin) where the server has zero knowledge of pasted data. -Data is encrypted and decrypted in the browser using 256bit AES in [Galois Counter mode](https://en.wikipedia.org/wiki/Galois/Counter_Mode). +Data is encrypted and decrypted in the browser using 256bit AES in +[Galois Counter mode](https://en.wikipedia.org/wiki/Galois/Counter_Mode). This is a fork of ZeroBin, originally developed by -[Sébastien Sauvage](https://github.com/sebsauvage/ZeroBin). ZeroBin was refactored -to allow easier and cleaner extensions. PrivateBin has many more features than the -original ZeroBin. It is, however, still fully compatible to the original ZeroBin 0.19 +[Sébastien Sauvage](https://github.com/sebsauvage/ZeroBin). PrivateBin was +refactored to allow easier and cleaner extensions and has many additional +features. It is, however, still fully compatible to the original ZeroBin 0.19 data storage scheme. Therefore, such installations can be upgraded to PrivateBin without losing any data. ## What PrivateBin provides + As a server administrator you don't have to worry if your users post content - that is considered illegal in your country. You have no knowledge of any - of the pastes content. If requested or enforced, you can delete any paste from - your system. + that is considered illegal in your country. You have plausible deniability of + any of the pastes content. If requested or enforced, you can delete any paste + from your system. + Pastebin-like system to store text documents, code samples, etc. @@ -31,13 +33,13 @@ without losing any data. ## What it doesn't provide -- As a user you have to trust the server administrator not to inject any malicious - javascript code. - For basic security, the PrivateBin installation *has to provide HTTPS*! - Otherwise you would also have to trust your internet provider, and any country - the traffic passes through. - Additionally the instance should be secured by - [HSTS](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security). It can use traditional certificate authorities and/or use +- As a user you have to trust the server administrator not to inject any + malicious code. For security, a PrivateBin installation *has to be used over* + *HTTPS*! Otherwise you would also have to trust your internet provider, and + any jurisdiction the traffic passes through. Additionally the instance should + be secured by + [HSTS](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security). It can + use traditional certificate authorities and/or use a [DNSSEC](https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions) protected [DANE](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities) @@ -45,18 +47,17 @@ without losing any data. - The "key" used to encrypt the paste is part of the URL. If you publicly post the URL of a paste that is not password-protected, anyone can read it. - Use a password if you want your paste to be private. In this case, make sure to - use a strong password and only share it privately and end-to-end-encrypted. + Use a password if you want your paste to remain private. In that case, make + sure to use a strong password and share it privately and end-to-end-encrypted. -- A server admin might be forced to hand over access logs to the authorities. +- A server admin can be forced to hand over access logs to the authorities. PrivateBin encrypts your text and the discussion contents, but who accessed a paste (first) might still be disclosed via access logs. - In case of a server breach your data is secure as it is only stored encrypted - on the server. However, the server could be misused or the server admin could - be legally forced into sending malicious JavaScript to all web users, which - grabs the decryption key and sends it to the server when a user accesses a - PrivateBin. + on the server. However, the server could be absused or the server admin could + be legally forced into sending malicious code to their users, which logs + the decryption key and sends it to a server when a user accesses a paste. Therefore, do not access any PrivateBin instance if you think it has been compromised. As long as no user accesses this instance with a previously generated URL, the content can't be decrypted. @@ -77,8 +78,8 @@ file](https://github.com/PrivateBin/PrivateBin/wiki/Configuration): * Syntax highlighting for source code using prettify.js, including 4 prettify themes -* File upload support, images get displayed (disabled by default, possibility - to adjust size limit) +* File upload support, image, media and PDF preview (disabled by default, size + limit adjustable) * Templates: By default there are bootstrap CSS, darkstrap and "classic ZeroBin" to choose from and it is easy to adapt these to your own websites layout or @@ -89,7 +90,7 @@ file](https://github.com/PrivateBin/PrivateBin/wiki/Configuration): * Language selection (disabled by default, as it uses a session cookie) -* QR code generation of URL, to easily transfer pastes over to a mobile device +* QR code for paste URLs, to easily transfer them over to mobile devices ## Further resources From 456ced37c26d01b5176c87b65e500b9e1210e080 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 5 Apr 2022 07:29:07 +0200 Subject: [PATCH 55/68] incrementing version --- INSTALL.md | 2 +- Makefile | 9 +++++---- README.md | 2 +- SECURITY.md | 4 ++-- css/bootstrap/privatebin.css | 2 +- css/noscript.css | 2 +- css/privatebin.css | 2 +- index.php | 2 +- js/package.json | 2 +- js/privatebin.js | 2 +- lib/Configuration.php | 2 +- lib/Controller.php | 4 ++-- lib/Data/AbstractData.php | 2 +- lib/Data/Database.php | 2 +- lib/Data/Filesystem.php | 2 +- lib/Filter.php | 2 +- lib/FormatV2.php | 2 +- lib/I18n.php | 2 +- lib/Json.php | 2 +- lib/Model.php | 2 +- lib/Model/AbstractModel.php | 2 +- lib/Model/Comment.php | 2 +- lib/Model/Paste.php | 2 +- lib/Persistence/AbstractPersistence.php | 2 +- lib/Persistence/PurgeLimiter.php | 2 +- lib/Persistence/ServerSalt.php | 2 +- lib/Persistence/TrafficLimiter.php | 2 +- lib/Request.php | 2 +- lib/View.php | 2 +- lib/Vizhash16x16.php | 2 +- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 32 files changed, 38 insertions(+), 37 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index ecdbb2dc..615ce56e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -205,7 +205,7 @@ CREATE INDEX parent ON prefix_comment(pasteid); CREATE TABLE prefix_config ( id CHAR(16) NOT NULL, value TEXT, PRIMARY KEY (id) ); -INSERT INTO prefix_config VALUES('VERSION', '1.3.5'); +INSERT INTO prefix_config VALUES('VERSION', '1.4.0'); ``` In **PostgreSQL**, the `data`, `attachment`, `nickname` and `vizhash` columns diff --git a/Makefile b/Makefile index 83a6b6de..33d893fd 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: all coverage coverage-js coverage-php doc doc-js doc-php increment sign test test-js test-php help -CURRENT_VERSION = 1.3.5 -VERSION ?= 1.3.6 +CURRENT_VERSION = 1.4.0 +VERSION ?= 1.4.1 VERSION_FILES = index.php cfg/ *.md css/ i18n/ img/ js/package.json js/privatebin.js lib/ Makefile tpl/ tst/ REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g") REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g") @@ -33,12 +33,13 @@ increment: ## Increment and commit new version number, set target version using do \ sed -i "s/$(REGEX_CURRENT_VERSION)/$(REGEX_VERSION)/g" $$F; \ done - git add $(VERSION_FILES) + cd tst && phpunit --no-coverage && cd .. + git add $(VERSION_FILES) tpl/ git commit -m "incrementing version" sign: ## Sign a release. git tag $(VERSION) - git push --tags + git push origin $(VERSION) signrelease.sh test: test-js test-php ## Run all unit tests. diff --git a/README.md b/README.md index b89dbd79..5ee04954 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # [![PrivateBin](https://cdn.rawgit.com/PrivateBin/assets/master/images/preview/logoSmall.png)](https://privatebin.info/) -*Current version: 1.3.5* +*Current version: 1.4.0* **PrivateBin** is a minimalist, open source online [pastebin](https://en.wikipedia.org/wiki/Pastebin) diff --git a/SECURITY.md b/SECURITY.md index e00398b9..1ed1abb5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,8 +4,8 @@ | Version | Supported | | ------- | ------------------ | -| 1.3.5 | :heavy_check_mark: | -| < 1.3.5 | :x: | +| 1.4.0 | :heavy_check_mark: | +| < 1.4.0 | :x: | ## Reporting a Vulnerability diff --git a/css/bootstrap/privatebin.css b/css/bootstrap/privatebin.css index de8b6797..b026d011 100644 --- a/css/bootstrap/privatebin.css +++ b/css/bootstrap/privatebin.css @@ -6,7 +6,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ body { diff --git a/css/noscript.css b/css/noscript.css index f42e419f..b07ba820 100644 --- a/css/noscript.css +++ b/css/noscript.css @@ -6,7 +6,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ /* When there is no script at all other */ diff --git a/css/privatebin.css b/css/privatebin.css index f852d396..38e44240 100644 --- a/css/privatebin.css +++ b/css/privatebin.css @@ -6,7 +6,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ /* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved. diff --git a/index.php b/index.php index f846adb7..382cc84a 100644 --- a/index.php +++ b/index.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ // change this, if your php files and data is outside of your webservers document root diff --git a/js/package.json b/js/package.json index 53e85dc3..42044b10 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "privatebin", - "version": "1.3.5", + "version": "1.4.0", "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", "directories": { diff --git a/js/privatebin.js b/js/privatebin.js index 21ba5438..21d94e1c 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -6,7 +6,7 @@ * @see {@link https://github.com/PrivateBin/PrivateBin} * @copyright 2012 Sébastien SAUVAGE ({@link http://sebsauvage.net}) * @license {@link https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License} - * @version 1.3.5 + * @version 1.4.0 * @name PrivateBin * @namespace */ diff --git a/lib/Configuration.php b/lib/Configuration.php index de065219..56e55adb 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; diff --git a/lib/Controller.php b/lib/Controller.php index 34087957..d16a8cac 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; @@ -28,7 +28,7 @@ class Controller * * @const string */ - const VERSION = '1.3.5'; + const VERSION = '1.4.0'; /** * minimal required PHP version diff --git a/lib/Data/AbstractData.php b/lib/Data/AbstractData.php index 591b91fd..05353ce0 100644 --- a/lib/Data/AbstractData.php +++ b/lib/Data/AbstractData.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Data; diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 03e60612..4b29fe2f 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Data; diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index 577ad34f..b2bc1eed 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Data; diff --git a/lib/Filter.php b/lib/Filter.php index 0ad87d3c..f8672a0f 100644 --- a/lib/Filter.php +++ b/lib/Filter.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; diff --git a/lib/FormatV2.php b/lib/FormatV2.php index d2055f31..4d8d43d9 100644 --- a/lib/FormatV2.php +++ b/lib/FormatV2.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; diff --git a/lib/I18n.php b/lib/I18n.php index 7b64655e..38464dea 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; diff --git a/lib/Json.php b/lib/Json.php index 5f4efcf3..13cd6157 100644 --- a/lib/Json.php +++ b/lib/Json.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; diff --git a/lib/Model.php b/lib/Model.php index 8aebd794..360cf68c 100644 --- a/lib/Model.php +++ b/lib/Model.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; diff --git a/lib/Model/AbstractModel.php b/lib/Model/AbstractModel.php index f2ab1daa..a21134ce 100644 --- a/lib/Model/AbstractModel.php +++ b/lib/Model/AbstractModel.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Model; diff --git a/lib/Model/Comment.php b/lib/Model/Comment.php index 4b3fc828..f9f45507 100644 --- a/lib/Model/Comment.php +++ b/lib/Model/Comment.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Model; diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php index b855e6d5..ea9355aa 100644 --- a/lib/Model/Paste.php +++ b/lib/Model/Paste.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Model; diff --git a/lib/Persistence/AbstractPersistence.php b/lib/Persistence/AbstractPersistence.php index 4e61a8ee..ab860a2e 100644 --- a/lib/Persistence/AbstractPersistence.php +++ b/lib/Persistence/AbstractPersistence.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Persistence; diff --git a/lib/Persistence/PurgeLimiter.php b/lib/Persistence/PurgeLimiter.php index ef377703..44e23304 100644 --- a/lib/Persistence/PurgeLimiter.php +++ b/lib/Persistence/PurgeLimiter.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Persistence; diff --git a/lib/Persistence/ServerSalt.php b/lib/Persistence/ServerSalt.php index 1095498c..7f96e337 100644 --- a/lib/Persistence/ServerSalt.php +++ b/lib/Persistence/ServerSalt.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Persistence; diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 93f91d23..921d9463 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -8,7 +8,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin\Persistence; diff --git a/lib/Request.php b/lib/Request.php index 858f1313..2eff8a96 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -7,7 +7,7 @@ * @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 - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; diff --git a/lib/View.php b/lib/View.php index 81698047..c6332a33 100644 --- a/lib/View.php +++ b/lib/View.php @@ -7,7 +7,7 @@ * @link https://github.com/PrivateBin/PrivateBin * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) * @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License - * @version 1.3.5 + * @version 1.4.0 */ namespace PrivateBin; diff --git a/lib/Vizhash16x16.php b/lib/Vizhash16x16.php index 77584eb0..351ba969 100644 --- a/lib/Vizhash16x16.php +++ b/lib/Vizhash16x16.php @@ -8,7 +8,7 @@ * @link http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) * @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License - * @version 0.0.5 beta PrivateBin 1.3.5 + * @version 0.0.5 beta PrivateBin 1.4.0 */ namespace PrivateBin; diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 581ab81b..c0d5770a 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -73,7 +73,7 @@ endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index 49d111b1..9ee9da77 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -51,7 +51,7 @@ endif; ?> - + From ba7bcbbb6208a831eb0ce9cbd776117d84fbd897 Mon Sep 17 00:00:00 2001 From: TMs Date: Sat, 9 Apr 2022 22:42:47 +0800 Subject: [PATCH 56/68] update zh translation --- i18n/zh.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/zh.json b/i18n/zh.json index e90627fb..1c6bc6b4 100644 --- a/i18n/zh.json +++ b/i18n/zh.json @@ -186,5 +186,5 @@ "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "访问此链接来查看该笔记。将此 URL 发送给任何人即可允许其访问该笔记。", "URL shortener may expose your decrypt key in URL.": "短链接服务可能会暴露您在 URL 中的解密密钥。", "Save paste": "保存内容", - "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." + "Your IP is not authorized to create pastes.": "你的 IP 无权创建粘贴。" } From cdea2917c1e80660ec890037f442bc5610bbcf13 Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Sun, 10 Apr 2022 09:43:11 +0200 Subject: [PATCH 57/68] New translations en.json (Chinese Simplified) --- i18n/zh.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/zh.json b/i18n/zh.json index 1c6bc6b4..9bcad318 100644 --- a/i18n/zh.json +++ b/i18n/zh.json @@ -186,5 +186,5 @@ "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "访问此链接来查看该笔记。将此 URL 发送给任何人即可允许其访问该笔记。", "URL shortener may expose your decrypt key in URL.": "短链接服务可能会暴露您在 URL 中的解密密钥。", "Save paste": "保存内容", - "Your IP is not authorized to create pastes.": "你的 IP 无权创建粘贴。" + "Your IP is not authorized to create pastes.": "您的 IP 无权创建粘贴。" } From 7b8e031ab5b6366f70b964069ab1913aa8cb906a Mon Sep 17 00:00:00 2001 From: Harald Leithner Date: Sun, 10 Apr 2022 10:36:39 +0200 Subject: [PATCH 58/68] Remove FLoC Header Google announced that it is discontinuing FLoC. --- lib/Controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Controller.php b/lib/Controller.php index d16a8cac..b6cd1e8c 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -342,7 +342,6 @@ class Controller header('Cross-Origin-Resource-Policy: same-origin'); header('Cross-Origin-Embedder-Policy: require-corp'); header('Cross-Origin-Opener-Policy: same-origin'); - header('Permissions-Policy: interest-cohort=()'); header('Referrer-Policy: no-referrer'); header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: deny'); From 4b3d11c9885fa098e56b2609e5d7091d771e09e6 Mon Sep 17 00:00:00 2001 From: Harald Leithner Date: Sun, 10 Apr 2022 11:28:52 +0200 Subject: [PATCH 59/68] Add browsing-topics premission policy --- lib/Controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Controller.php b/lib/Controller.php index b6cd1e8c..a6fd0a5b 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -342,6 +342,7 @@ class Controller header('Cross-Origin-Resource-Policy: same-origin'); header('Cross-Origin-Embedder-Policy: require-corp'); header('Cross-Origin-Opener-Policy: same-origin'); + header('Permissions-Policy: browsing-topics=()'); header('Referrer-Policy: no-referrer'); header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: deny'); From d2126d6dd69499e926b9760b0b6e1fcfa4a9390a Mon Sep 17 00:00:00 2001 From: El RIDO Date: Thu, 14 Apr 2022 06:08:48 +0200 Subject: [PATCH 60/68] fix php8 refresh workflow requires updating the commit action due to https://github.com/github-actions-x/commit/issues/30 --- .github/workflows/refresh-php8.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/refresh-php8.yml b/.github/workflows/refresh-php8.yml index 4c131b40..fd2f9dbc 100644 --- a/.github/workflows/refresh-php8.yml +++ b/.github/workflows/refresh-php8.yml @@ -2,9 +2,9 @@ name: Refresh PHP 8 branch on: push: - branches: [ master ] + branches: [ master ] schedule: - - cron: '42 2 * * *' + - cron: '42 2 * * *' workflow_dispatch: jobs: @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout php8 branch + - name: Checkout php8 branch uses: actions/checkout@v2 with: # directly checkout the php8 branch @@ -28,7 +28,7 @@ jobs: git merge origin/master - name: Push new changes - uses: github-actions-x/commit@v2.8 + uses: github-actions-x/commit@v2 with: name: github-actions[bot] email: 41898282+github-actions[bot]@users.noreply.github.com From 15a9b8d82608ed6e9eb3b143220f692c23e6f9d3 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Thu, 14 Apr 2022 06:17:10 +0200 Subject: [PATCH 61/68] fix php8 refresh workflow requires updating the commit action due to https://github.com/github-actions-x/commit/issues/30 --- .github/workflows/refresh-php8.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/refresh-php8.yml b/.github/workflows/refresh-php8.yml index fd2f9dbc..92371f49 100644 --- a/.github/workflows/refresh-php8.yml +++ b/.github/workflows/refresh-php8.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout php8 branch - uses: actions/checkout@v2 + uses: actions/checkout@v2.9 with: # directly checkout the php8 branch ref: php8 From a15b395eaf049ff93596301669d17679228d62a7 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Thu, 14 Apr 2022 06:19:19 +0200 Subject: [PATCH 62/68] fix php8 refresh workflow requires updating the commit action due to https://github.com/github-actions-x/commit/issues/30 --- .github/workflows/refresh-php8.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/refresh-php8.yml b/.github/workflows/refresh-php8.yml index 92371f49..b17a6ab8 100644 --- a/.github/workflows/refresh-php8.yml +++ b/.github/workflows/refresh-php8.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout php8 branch - uses: actions/checkout@v2.9 + uses: actions/checkout@v2 with: # directly checkout the php8 branch ref: php8 @@ -28,7 +28,7 @@ jobs: git merge origin/master - name: Push new changes - uses: github-actions-x/commit@v2 + uses: github-actions-x/commit@v2.9 with: name: github-actions[bot] email: 41898282+github-actions[bot]@users.noreply.github.com From 0e2ec27033aeaf6b034b2292d9d41ed19f7b9c82 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 19 Apr 2022 18:44:00 +0200 Subject: [PATCH 63/68] Avoid privilege for setting the for MariaDB/MySQL, fixes #919 --- CHANGELOG.md | 2 ++ lib/Data/Database.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9526c7df..dd9263bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # PrivateBin version history + * **1.4.1 (not yet released)** + * CHANGED: Avoid `SUPER` privilege for setting the `sql_mode` for MariaDB/MySQL (#919) * **1.4 (2022-04-09)** * ADDED: Translations for Corsican, Estonian, Finnish and Lojban * ADDED: new HTTP headers improving security (#765) diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 4b29fe2f..2aa3ecd6 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -100,7 +100,7 @@ class Database extends AbstractData // MySQL uses backticks to quote identifiers by default, // tell it to expect ANSI SQL double quotes if (self::$_type === 'mysql' && defined('PDO::MYSQL_ATTR_INIT_COMMAND')) { - $options['opt'][PDO::MYSQL_ATTR_INIT_COMMAND] = "SET sql_mode='ANSI_QUOTES'"; + $options['opt'][PDO::MYSQL_ATTR_INIT_COMMAND] = "SET SESSION sql_mode='ANSI_QUOTES'"; } $tableQuery = self::_getTableQuery(self::$_type); self::$_db = new PDO( From b02335199141a546b82654c683e1f411ad6925a4 Mon Sep 17 00:00:00 2001 From: Emir Ensar Rahmanlar <64320256+rahmanlar@users.noreply.github.com> Date: Thu, 28 Apr 2022 11:37:27 +0300 Subject: [PATCH 64/68] Update Turkish localization --- i18n/tr.json | 226 +++++++++++++++++++++++++-------------------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/i18n/tr.json b/i18n/tr.json index c2c380e1..f39ebdf9 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -1,135 +1,135 @@ { "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 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.": "%s sunucunun burada paylaştığınız veriyi görmediği, minimal, açık kaynak bir pastebindir. Veriler tarayıcıda 256 bit AES kullanılarak şifrelenir/çözülür.", "More information on the project page.": "Daha fazla bilgi için proje sayfası'na göz atabilirsiniz.", "Because ignorance is bliss": "Çünkü, cehalet mutluluktur", "en": "tr", "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 requires php %s or above to work. Sorry.", - "%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.", + "%s requires php %s or above to work. Sorry.": "%s PHP %s veya daha üstünü gerektirir.", + "%s requires configuration section [%s] to be present in configuration file.": "%s konfigürasyon bölümünün [%s] bulunmasını gerektir.", "Please wait %d seconds between each post.": [ - "Please wait %d second between each post. (singular)", - "Please wait %d seconds between each post. (1st plural)", - "Please wait %d seconds between each post. (2nd plural)", - "Please wait %d seconds between each post. (3rd plural)" + "Lütfen paylaşımlar arasında %d saniye bekleyiniz. (singular)", + "Lütfen paylaşımlar arasında %d saniye bekleyiniz. (1st plural)", + "Lütfen paylaşımlar arasında %d saniye bekleyiniz. (2nd plural)", + "Lütfen paylaşımlar arasında %d saniye bekleyiniz. (3rd plural)" ], - "Paste is limited to %s of encrypted data.": "Paste is limited to %s of encrypted data.", + "Paste is limited to %s of encrypted data.": "Yazılar %s şifreli veriyle sınırlıdır.", "Invalid data.": "Geçersiz veri.", "You are unlucky. Try again.": "Lütfen tekrar deneyiniz.", - "Error saving comment. Sorry.": "Error saving comment. Sorry.", - "Error saving paste. Sorry.": "Error saving paste. Sorry.", - "Invalid paste ID.": "Invalid paste ID.", - "Paste is not of burn-after-reading type.": "Paste is not of burn-after-reading type.", - "Wrong deletion token. Paste was not deleted.": "Wrong deletion token. Paste was not deleted.", - "Paste was properly deleted.": "Paste was properly deleted.", - "JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript is required for %s to work. Sorry for the inconvenience.", - "%s requires a modern browser to work.": "%s requires a modern browser to work.", + "Error saving comment. Sorry.": "Yorum kaydedilemedi.", + "Error saving paste. Sorry.": "Yazı kaydedilemedi.", + "Invalid paste ID.": "Geçersiz yazı ID'si.", + "Paste is not of burn-after-reading type.": "Yazı okunduğunda silinmeyecek şekilde ayarlanmış.", + "Wrong deletion token. Paste was not deleted.": "Yanlış silme anahtarı. Yazı silinemedi.", + "Paste was properly deleted.": "Yazı başarıyla silindi.", + "JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript %s 'in çalışması için gereklidir.", + "%s requires a modern browser to work.": "%s çalışmak için çağdaş bir tarayıcı gerektirir.", "New": "Yeni", "Send": "Gönder", "Clone": "Kopyala", - "Raw text": "Raw text", + "Raw text": "Açık yazı", "Expires": "Süre Sonu", - "Burn after reading": "Burn after reading", + "Burn after reading": "Okuduktan sonra sil", "Open discussion": "Açık Tartışmalar", - "Password (recommended)": "Password (recommended)", + "Password (recommended)": "Şifre (önerilir)", "Discussion": "Tartışma", "Toggle navigation": "Gezinmeyi değiştir", "%d seconds": [ - "%d second (singular)", - "%d seconds (1st plural)", - "%d seconds (2nd plural)", - "%d seconds (3rd plural)" + "%d saniye (singular)", + "%d saniye (1st plural)", + "%d saniye (2nd plural)", + "%d saniye (3rd plural)" ], "%d minutes": [ - "%d minute (singular)", - "%d minutes (1st plural)", - "%d minutes (2nd plural)", - "%d minutes (3rd plural)" + "%d dakika (singular)", + "%d dakika (1st plural)", + "%d dakika (2nd plural)", + "%d dakika (3rd plural)" ], "%d hours": [ - "%d hour (singular)", - "%d hours (1st plural)", - "%d hours (2nd plural)", - "%d hours (3rd plural)" + "%d saat (singular)", + "%d saat (1st plural)", + "%d saat (2nd plural)", + "%d saat (3rd plural)" ], "%d days": [ - "%d day (singular)", - "%d days (1st plural)", - "%d days (2nd plural)", - "%d days (3rd plural)" + "%d gün (singular)", + "%d gün (1st plural)", + "%d gün (2nd plural)", + "%d gün (3rd plural)" ], "%d weeks": [ "%d hafta (tekil)", - "%d haftalar (çoğul)", - "%d weeks (2nd plural)", - "%d weeks (3rd plural)" + "%d hafta (çoğul)", + "%d hafta (2nd plural)", + "%d hafta (3rd plural)" ], "%d months": [ - "%d month (singular)", - "%d months (1st plural)", - "%d months (2nd plural)", - "%d months (3rd plural)" + "%d ay (singular)", + "%d ay (1st plural)", + "%d ay (2nd plural)", + "%d ay (3rd plural)" ], "%d years": [ - "%d year (singular)", - "%d years (1st plural)", - "%d years (2nd plural)", - "%d years (3rd plural)" + "%d yıl (singular)", + "%d yıl (1st plural)", + "%d yıl (2nd plural)", + "%d yıl (3rd plural)" ], - "Never": "Never", + "Never": "Asla", "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 second. (singular)", - "This document will expire in %d seconds. (1st plural)", - "This document will expire in %d seconds. (2nd plural)", - "This document will expire in %d seconds. (3rd plural)" + "Bu belge %s saniyede silinecektir. (singular)", + "Bu belge %s saniyede silinecektir. (1st plural)", + "Bu belge %s saniyede silinecektir. (2nd plural)", + "Bu belge %s saniyede silinecektir. (3rd plural)" ], "This document will expire in %d minutes.": [ - "This document will expire in %d minute. (singular)", - "This document will expire in %d minutes. (1st plural)", - "This document will expire in %d minutes. (2nd plural)", - "This document will expire in %d minutes. (3rd plural)" + "Bu belge %s dakikada silinecektir. (singular)", + "Bu belge %s dakikada silinecektir. (1st plural)", + "Bu belge %s dakikada silinecektir. (2nd plural)", + "Bu belge %s dakikada silinecektir. (3rd plural)" ], "This document will expire in %d hours.": [ - "This document will expire in %d hour. (singular)", - "This document will expire in %d hours. (1st plural)", - "This document will expire in %d hours. (2nd plural)", - "This document will expire in %d hours. (3rd plural)" + "Bu belge %s saatte silinecektir. (singular)", + "Bu belge %s saatte silinecektir. (1st plural)", + "Bu belge %s saatte silinecektir. (2nd plural)", + "Bu belge %s saatte silinecektir.. (3rd plural)" ], "This document will expire in %d days.": [ - "This document will expire in %d day. (singular)", - "This document will expire in %d days. (1st plural)", - "This document will expire in %d days. (2nd plural)", - "This document will expire in %d days. (3rd plural)" + "Bu belge %s günde silinecektir. (singular)", + "Bu belge %s günde silinecektir. (1st plural)", + "Bu belge %s günde silinecektir. (2nd plural)", + "Bu belge %s günde silinecektir.(3rd plural)" ], "This document will expire in %d months.": [ - "This document will expire in %d month. (singular)", - "This document will expire in %d months. (1st plural)", - "This document will expire in %d months. (2nd plural)", - "This document will expire in %d months. (3rd plural)" + "Bu belge %s ayda silinecektir. (singular)", + "Bu belge %s ayda silinecektir (1st plural)", + "Bu belge %s ayda silinecektir (2nd plural)", + "Bu belge %s ayda silinecektir (3rd 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?)", - "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?", + "Please enter the password for this paste:": "Lütfen bu yazı için şifrenizi girin:", + "Could not decrypt data (Wrong key?)": "Şifre çözülemedi (Yanlış anahtar mı kullandınız?)", + "Could not delete the paste, it was not stored in burn after reading mode.": "Yazı silinemedi, okunduktan sonra silinmek için ayarlanmadı.", + "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "BU DOSYAYI SADECE SİZ GÖRÜNTÜLEYEBİLİRSİNİZ. Bu pencereyi kapatmayın, yazıyı tekrar görüntüleyemeyeceksiniz.", + "Could not decrypt comment; Wrong key?": "Dosya şifresi çözülemedi, doğru anahtarı girdiğinizden emin misiniz?", "Reply": "Cevapla", "Anonymous": "Anonim", - "Avatar generated from IP address": "Avatar generated from IP address", + "Avatar generated from IP address": "IP adresinden oluşturulmuş avatar", "Add comment": "Yorum ekle", - "Optional nickname…": "Optional nickname…", + "Optional nickname…": "İsteğe bağlı takma isim...", "Post comment": "Yorumu gönder", - "Sending comment…": "Sending comment…", + "Sending comment…": "Yorum gönderiliyor...", "Comment posted.": "Yorum gönderildi.", - "Could not refresh display: %s": "Could not refresh display: %s", - "unknown status": "unknown status", - "server error or not responding": "server error or not responding", - "Could not post comment: %s": "Could not post comment: %s", - "Sending paste…": "Sending paste…", - "Your paste is %s (Hit [Ctrl]+[c] to copy)": "Your paste is %s (Hit [Ctrl]+[c] to copy)", + "Could not refresh display: %s": "Görüntü yenilenemedi: %s", + "unknown status": "bilinmeyen durum", + "server error or not responding": "sunucu hatası veya yanıt vermiyor", + "Could not post comment: %s": "Yorum paylaşılamadı: %s", + "Sending paste…": "Yazı gönderiliyor…", + "Your paste is %s (Hit [Ctrl]+[c] to copy)": "Yazınız: %s ([Ctrl]+[c] tuşlarına basarak kopyalayın.)", "Delete data": "Veriyi sil", - "Could not create paste: %s": "Could not create 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?)": "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)", + "Could not create paste: %s": "Yazı oluşturulamadı: %s", + "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Yazı şifresi çözülemedi, çözme anahtarı URL'de bulunamadı. (Buraya bir yönlendirici veya URL kısaltıcı kullanarak gelmiş olabilirsiniz.)", "B": "B", "KiB": "KiB", "MiB": "MiB", @@ -140,51 +140,51 @@ "ZiB": "ZiB", "YiB": "YiB", "Format": "Format", - "Plain Text": "Plain Text", - "Source Code": "Source Code", + "Plain Text": "Düz Yazı", + "Source Code": "Kaynak Kodu", "Markdown": "Markdown", - "Download attachment": "Download attachment", - "Cloned: '%s'": "Cloned: '%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", - "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", - "Shorten URL": "Shorten URL", - "Editor": "Editor", + "Download attachment": "Eki indir", + "Cloned: '%s'": "Klonlandı: '%s'", + "The cloned file '%s' was attached to this paste.": "Klonlanmış dosya '%s' bu yazıya eklendi.", + "Attach a file": "Dosya ekle", + "alternatively drag & drop a file or paste an image from the clipboard": "alternatif olarak dosyasyı yapıştırabilir veya sürükleyip bırakabilirsin.z", + "File too large, to display a preview. Please download the attachment.": "Dosya önizleme için çok büyük. Lütfen eki indirin.", + "Remove attachment": "Eki sil", + "Your browser does not support uploading encrypted files. Please use a newer browser.": "Tarayıcınız şifreli dosyaları desteklemiyor.", + "Invalid attachment.": "Geçersiz ek.", + "Options": "Seçenekler", + "Shorten URL": "URL kısaltma", + "Editor": "Düzenleyici", "Preview": "Ön izleme", "%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", + "Decrypt": "Şifreyi çöz", "Enter password": "Şifreyi girin", "Loading…": "Yükleniyor…", - "Decrypting paste…": "Decrypting paste…", - "Preparing new paste…": "Preparing new paste…", + "Decrypting paste…": "Yazı şifresi çözülüyor...", + "Preparing new paste…": "Yeni yazı hazırlanıyor...", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "In case this message never disappears please have a look at this FAQ for information to troubleshoot.", "+++ no paste text +++": "+++ no paste text +++", - "Could not get paste data: %s": "Could not get paste data: %s", + "Could not get paste data: %s": "Yazı verisi alınamıyor: %s", "QR code": "QR kodu", "This website is using an insecure HTTP connection! Please use it only for testing.": "This website is using an insecure HTTP connection! Please use it only for testing.", "For more information see this FAQ entry.": "For more information see this FAQ entry.", "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.", "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.", "waiting on user to provide a password": "waiting on user to provide a password", - "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.", + "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Dosya şifresi çözülemedi, doğru şifreyi kullandığınıza emin misiniz? Üstteki buton ile tekrar deneyin.", "Retry": "Yeniden Dene", - "Showing raw text…": "Showing raw text…", + "Showing raw text…": "Açık yazı gösteriliyor...", "Notice:": "Bildirim:", - "This link will expire after %s.": "This link will expire after %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.", + "This link will expire after %s.": "Bu bağlantı şu kadar zaman sonra etkisiz kalacaktır: %s.", + "This link can only be accessed once, do not use back or refresh button in your browser.": "Bu bağlantı sadece bir kere erişilebilir, lütfen sayfayı yenilemeyiniz.", "Link:": "Bağlantı:", - "Recipient may become aware of your timezone, convert time to UTC?": "Recipient may become aware of your timezone, convert time to UTC?", - "Use Current Timezone": "Use Current Timezone", - "Convert To UTC": "Convert To UTC", + "Recipient may become aware of your timezone, convert time to UTC?": "Alıcı zaman dilmini öğrenebilir, zaman dilimini UTC'ye çevirmek ister misin?", + "Use Current Timezone": "Şuanki zaman dilimini kullan", + "Convert To UTC": "UTC zaman dilimine çevir", "Close": "Kapat", - "Encrypted note on PrivateBin": "Encrypted note on PrivateBin", - "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 shortener may expose your decrypt key in URL.", - "Save paste": "Save paste", - "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes." + "Encrypted note on PrivateBin": "PrivateBin üzerinde şifrelenmiş not", + "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Notu görmek için bu bağlantıyı ziyaret et. Bağlantıya sahip olan birisi notu görebilir.", + "URL shortener may expose your decrypt key in URL.": "URL kısaltıcı şifreleme anahtarınızı URL içerisinde gösterebilir.", + "Save paste": "Yazıyı kaydet", + "Your IP is not authorized to create pastes.": "IP adresinizin yazı oluşturmaya yetkisi yoktur." } From 2c01892ee1f754896e88a0e9770d273618e86fe3 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Thu, 28 Apr 2022 19:47:28 +0200 Subject: [PATCH 65/68] bump github/codeql-action from 1 to 2 --- .github/workflows/snyk-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk-scan.yml b/.github/workflows/snyk-scan.yml index 4730eec0..dce638c1 100644 --- a/.github/workflows/snyk-scan.yml +++ b/.github/workflows/snyk-scan.yml @@ -24,6 +24,6 @@ jobs: with: args: --sarif-file-output=snyk.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: sarif_file: snyk.sarif From f717334ee09054a602b0def5d841e3054284a417 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Thu, 28 Apr 2022 20:05:57 +0200 Subject: [PATCH 66/68] - credit & document Turkish translation - remove plural indicators - add plural logic and enable Turkish translation --- CHANGELOG.md | 1 + CREDITS.md | 1 + i18n/tr.json | 102 +++++++++++++++++++++++----------------------- js/privatebin.js | 3 +- lib/I18n.php | 1 + tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 7 files changed, 58 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd9263bb..7c036ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # PrivateBin version history * **1.4.1 (not yet released)** + * ADDED: Translations for Turkish * CHANGED: Avoid `SUPER` privilege for setting the `sql_mode` for MariaDB/MySQL (#919) * **1.4 (2022-04-09)** * ADDED: Translations for Corsican, Estonian, Finnish and Lojban diff --git a/CREDITS.md b/CREDITS.md index e40e45ab..1badfb9b 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -56,3 +56,4 @@ * foxsouns - Lojban * Patriccollu di Santa Maria è Sichè - Corsican * Markus Mikkonen - Finnish +* Emir Ensar Rahmanlar - Turkish diff --git a/i18n/tr.json b/i18n/tr.json index f39ebdf9..579d5e25 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -8,10 +8,10 @@ "%s requires php %s or above to work. Sorry.": "%s PHP %s veya daha üstünü gerektirir.", "%s requires configuration section [%s] to be present in configuration file.": "%s konfigürasyon bölümünün [%s] bulunmasını gerektir.", "Please wait %d seconds between each post.": [ - "Lütfen paylaşımlar arasında %d saniye bekleyiniz. (singular)", - "Lütfen paylaşımlar arasında %d saniye bekleyiniz. (1st plural)", - "Lütfen paylaşımlar arasında %d saniye bekleyiniz. (2nd plural)", - "Lütfen paylaşımlar arasında %d saniye bekleyiniz. (3rd plural)" + "Lütfen paylaşımlar arasında %d saniye bekleyiniz.", + "Lütfen paylaşımlar arasında %d saniye bekleyiniz.", + "Lütfen paylaşımlar arasında %d saniye bekleyiniz.", + "Lütfen paylaşımlar arasında %d saniye bekleyiniz." ], "Paste is limited to %s of encrypted data.": "Yazılar %s şifreli veriyle sınırlıdır.", "Invalid data.": "Geçersiz veri.", @@ -35,78 +35,78 @@ "Discussion": "Tartışma", "Toggle navigation": "Gezinmeyi değiştir", "%d seconds": [ - "%d saniye (singular)", - "%d saniye (1st plural)", - "%d saniye (2nd plural)", - "%d saniye (3rd plural)" + "%d saniye", + "%d saniye", + "%d saniye", + "%d saniye" ], "%d minutes": [ - "%d dakika (singular)", - "%d dakika (1st plural)", - "%d dakika (2nd plural)", - "%d dakika (3rd plural)" + "%d dakika", + "%d dakika", + "%d dakika", + "%d dakika" ], "%d hours": [ - "%d saat (singular)", - "%d saat (1st plural)", - "%d saat (2nd plural)", - "%d saat (3rd plural)" + "%d saat", + "%d saat", + "%d saat", + "%d saat" ], "%d days": [ - "%d gün (singular)", - "%d gün (1st plural)", - "%d gün (2nd plural)", - "%d gün (3rd plural)" + "%d gün", + "%d gün", + "%d gün", + "%d gün" ], "%d weeks": [ - "%d hafta (tekil)", - "%d hafta (çoğul)", - "%d hafta (2nd plural)", - "%d hafta (3rd plural)" + "%d hafta", + "%d hafta", + "%d hafta", + "%d hafta" ], "%d months": [ - "%d ay (singular)", - "%d ay (1st plural)", - "%d ay (2nd plural)", - "%d ay (3rd plural)" + "%d ay", + "%d ay", + "%d ay", + "%d ay" ], "%d years": [ - "%d yıl (singular)", - "%d yıl (1st plural)", - "%d yıl (2nd plural)", - "%d yıl (3rd plural)" + "%d yıl", + "%d yıl", + "%d yıl", + "%d yıl" ], "Never": "Asla", "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.": [ - "Bu belge %s saniyede silinecektir. (singular)", - "Bu belge %s saniyede silinecektir. (1st plural)", - "Bu belge %s saniyede silinecektir. (2nd plural)", - "Bu belge %s saniyede silinecektir. (3rd plural)" + "Bu belge %s saniyede silinecektir.", + "Bu belge %s saniyede silinecektir.", + "Bu belge %s saniyede silinecektir.", + "Bu belge %s saniyede silinecektir." ], "This document will expire in %d minutes.": [ - "Bu belge %s dakikada silinecektir. (singular)", - "Bu belge %s dakikada silinecektir. (1st plural)", - "Bu belge %s dakikada silinecektir. (2nd plural)", - "Bu belge %s dakikada silinecektir. (3rd plural)" + "Bu belge %s dakikada silinecektir.", + "Bu belge %s dakikada silinecektir.", + "Bu belge %s dakikada silinecektir.", + "Bu belge %s dakikada silinecektir." ], "This document will expire in %d hours.": [ - "Bu belge %s saatte silinecektir. (singular)", - "Bu belge %s saatte silinecektir. (1st plural)", - "Bu belge %s saatte silinecektir. (2nd plural)", - "Bu belge %s saatte silinecektir.. (3rd plural)" + "Bu belge %s saatte silinecektir.", + "Bu belge %s saatte silinecektir.", + "Bu belge %s saatte silinecektir.", + "Bu belge %s saatte silinecektir.." ], "This document will expire in %d days.": [ - "Bu belge %s günde silinecektir. (singular)", - "Bu belge %s günde silinecektir. (1st plural)", - "Bu belge %s günde silinecektir. (2nd plural)", + "Bu belge %s günde silinecektir.", + "Bu belge %s günde silinecektir.", + "Bu belge %s günde silinecektir.", "Bu belge %s günde silinecektir.(3rd plural)" ], "This document will expire in %d months.": [ - "Bu belge %s ayda silinecektir. (singular)", - "Bu belge %s ayda silinecektir (1st plural)", - "Bu belge %s ayda silinecektir (2nd plural)", - "Bu belge %s ayda silinecektir (3rd plural)" + "Bu belge %s ayda silinecektir.", + "Bu belge %s ayda silinecektir", + "Bu belge %s ayda silinecektir", + "Bu belge %s ayda silinecektir" ], "Please enter the password for this paste:": "Lütfen bu yazı için şifrenizi girin:", "Could not decrypt data (Wrong key?)": "Şifre çözülemedi (Yanlış anahtar mı kullandınız?)", diff --git a/js/privatebin.js b/js/privatebin.js index 21d94e1c..9eec6e09 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -627,7 +627,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { * @prop {string[]} * @readonly */ - const supportedLanguages = ['bg', 'ca', 'co', 'cs', 'de', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh']; + const supportedLanguages = ['bg', 'ca', 'co', 'cs', 'de', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'tr', 'uk', 'zh']; /** * built in language @@ -807,6 +807,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { case 'co': case 'fr': case 'oc': + case 'tr': case 'zh': return n > 1 ? 1 : 0; case 'he': diff --git a/lib/I18n.php b/lib/I18n.php index 38464dea..0d4734a3 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -320,6 +320,7 @@ class I18n case 'co': case 'fr': case 'oc': + case 'tr': case 'zh': return $n > 1 ? 1 : 0; case 'he': diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index c0d5770a..65f093ef 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -73,7 +73,7 @@ endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index 9ee9da77..1e377609 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -51,7 +51,7 @@ endif; ?> - + From b6fc4bd19dda4af909cf5b2d6698c0df10481f80 Mon Sep 17 00:00:00 2001 From: MTBBK <65126563+MTBBK@users.noreply.github.com> Date: Thu, 28 Apr 2022 23:00:31 +0300 Subject: [PATCH 67/68] Update tr.json --- i18n/tr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/tr.json b/i18n/tr.json index f39ebdf9..f4f7a13f 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -17,12 +17,12 @@ "Invalid data.": "Geçersiz veri.", "You are unlucky. Try again.": "Lütfen tekrar deneyiniz.", "Error saving comment. Sorry.": "Yorum kaydedilemedi.", - "Error saving paste. Sorry.": "Yazı kaydedilemedi.", + "Error saving paste. Sorry.": "Yazı kaydedilemedi. Üzgünüz.", "Invalid paste ID.": "Geçersiz yazı ID'si.", "Paste is not of burn-after-reading type.": "Yazı okunduğunda silinmeyecek şekilde ayarlanmış.", "Wrong deletion token. Paste was not deleted.": "Yanlış silme anahtarı. Yazı silinemedi.", "Paste was properly deleted.": "Yazı başarıyla silindi.", - "JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript %s 'in çalışması için gereklidir.", + "JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript %s 'in çalışması için gereklidir. Rahatsızlıktan dolayı özür dileriz.", "%s requires a modern browser to work.": "%s çalışmak için çağdaş bir tarayıcı gerektirir.", "New": "Yeni", "Send": "Gönder", From 3ae97cde7a32f14ce7f8a1992646847ba214486b Mon Sep 17 00:00:00 2001 From: PrivateBin Translator Bot <72346835+privatebin-translator@users.noreply.github.com> Date: Mon, 2 May 2022 00:14:26 +0200 Subject: [PATCH 68/68] New translations en.json (Catalan) --- i18n/ca.json | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/i18n/ca.json b/i18n/ca.json index 245b884f..01651989 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -79,34 +79,34 @@ "Never": "Mai", "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 second. (singular)", - "This document will expire in %d seconds. (1st plural)", - "This document will expire in %d seconds. (2nd plural)", - "This document will expire in %d seconds. (3rd plural)" + "Aquest document caducarà d'aquí %d segon.", + "Aquest document caducarà d'aquí %d segons.", + "Aquest document caducarà d'aquí %d segons.", + "Aquest document caducarà d'aquí %d segons." ], "This document will expire in %d minutes.": [ - "This document will expire in %d minute. (singular)", - "This document will expire in %d minutes. (1st plural)", - "This document will expire in %d minutes. (2nd plural)", - "This document will expire in %d minutes. (3rd plural)" + "Aquest document caducarà d'aquí %d minut.", + "Aquest document caducarà d'aquí %d minuts.", + "Aquest document caducarà d'aquí %d minuts.", + "Aquest document caducarà d'aquí %d minuts." ], "This document will expire in %d hours.": [ - "This document will expire in %d hour. (singular)", - "This document will expire in %d hours. (1st plural)", - "This document will expire in %d hours. (2nd plural)", - "This document will expire in %d hours. (3rd plural)" + "Aquest document caducarà d'aquí %d hora.", + "Aquest document caducarà d'aquí %d hores.", + "Aquest document caducarà d'aquí %d hores.", + "Aquest document caducarà d'aquí %d hores." ], "This document will expire in %d days.": [ - "This document will expire in %d day. (singular)", - "This document will expire in %d days. (1st plural)", - "This document will expire in %d days. (2nd plural)", - "This document will expire in %d days. (3rd plural)" + "Aquest document caducarà d'aquí %d dia.", + "Aquest document caducarà d'aquí %d dies.", + "Aquest document caducarà d'aquí %d dies.", + "Aquest document caducarà d'aquí %d dies." ], "This document will expire in %d months.": [ - "This document will expire in %d month. (singular)", - "This document will expire in %d months. (1st plural)", - "This document will expire in %d months. (2nd plural)", - "This document will expire in %d months. (3rd plural)" + "Aquest document caducarà d'aquí %d mes.", + "Aquest document caducarà d'aquí %d mesos.", + "Aquest document caducarà d'aquí %d mesos.", + "Aquest document caducarà d'aquí %d mesos." ], "Please enter the password for this paste:": "Si us plau, introdueix la contrasenya per aquest paste:", "Could not decrypt data (Wrong key?)": "No s'han pogut desxifrar les dades (Clau incorrecte?)", @@ -124,7 +124,7 @@ "Could not refresh display: %s": "Could not refresh display: %s", "unknown status": "estat desconegut", "server error or not responding": "server error or not responding", - "Could not post comment: %s": "Could not post comment: %s", + "Could not post comment: %s": "No s'ha pogut publicar el comentari: %s", "Sending paste…": "Enviant paste…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": "Your paste is %s (Hit [Ctrl]+[c] to copy)", "Delete data": "Esborrar les dades", @@ -146,7 +146,7 @@ "Download attachment": "Baixar els adjunts", "Cloned: '%s'": "Cloned: '%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", + "Attach a file": "Adjuntar un fitxer", "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",