mirror of
https://github.com/PrivateBin/PrivateBin.git
synced 2026-01-22 18:28:26 +00:00
Merge pull request #1721 from PrivateBin/exception-refactoring
Exception handling refactoring
This commit is contained in:
commit
6220f7e4fa
30 changed files with 421 additions and 331 deletions
|
|
@ -1,7 +1,9 @@
|
|||
# PrivateBin version history
|
||||
|
||||
## 2.0.4 (not yet released)
|
||||
* CHANGED: Deduplicate JSON error message translations.
|
||||
* CHANGED: Deduplicate JSON error message translations
|
||||
* CHANGED: Refactored translation of exception messages
|
||||
* FIXED: Some exceptions not getting translated
|
||||
|
||||
## 1.7.9 (2025-11-13)
|
||||
* CHANGED: Upgrading libraries to: base-x 5.0.1, bootstrap 5.3.8, DOMpurify 3.2.7, ip-lib 1.21.0 & kjua 0.10.0
|
||||
|
|
|
|||
24
composer.lock
generated
24
composer.lock
generated
|
|
@ -397,16 +397,16 @@
|
|||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.6.1",
|
||||
"version": "v5.6.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2"
|
||||
"reference": "3a454ca033b9e06b63282ce19562e892747449bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
|
||||
"reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
|
||||
"reference": "3a454ca033b9e06b63282ce19562e892747449bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -449,9 +449,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
|
||||
},
|
||||
"time": "2025-08-13T20:13:15+00:00"
|
||||
"time": "2025-10-21T19:32:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
|
|
@ -2014,16 +2014,16 @@
|
|||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
"version": "1.2.3",
|
||||
"version": "1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/theseer/tokenizer.git",
|
||||
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
|
||||
"reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
|
||||
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
|
||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/d74205c497bfbca49f34d4bc4c19c17e22db4ebb",
|
||||
"reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2052,7 +2052,7 @@
|
|||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||
"support": {
|
||||
"issues": "https://github.com/theseer/tokenizer/issues",
|
||||
"source": "https://github.com/theseer/tokenizer/tree/1.2.3"
|
||||
"source": "https://github.com/theseer/tokenizer/tree/1.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2060,7 +2060,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-03-03T12:36:25+00:00"
|
||||
"time": "2025-11-13T13:44:09+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
namespace PrivateBin;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Exception\TranslatedException;
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
|
|
@ -131,7 +132,7 @@ class Configuration
|
|||
/**
|
||||
* parse configuration file and ensure default configuration values are present
|
||||
*
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
|
@ -148,7 +149,8 @@ class Configuration
|
|||
$config = parse_ini_file($configFile, true);
|
||||
foreach (array('main', 'model', 'model_options') as $section) {
|
||||
if (!array_key_exists($section, $config)) {
|
||||
throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2);
|
||||
$name = $config['main']['name'] ?? self::getDefaults()['main']['name'];
|
||||
throw new TranslatedException(array('%s requires configuration section [%s] to be present in configuration file.', I18n::_($name), $section), 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -158,7 +160,7 @@ class Configuration
|
|||
$opts = '_options';
|
||||
foreach (self::getDefaults() as $section => $values) {
|
||||
// fill missing sections with default values
|
||||
if (!array_key_exists($section, $config) || count($config[$section]) == 0) {
|
||||
if (!array_key_exists($section, $config) || count($config[$section]) === 0) {
|
||||
$this->_configuration[$section] = $values;
|
||||
if (array_key_exists('dir', $this->_configuration[$section])) {
|
||||
$this->_configuration[$section]['dir'] = PATH . $this->_configuration[$section]['dir'];
|
||||
|
|
@ -167,7 +169,7 @@ class Configuration
|
|||
}
|
||||
// provide different defaults for database model
|
||||
elseif (
|
||||
$section == 'model_options' &&
|
||||
$section === 'model_options' &&
|
||||
$this->_configuration['model']['class'] === 'Database'
|
||||
) {
|
||||
$values = array(
|
||||
|
|
@ -178,7 +180,7 @@ class Configuration
|
|||
'opt' => array(),
|
||||
);
|
||||
} elseif (
|
||||
$section == 'model_options' &&
|
||||
$section === 'model_options' &&
|
||||
$this->_configuration['model']['class'] === 'GoogleCloudStorage'
|
||||
) {
|
||||
$values = array(
|
||||
|
|
@ -187,7 +189,7 @@ class Configuration
|
|||
'uniformacl' => false,
|
||||
);
|
||||
} elseif (
|
||||
$section == 'model_options' &&
|
||||
$section === 'model_options' &&
|
||||
$this->_configuration['model']['class'] === 'S3Storage'
|
||||
) {
|
||||
$values = array(
|
||||
|
|
@ -216,11 +218,11 @@ class Configuration
|
|||
// check for missing keys and set defaults if necessary
|
||||
else {
|
||||
// preserve configured SRI hashes
|
||||
if ($section == 'sri' && array_key_exists($section, $config)) {
|
||||
if ($section === 'sri' && array_key_exists($section, $config)) {
|
||||
$this->_configuration[$section] = $config[$section];
|
||||
}
|
||||
foreach ($values as $key => $val) {
|
||||
if ($key == 'dir') {
|
||||
if ($key === 'dir') {
|
||||
$val = PATH . $val;
|
||||
}
|
||||
$result = $val;
|
||||
|
|
@ -304,13 +306,13 @@ class Configuration
|
|||
* get a section from the configuration, must exist
|
||||
*
|
||||
* @param string $section
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSection($section)
|
||||
{
|
||||
if (!array_key_exists($section, $this->_configuration)) {
|
||||
throw new Exception(I18n::_('%s requires configuration section [%s] to be present in configuration file.', I18n::_($this->getKey('name')), $section), 3);
|
||||
throw new TranslatedException(array('%s requires configuration section [%s] to be present in configuration file.', I18n::_($this->getKey('name')), $section), 3);
|
||||
}
|
||||
return $this->_configuration[$section];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
namespace PrivateBin;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
use PrivateBin\Exception\TranslatedException;
|
||||
use PrivateBin\Persistence\ServerSalt;
|
||||
use PrivateBin\Persistence\TrafficLimiter;
|
||||
use PrivateBin\Proxy\AbstractProxy;
|
||||
|
|
@ -195,13 +197,14 @@ class Controller
|
|||
* Set default language
|
||||
*
|
||||
* @access private
|
||||
* @throws Exception
|
||||
*/
|
||||
private function _setDefaultLanguage()
|
||||
{
|
||||
$lang = $this->_conf->getKey('languagedefault');
|
||||
I18n::setLanguageFallback($lang);
|
||||
// force default language, if language selection is disabled and a default is set
|
||||
if (!$this->_conf->getKey('languageselection') && strlen($lang) == 2) {
|
||||
if (!$this->_conf->getKey('languageselection') && strlen($lang) === 2) {
|
||||
$_COOKIE['lang'] = $lang;
|
||||
setcookie('lang', $lang, array('SameSite' => 'Lax', 'Secure' => true));
|
||||
}
|
||||
|
|
@ -211,6 +214,7 @@ class Controller
|
|||
* Set default template
|
||||
*
|
||||
* @access private
|
||||
* @throws Exception
|
||||
*/
|
||||
private function _setDefaultTemplate()
|
||||
{
|
||||
|
|
@ -260,6 +264,7 @@ class Controller
|
|||
* pasteid (optional) = in discussions, which paste this comment belongs to.
|
||||
*
|
||||
* @access private
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
private function _create()
|
||||
|
|
@ -270,8 +275,7 @@ class Controller
|
|||
TrafficLimiter::setStore($this->_model->getStore());
|
||||
try {
|
||||
TrafficLimiter::canPass();
|
||||
} catch (Exception $e) {
|
||||
// traffic limiter exceptions come translated
|
||||
} catch (TranslatedException $e) {
|
||||
$this->_json_error($e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
|
@ -305,12 +309,10 @@ class Controller
|
|||
$comment = $paste->getComment($data['parentid']);
|
||||
$comment->setData($data);
|
||||
$comment->store();
|
||||
$this->_json_result($comment->getId());
|
||||
} catch (Exception $e) {
|
||||
// comment exceptions need translation
|
||||
$this->_json_error(I18n::_($e->getMessage()));
|
||||
return;
|
||||
$this->_json_error($e->getMessage());
|
||||
}
|
||||
$this->_json_result($comment->getId());
|
||||
} else {
|
||||
$this->_json_error(I18n::_('Invalid data.'));
|
||||
}
|
||||
|
|
@ -319,22 +321,13 @@ class Controller
|
|||
else {
|
||||
try {
|
||||
$this->_model->purge();
|
||||
} catch (Exception $e) {
|
||||
error_log('Error purging documents: ' . $e->getMessage() . PHP_EOL .
|
||||
'Use the administration scripts statistics to find ' .
|
||||
'damaged paste IDs and either delete them or restore them ' .
|
||||
'from backup.');
|
||||
}
|
||||
$paste = $this->_model->getPaste();
|
||||
try {
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($data);
|
||||
$paste->store();
|
||||
$this->_json_result($paste->getId(), array('deletetoken' => $paste->getDeleteToken()));
|
||||
} catch (Exception $e) {
|
||||
// paste exceptions need translation
|
||||
$this->_json_error(I18n::_($e->getMessage()));
|
||||
return;
|
||||
$this->_json_error($e->getMessage());
|
||||
}
|
||||
$this->_json_result($paste->getId(), array('deletetoken' => $paste->getDeleteToken()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +357,7 @@ class Controller
|
|||
} else {
|
||||
$this->_error = self::GENERIC_ERROR;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
} catch (TranslatedException $e) {
|
||||
$this->_error = $e->getMessage();
|
||||
}
|
||||
if ($this->_request->isJsonApiCall()) {
|
||||
|
|
@ -399,9 +392,8 @@ class Controller
|
|||
} else {
|
||||
$this->_json_error(I18n::_(self::GENERIC_ERROR));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// paste exceptions need translation
|
||||
$this->_json_error(I18n::_($e->getMessage()));
|
||||
} catch (TranslatedException $e) {
|
||||
$this->_json_error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -409,6 +401,7 @@ class Controller
|
|||
* Display frontend.
|
||||
*
|
||||
* @access private
|
||||
* @throws Exception
|
||||
*/
|
||||
private function _view()
|
||||
{
|
||||
|
|
@ -428,7 +421,7 @@ class Controller
|
|||
// label all the expiration options
|
||||
$expire = array();
|
||||
foreach ($this->_conf->getSection('expire_options') as $time => $seconds) {
|
||||
$expire[$time] = ($seconds == 0) ? I18n::_(ucfirst($time)) : Filter::formatHumanReadableTime($time);
|
||||
$expire[$time] = ($seconds === 0) ? I18n::_(ucfirst($time)) : Filter::formatHumanReadableTime($time);
|
||||
}
|
||||
|
||||
// translate all the formatter options
|
||||
|
|
@ -469,7 +462,7 @@ class Controller
|
|||
}
|
||||
$page->assign('BASEPATH', I18n::_($this->_conf->getKey('basepath')));
|
||||
$page->assign('STATUS', I18n::_($this->_status));
|
||||
$page->assign('ISDELETED', I18n::_(json_encode($this->_is_deleted)));
|
||||
$page->assign('ISDELETED', $this->_is_deleted);
|
||||
$page->assign('VERSION', self::VERSION);
|
||||
$page->assign('DISCUSSION', $this->_conf->getKey('discussion'));
|
||||
$page->assign('OPENDISCUSSION', $this->_conf->getKey('opendiscussion'));
|
||||
|
|
@ -545,6 +538,7 @@ class Controller
|
|||
*
|
||||
* @access private
|
||||
* @param string $error
|
||||
* @throws JsonException
|
||||
*/
|
||||
private function _json_error($error)
|
||||
{
|
||||
|
|
@ -561,6 +555,7 @@ class Controller
|
|||
* @access private
|
||||
* @param string $dataid
|
||||
* @param array $other
|
||||
* @throws JsonException
|
||||
*/
|
||||
private function _json_result($dataid, $other = array())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use Exception;
|
|||
use PDO;
|
||||
use PDOException;
|
||||
use PrivateBin\Controller;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
use PrivateBin\Json;
|
||||
|
||||
/**
|
||||
|
|
@ -179,18 +180,24 @@ class Database extends AbstractData
|
|||
'SELECT * FROM "' . $this->_sanitizeIdentifier('paste') .
|
||||
'" WHERE "dataid" = ?', array($pasteid), true
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
} catch (PDOException $e) {
|
||||
$row = false;
|
||||
}
|
||||
if ($row === false) {
|
||||
return false;
|
||||
}
|
||||
// create array
|
||||
$paste = Json::decode($row['data']);
|
||||
try {
|
||||
$paste = Json::decode($row['data']);
|
||||
} catch (JsonException $e) {
|
||||
error_log('Error while reading a paste from the database: ' . $e->getMessage());
|
||||
$paste = array();
|
||||
}
|
||||
|
||||
try {
|
||||
$paste['meta'] = Json::decode($row['meta']);
|
||||
} catch (Exception $e) {
|
||||
} catch (JsonException $e) {
|
||||
error_log('Error while reading a paste from the database: ' . $e->getMessage());
|
||||
$paste['meta'] = array();
|
||||
}
|
||||
$expire_date = (int) $row['expiredate'];
|
||||
|
|
@ -233,7 +240,7 @@ class Database extends AbstractData
|
|||
'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') .
|
||||
'" WHERE "dataid" = ?', array($pasteid), true
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
} catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
return (bool) $row;
|
||||
|
|
@ -253,7 +260,7 @@ class Database extends AbstractData
|
|||
{
|
||||
try {
|
||||
$data = Json::encode($comment);
|
||||
} catch (Exception $e) {
|
||||
} catch (JsonException $e) {
|
||||
error_log('Error while attempting to insert a comment into the database: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -274,7 +281,7 @@ class Database extends AbstractData
|
|||
$meta['created'],
|
||||
)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
} catch (PDOException $e) {
|
||||
error_log('Error while attempting to insert a comment into the database: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -298,8 +305,14 @@ class Database extends AbstractData
|
|||
$comments = array();
|
||||
if (count($rows)) {
|
||||
foreach ($rows as $row) {
|
||||
try {
|
||||
$data = Json::decode($row['data']);
|
||||
} catch (JsonException $e) {
|
||||
error_log('Error while reading a comment from the database: ' . $e->getMessage());
|
||||
$data = array();
|
||||
}
|
||||
$i = $this->getOpenSlot($comments, (int) $row['postdate']);
|
||||
$comments[$i] = Json::decode($row['data']);
|
||||
$comments[$i] = $data;
|
||||
$comments[$i]['id'] = $row['dataid'];
|
||||
$comments[$i]['parentid'] = $row['parentid'];
|
||||
$comments[$i]['meta'] = array('created' => (int) $row['postdate']);
|
||||
|
|
@ -329,7 +342,7 @@ class Database extends AbstractData
|
|||
'" WHERE "pasteid" = ? AND "parentid" = ? AND "dataid" = ?',
|
||||
array($pasteid, $parentid, $commentid), true
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
} catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -349,7 +362,8 @@ class Database extends AbstractData
|
|||
$this->_last_cache[$key] = $value;
|
||||
try {
|
||||
$value = Json::encode($this->_last_cache);
|
||||
} catch (Exception $e) {
|
||||
} catch (JsonException $e) {
|
||||
error_log('Error encoding JSON for table "config", row "traffic_limiter": ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -386,14 +400,17 @@ class Database extends AbstractData
|
|||
$fs = new Filesystem(array('dir' => 'data'));
|
||||
$value = $fs->getValue('salt');
|
||||
$this->setValue($value, 'salt');
|
||||
unlink($file);
|
||||
if (!unlink($file)) {
|
||||
error_log('Error deleting migrated salt: ' . $file);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
if ($value && $namespace === 'traffic_limiter') {
|
||||
try {
|
||||
$this->_last_cache = Json::decode($value);
|
||||
} catch (Exception $e) {
|
||||
} catch (JsonException $e) {
|
||||
error_log('Error decoding JSON from table "config", row "traffic_limiter": ' . $e->getMessage());
|
||||
$this->_last_cache = array();
|
||||
}
|
||||
if (array_key_exists($key, $this->_last_cache)) {
|
||||
|
|
@ -412,13 +429,18 @@ class Database extends AbstractData
|
|||
*/
|
||||
protected function _getExpiredPastes($batchsize)
|
||||
{
|
||||
$statement = $this->_db->prepare(
|
||||
'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') .
|
||||
'" WHERE "expiredate" < ? AND "expiredate" != ? ' .
|
||||
($this->_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?')
|
||||
);
|
||||
$statement->execute(array(time(), 0, $batchsize));
|
||||
return $statement->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
try {
|
||||
$statement = $this->_db->prepare(
|
||||
'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') .
|
||||
'" WHERE "expiredate" < ? AND "expiredate" != ? ' .
|
||||
($this->_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?')
|
||||
);
|
||||
$statement->execute(array(time(), 0, $batchsize));
|
||||
return $statement->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
} catch (PDOException $e) {
|
||||
error_log('Error while attempting to find expired pastes in the database: ' . $e->getMessage());
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -552,6 +574,7 @@ class Database extends AbstractData
|
|||
'" WHERE "id" = ?', array($key), true
|
||||
);
|
||||
} catch (PDOException $e) {
|
||||
error_log('Error while attempting to fetch configuration key "' . $key . '" in the database: ' . $e->getMessage());
|
||||
return '';
|
||||
}
|
||||
return $row ? $row['value'] : '';
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@
|
|||
|
||||
namespace PrivateBin\Data;
|
||||
|
||||
use Exception;
|
||||
use DirectoryIterator;
|
||||
use GlobIterator;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
use PrivateBin\Json;
|
||||
|
||||
/**
|
||||
|
|
@ -104,13 +105,10 @@ class Filesystem extends AbstractData
|
|||
*/
|
||||
public function read($pasteid)
|
||||
{
|
||||
if (
|
||||
!$this->exists($pasteid) ||
|
||||
!$paste = $this->_get($this->_dataid2path($pasteid) . $pasteid . '.php')
|
||||
) {
|
||||
return false;
|
||||
if ($this->exists($pasteid)) {
|
||||
return $this->_get($this->_dataid2path($pasteid) . $pasteid . '.php');
|
||||
}
|
||||
return $paste;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,21 +122,24 @@ class Filesystem extends AbstractData
|
|||
$pastedir = $this->_dataid2path($pasteid);
|
||||
if (is_dir($pastedir)) {
|
||||
// Delete the paste itself.
|
||||
if (is_file($pastedir . $pasteid . '.php')) {
|
||||
unlink($pastedir . $pasteid . '.php');
|
||||
$pastefile = $pastedir . $pasteid . '.php';
|
||||
if (is_file($pastefile)) {
|
||||
if (!unlink($pastefile)) {
|
||||
error_log('Error deleting paste: ' . $pastefile);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete discussion if it exists.
|
||||
$discdir = $this->_dataid2discussionpath($pasteid);
|
||||
if (is_dir($discdir)) {
|
||||
// Delete all files in discussion directory
|
||||
$dir = dir($discdir);
|
||||
while (false !== ($filename = $dir->read())) {
|
||||
if (is_file($discdir . $filename)) {
|
||||
unlink($discdir . $filename);
|
||||
foreach (new DirectoryIterator($discdir) as $file) {
|
||||
if ($file->isFile()) {
|
||||
if (!unlink($file->getPathname())) {
|
||||
error_log('Error deleting comment: ' . $file->getPathname());
|
||||
}
|
||||
}
|
||||
}
|
||||
$dir->close();
|
||||
rmdir($discdir);
|
||||
}
|
||||
}
|
||||
|
|
@ -162,14 +163,11 @@ class Filesystem extends AbstractData
|
|||
// convert comments, too
|
||||
$discdir = $this->_dataid2discussionpath($pasteid);
|
||||
if (is_dir($discdir)) {
|
||||
$dir = dir($discdir);
|
||||
while (false !== ($filename = $dir->read())) {
|
||||
if (substr($filename, -4) !== '.php' && strlen($filename) >= 16) {
|
||||
$commentFilename = $discdir . $filename . '.php';
|
||||
$this->_prependRename($discdir . $filename, $commentFilename);
|
||||
foreach (new DirectoryIterator($discdir) as $file) {
|
||||
if ($file->getExtension() !== 'php' && strlen($file->getFilename()) >= 16) {
|
||||
$this->_prependRename($file->getPathname(), $file->getPathname() . '.php');
|
||||
}
|
||||
}
|
||||
$dir->close();
|
||||
}
|
||||
}
|
||||
return is_readable($pastePath);
|
||||
|
|
@ -210,15 +208,14 @@ class Filesystem extends AbstractData
|
|||
$comments = array();
|
||||
$discdir = $this->_dataid2discussionpath($pasteid);
|
||||
if (is_dir($discdir)) {
|
||||
$dir = dir($discdir);
|
||||
while (false !== ($filename = $dir->read())) {
|
||||
foreach (new DirectoryIterator($discdir) as $file) {
|
||||
// Filename is in the form pasteid.commentid.parentid.php:
|
||||
// - pasteid is the paste this reply belongs to.
|
||||
// - commentid is the comment identifier itself.
|
||||
// - parentid is the comment this comment replies to (It can be pasteid)
|
||||
if (is_file($discdir . $filename)) {
|
||||
$comment = $this->_get($discdir . $filename);
|
||||
$items = explode('.', $filename);
|
||||
if ($file->isFile()) {
|
||||
$comment = $this->_get($file->getPathname());
|
||||
$items = explode('.', $file->getBasename('.php'));
|
||||
// Add some meta information not contained in file.
|
||||
$comment['id'] = $items[1];
|
||||
$comment['parentid'] = $items[2];
|
||||
|
|
@ -231,7 +228,6 @@ class Filesystem extends AbstractData
|
|||
$comments[$key] = $comment;
|
||||
}
|
||||
}
|
||||
$dir->close();
|
||||
|
||||
// Sort comments by date, oldest first.
|
||||
ksort($comments);
|
||||
|
|
@ -312,7 +308,7 @@ class Filesystem extends AbstractData
|
|||
$file = $this->_path . DIRECTORY_SEPARATOR . 'salt.php';
|
||||
if (is_readable($file)) {
|
||||
$items = explode('|', file_get_contents($file));
|
||||
if (count($items) == 3) {
|
||||
if (count($items) === 3) {
|
||||
return $items[1];
|
||||
}
|
||||
}
|
||||
|
|
@ -346,7 +342,12 @@ class Filesystem extends AbstractData
|
|||
file_get_contents($filename),
|
||||
strlen(self::PROTECTION_LINE . PHP_EOL)
|
||||
);
|
||||
return Json::decode($data);
|
||||
try {
|
||||
return Json::decode($data);
|
||||
} catch (JsonException $e) {
|
||||
error_log('Error decoding JSON from "' . $filename . '": ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -368,10 +369,7 @@ class Filesystem extends AbstractData
|
|||
foreach ($files as $pasteid) {
|
||||
if ($this->exists($pasteid)) {
|
||||
$data = $this->read($pasteid);
|
||||
if (
|
||||
array_key_exists('expire_date', $data['meta']) &&
|
||||
$data['meta']['expire_date'] < $time
|
||||
) {
|
||||
if (($data['meta']['expire_date'] ?? $time) < $time) {
|
||||
$pastes[] = $pasteid;
|
||||
if (++$count >= $batchsize) {
|
||||
break;
|
||||
|
|
@ -450,7 +448,7 @@ class Filesystem extends AbstractData
|
|||
$filename,
|
||||
self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
} catch (JsonException $e) {
|
||||
error_log('Error while trying to store data to the filesystem at path "' . $filename . '": ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -523,6 +521,8 @@ class Filesystem extends AbstractData
|
|||
file_put_contents($destFile, $handle, FILE_APPEND);
|
||||
fclose($handle);
|
||||
}
|
||||
unlink($srcFile);
|
||||
if (!unlink($srcFile)) {
|
||||
error_log('Error deleting converted document: ' . $srcFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use Exception;
|
|||
use Google\Cloud\Core\Exception\NotFoundException;
|
||||
use Google\Cloud\Storage\Bucket;
|
||||
use Google\Cloud\Storage\StorageClient;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
use PrivateBin\Json;
|
||||
|
||||
class GoogleCloudStorage extends AbstractData
|
||||
|
|
@ -89,7 +90,7 @@ class GoogleCloudStorage extends AbstractData
|
|||
*/
|
||||
private function _getKey($pasteid)
|
||||
{
|
||||
if ($this->_prefix != '') {
|
||||
if (!empty($this->_prefix)) {
|
||||
return $this->_prefix . '/' . $pasteid;
|
||||
}
|
||||
return $pasteid;
|
||||
|
|
@ -106,7 +107,7 @@ class GoogleCloudStorage extends AbstractData
|
|||
*/
|
||||
private function _upload($key, &$payload)
|
||||
{
|
||||
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
|
||||
$metadata = $payload['meta'] ?? array();
|
||||
unset($metadata['salt']);
|
||||
foreach ($metadata as $k => $v) {
|
||||
$metadata[$k] = strval($v);
|
||||
|
|
@ -219,7 +220,12 @@ class GoogleCloudStorage extends AbstractData
|
|||
try {
|
||||
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) {
|
||||
$data = $this->_bucket->object($key->name())->downloadAsString();
|
||||
$comment = Json::decode($data);
|
||||
try {
|
||||
$comment = Json::decode($data);
|
||||
} catch (JsonException $e) {
|
||||
error_log('failed to read comment from ' . $key->name() . ', ' . $e->getMessage());
|
||||
$comment = array();
|
||||
}
|
||||
$comment['id'] = basename($key->name());
|
||||
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
|
||||
$comments[$slot] = $comment;
|
||||
|
|
@ -252,15 +258,12 @@ class GoogleCloudStorage extends AbstractData
|
|||
if (strlen($name) > strlen($path) && substr($name, strlen($path), 1) !== '/') {
|
||||
continue;
|
||||
}
|
||||
$info = $object->info();
|
||||
if (key_exists('metadata', $info) && key_exists('value', $info['metadata'])) {
|
||||
$value = $info['metadata']['value'];
|
||||
if (is_numeric($value) && intval($value) < $time) {
|
||||
try {
|
||||
$object->delete();
|
||||
} catch (NotFoundException $e) {
|
||||
// deleted by another instance.
|
||||
}
|
||||
$value = $object->info()['metadata']['value'] ?? '';
|
||||
if (is_numeric($value) && intval($value) < $time) {
|
||||
try {
|
||||
$object->delete();
|
||||
} catch (NotFoundException $e) {
|
||||
// deleted by another instance.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -276,14 +279,14 @@ class GoogleCloudStorage extends AbstractData
|
|||
*/
|
||||
public function setValue($value, $namespace, $key = '')
|
||||
{
|
||||
if ($key === '') {
|
||||
if (empty($key)) {
|
||||
$key = 'config/' . $namespace;
|
||||
} else {
|
||||
$key = 'config/' . $namespace . '/' . $key;
|
||||
}
|
||||
|
||||
$metadata = array('namespace' => $namespace);
|
||||
if ($namespace != 'salt') {
|
||||
if ($namespace !== 'salt') {
|
||||
$metadata['value'] = strval($value);
|
||||
}
|
||||
try {
|
||||
|
|
@ -334,17 +337,14 @@ class GoogleCloudStorage extends AbstractData
|
|||
|
||||
$now = time();
|
||||
$prefix = $this->_prefix;
|
||||
if ($prefix != '') {
|
||||
if (!empty($prefix)) {
|
||||
$prefix .= '/';
|
||||
}
|
||||
try {
|
||||
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) {
|
||||
$metadata = $object->info()['metadata'];
|
||||
if ($metadata != null && array_key_exists('expire_date', $metadata)) {
|
||||
$expire_at = intval($metadata['expire_date']);
|
||||
if ($expire_at != 0 && $expire_at < $now) {
|
||||
array_push($expired, basename($object->name()));
|
||||
}
|
||||
$expire_at = $object->info()['metadata']['expire_date'] ?? '';
|
||||
if (is_numeric($expire_at) && intval($expire_at) < $now) {
|
||||
array_push($expired, basename($object->name()));
|
||||
}
|
||||
|
||||
if (count($expired) > $batchsize) {
|
||||
|
|
@ -364,7 +364,7 @@ class GoogleCloudStorage extends AbstractData
|
|||
{
|
||||
$pastes = array();
|
||||
$prefix = $this->_prefix;
|
||||
if ($prefix != '') {
|
||||
if (!empty($prefix)) {
|
||||
$prefix .= '/';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ namespace PrivateBin\Data;
|
|||
|
||||
use Aws\S3\Exception\S3Exception;
|
||||
use Aws\S3\S3Client;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
use PrivateBin\Json;
|
||||
|
||||
class S3Storage extends AbstractData
|
||||
|
|
@ -147,7 +148,7 @@ class S3Storage extends AbstractData
|
|||
*/
|
||||
private function _getKey($pasteid)
|
||||
{
|
||||
if ($this->_prefix != '') {
|
||||
if (!empty($this->_prefix)) {
|
||||
return $this->_prefix . '/' . $pasteid;
|
||||
}
|
||||
return $pasteid;
|
||||
|
|
@ -164,7 +165,7 @@ class S3Storage extends AbstractData
|
|||
*/
|
||||
private function _upload($key, &$payload)
|
||||
{
|
||||
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
|
||||
$metadata = $payload['meta'] ?? array();
|
||||
unset($metadata['salt']);
|
||||
foreach ($metadata as $k => $v) {
|
||||
$metadata[$k] = strval($v);
|
||||
|
|
@ -177,12 +178,14 @@ class S3Storage extends AbstractData
|
|||
'ContentType' => 'application/json',
|
||||
'Metadata' => $metadata,
|
||||
));
|
||||
return true;
|
||||
} catch (S3Exception $e) {
|
||||
error_log('failed to upload ' . $key . ' to ' . $this->_bucket . ', ' .
|
||||
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||
return false;
|
||||
} catch (JsonException $e) {
|
||||
error_log('failed to JSON encode ' . $key . ', ' . $e->getMessage());
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -212,8 +215,10 @@ class S3Storage extends AbstractData
|
|||
} catch (S3Exception $e) {
|
||||
error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket . ', ' .
|
||||
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||
return false;
|
||||
} catch (JsonException $e) {
|
||||
error_log('failed to JSON decode ' . $pasteid . ', ' . $e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -312,7 +317,7 @@ class S3Storage extends AbstractData
|
|||
public function purgeValues($namespace, $time)
|
||||
{
|
||||
$path = $this->_prefix;
|
||||
if ($path != '') {
|
||||
if (!empty($path)) {
|
||||
$path .= '/';
|
||||
}
|
||||
$path .= 'config/' . $namespace;
|
||||
|
|
@ -327,17 +332,15 @@ class S3Storage extends AbstractData
|
|||
'Bucket' => $this->_bucket,
|
||||
'Key' => $name,
|
||||
));
|
||||
if ($head->get('Metadata') != null && array_key_exists('value', $head->get('Metadata'))) {
|
||||
$value = $head->get('Metadata')['value'];
|
||||
if (is_numeric($value) && intval($value) < $time) {
|
||||
try {
|
||||
$this->_client->deleteObject(array(
|
||||
'Bucket' => $this->_bucket,
|
||||
'Key' => $name,
|
||||
));
|
||||
} catch (S3Exception $e) {
|
||||
// deleted by another instance.
|
||||
}
|
||||
$value = $head->get('Metadata')['value'] ?? '';
|
||||
if (is_numeric($value) && intval($value) < $time) {
|
||||
try {
|
||||
$this->_client->deleteObject(array(
|
||||
'Bucket' => $this->_bucket,
|
||||
'Key' => $name,
|
||||
));
|
||||
} catch (S3Exception $e) {
|
||||
// deleted by another instance.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -354,7 +357,7 @@ class S3Storage extends AbstractData
|
|||
public function setValue($value, $namespace, $key = '')
|
||||
{
|
||||
$prefix = $this->_prefix;
|
||||
if ($prefix != '') {
|
||||
if (!empty($prefix)) {
|
||||
$prefix .= '/';
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +368,7 @@ class S3Storage extends AbstractData
|
|||
}
|
||||
|
||||
$metadata = array('namespace' => $namespace);
|
||||
if ($namespace != 'salt') {
|
||||
if ($namespace !== 'salt') {
|
||||
$metadata['value'] = strval($value);
|
||||
}
|
||||
try {
|
||||
|
|
@ -390,7 +393,7 @@ class S3Storage extends AbstractData
|
|||
public function getValue($namespace, $key = '')
|
||||
{
|
||||
$prefix = $this->_prefix;
|
||||
if ($prefix != '') {
|
||||
if (!empty($prefix)) {
|
||||
$prefix .= '/';
|
||||
}
|
||||
|
||||
|
|
@ -419,7 +422,7 @@ class S3Storage extends AbstractData
|
|||
$expired = array();
|
||||
$now = time();
|
||||
$prefix = $this->_prefix;
|
||||
if ($prefix != '') {
|
||||
if (!empty($prefix)) {
|
||||
$prefix .= '/';
|
||||
}
|
||||
|
||||
|
|
@ -429,11 +432,9 @@ class S3Storage extends AbstractData
|
|||
'Bucket' => $this->_bucket,
|
||||
'Key' => $object['Key'],
|
||||
));
|
||||
if ($head->get('Metadata') != null && array_key_exists('expire_date', $head->get('Metadata'))) {
|
||||
$expire_at = intval($head->get('Metadata')['expire_date']);
|
||||
if ($expire_at != 0 && $expire_at < $now) {
|
||||
array_push($expired, $object['Key']);
|
||||
}
|
||||
$expire_at = $head->get('Metadata')['expire_date'] ?? '';
|
||||
if (is_numeric($expire_at) && intval($expire_at) < $now) {
|
||||
array_push($expired, $object['Key']);
|
||||
}
|
||||
|
||||
if (count($expired) > $batchsize) {
|
||||
|
|
@ -453,7 +454,7 @@ class S3Storage extends AbstractData
|
|||
{
|
||||
$pastes = array();
|
||||
$prefix = $this->_prefix;
|
||||
if ($prefix != '') {
|
||||
if (!empty($prefix)) {
|
||||
$prefix .= '/';
|
||||
}
|
||||
|
||||
|
|
|
|||
38
lib/Exception/JsonException.php
Normal file
38
lib/Exception/JsonException.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* PrivateBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* JsonException
|
||||
*
|
||||
* An Exception representing JSON en- or decoding errors.
|
||||
*/
|
||||
class JsonException extends Exception
|
||||
{
|
||||
/**
|
||||
* Exception constructor with mandatory JSON error code.
|
||||
*
|
||||
* @access public
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct(int $code)
|
||||
{
|
||||
$message = 'A JSON error occurred';
|
||||
if (function_exists('json_last_error_msg')) {
|
||||
$message .= ': ' . json_last_error_msg();
|
||||
}
|
||||
$message .= ' (' . $code . ')';
|
||||
parent::__construct($message, 90);
|
||||
}
|
||||
}
|
||||
36
lib/Exception/TranslatedException.php
Normal file
36
lib/Exception/TranslatedException.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* PrivateBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Exception;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\I18n;
|
||||
|
||||
/**
|
||||
* TranslatedException
|
||||
*
|
||||
* An Exception that translates it's message.
|
||||
*/
|
||||
class TranslatedException extends Exception
|
||||
{
|
||||
/**
|
||||
* Translating exception constructor with mandatory messageId.
|
||||
*
|
||||
* @access public
|
||||
* @param string|array $messageId message ID or array of message ID and parameters
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct($messageId, int $code = 0)
|
||||
{
|
||||
$message = is_string($messageId) ? I18n::translate($messageId) : forward_static_call_array('PrivateBin\I18n::translate', $messageId);
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ class FormatV2
|
|||
}
|
||||
|
||||
// Make sure no additionnal keys were added.
|
||||
if (count(array_keys($message)) != count($required_keys)) {
|
||||
if (count(array_keys($message)) !== count($required_keys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
21
lib/I18n.php
21
lib/I18n.php
|
|
@ -162,6 +162,7 @@ class I18n
|
|||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @throws JsonException
|
||||
*/
|
||||
public static function loadTranslations()
|
||||
{
|
||||
|
|
@ -172,21 +173,20 @@ class I18n
|
|||
array_key_exists('lang', $_COOKIE) &&
|
||||
($key = array_search($_COOKIE['lang'], $availableLanguages)) !== false
|
||||
) {
|
||||
$match = $availableLanguages[$key];
|
||||
self::$_language = $availableLanguages[$key];
|
||||
}
|
||||
// find a translation file matching the browsers language preferences
|
||||
else {
|
||||
$match = self::_getMatchingLanguage(
|
||||
self::$_language = self::_getMatchingLanguage(
|
||||
self::getBrowserLanguages(), $availableLanguages
|
||||
);
|
||||
}
|
||||
|
||||
// load translations
|
||||
self::$_language = $match;
|
||||
if ($match == 'en') {
|
||||
if (self::$_language === 'en') {
|
||||
self::$_translations = array();
|
||||
} else {
|
||||
$data = file_get_contents(self::_getPath($match . '.json'));
|
||||
$data = file_get_contents(self::_getPath(self::$_language . '.json'));
|
||||
self::$_translations = Json::decode($data);
|
||||
}
|
||||
}
|
||||
|
|
@ -200,14 +200,14 @@ class I18n
|
|||
*/
|
||||
public static function getAvailableLanguages()
|
||||
{
|
||||
if (count(self::$_availableLanguages) == 0) {
|
||||
if (count(self::$_availableLanguages) === 0) {
|
||||
self::$_availableLanguages[] = 'en'; // en.json is not part of the release archive
|
||||
$languageIterator = new AppendIterator();
|
||||
$languageIterator->append(new GlobIterator(self::_getPath('??.json')));
|
||||
$languageIterator->append(new GlobIterator(self::_getPath('???.json'))); // for jbo
|
||||
foreach ($languageIterator as $file) {
|
||||
$language = $file->getBasename('.json');
|
||||
if ($language != 'en') {
|
||||
if ($language !== 'en') {
|
||||
self::$_availableLanguages[] = $language;
|
||||
}
|
||||
}
|
||||
|
|
@ -270,16 +270,17 @@ class I18n
|
|||
* @access public
|
||||
* @static
|
||||
* @param array $languages
|
||||
* @throws JsonException
|
||||
* @return array
|
||||
*/
|
||||
public static function getLanguageLabels($languages = array())
|
||||
{
|
||||
$file = self::_getPath('languages.json');
|
||||
if (count(self::$_languageLabels) == 0 && is_readable($file)) {
|
||||
if (count(self::$_languageLabels) === 0 && is_readable($file)) {
|
||||
$data = file_get_contents($file);
|
||||
self::$_languageLabels = Json::decode($data);
|
||||
}
|
||||
if (count($languages) == 0) {
|
||||
if (count($languages) === 0) {
|
||||
return self::$_languageLabels;
|
||||
}
|
||||
return array_intersect_key(self::$_languageLabels, array_flip($languages));
|
||||
|
|
@ -366,7 +367,7 @@ class I18n
|
|||
return $n === 1 ? 0 : (($n === 0 || ($n % 100 > 0 && $n % 100 < 20)) ? 1 : 2);
|
||||
case 'ru':
|
||||
case 'uk':
|
||||
return $n % 10 === 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
|
||||
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));
|
||||
default:
|
||||
|
|
|
|||
19
lib/Json.php
19
lib/Json.php
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace PrivateBin;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
|
||||
/**
|
||||
* Json
|
||||
|
|
@ -26,7 +26,7 @@ class Json
|
|||
* @access public
|
||||
* @static
|
||||
* @param mixed $input
|
||||
* @throws Exception
|
||||
* @throws JsonException
|
||||
* @return string
|
||||
*/
|
||||
public static function encode(&$input)
|
||||
|
|
@ -42,7 +42,7 @@ class Json
|
|||
* @access public
|
||||
* @static
|
||||
* @param string $input
|
||||
* @throws Exception
|
||||
* @throws JsonException
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decode(&$input)
|
||||
|
|
@ -57,21 +57,14 @@ class Json
|
|||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @throws Exception
|
||||
* @throws JsonException
|
||||
* @return void
|
||||
*/
|
||||
private static function _detectError()
|
||||
{
|
||||
$errorCode = json_last_error();
|
||||
if ($errorCode === JSON_ERROR_NONE) {
|
||||
return;
|
||||
if ($errorCode !== JSON_ERROR_NONE) {
|
||||
throw new JsonException($errorCode);
|
||||
}
|
||||
|
||||
$message = 'A JSON error occurred';
|
||||
if (function_exists('json_last_error_msg')) {
|
||||
$message .= ': ' . json_last_error_msg();
|
||||
}
|
||||
$message .= ' (' . $errorCode . ')';
|
||||
throw new Exception($message, 90);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
namespace PrivateBin\Model;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Configuration;
|
||||
use PrivateBin\Data\AbstractData;
|
||||
use PrivateBin\Exception\TranslatedException;
|
||||
|
||||
/**
|
||||
* AbstractModel
|
||||
|
|
@ -22,6 +22,20 @@ use PrivateBin\Data\AbstractData;
|
|||
*/
|
||||
abstract class AbstractModel
|
||||
{
|
||||
/**
|
||||
* show the same error message if the data is invalid
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const INVALID_DATA_ERROR = 'Invalid data.';
|
||||
|
||||
/**
|
||||
* show the same error message if the document ID already exists
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const COLLISION_ERROR = 'You are unlucky. Try again.';
|
||||
|
||||
/**
|
||||
* Instance ID.
|
||||
*
|
||||
|
|
@ -83,12 +97,12 @@ abstract class AbstractModel
|
|||
*
|
||||
* @access public
|
||||
* @param string $id
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
if (!self::isValidId($id)) {
|
||||
throw new Exception('Invalid document ID.', 60);
|
||||
throw new TranslatedException('Invalid document ID.', 60);
|
||||
}
|
||||
$this->_id = $id;
|
||||
}
|
||||
|
|
@ -98,7 +112,7 @@ abstract class AbstractModel
|
|||
*
|
||||
* @access public
|
||||
* @param array $data
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
public function setData(array &$data)
|
||||
{
|
||||
|
|
@ -125,7 +139,7 @@ abstract class AbstractModel
|
|||
* Store the instance's data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
abstract public function store();
|
||||
|
||||
|
|
@ -163,7 +177,7 @@ abstract class AbstractModel
|
|||
*
|
||||
* @access protected
|
||||
* @param array $data
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
protected function _validate(array &$data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
namespace PrivateBin\Model;
|
||||
|
||||
use Exception;
|
||||
use Identicon\Identicon;
|
||||
use Jdenticon\Identicon as Jdenticon;
|
||||
use PrivateBin\Exception\TranslatedException;
|
||||
use PrivateBin\Persistence\TrafficLimiter;
|
||||
use PrivateBin\Vizhash16x16;
|
||||
|
||||
|
|
@ -36,24 +36,24 @@ class Comment extends AbstractModel
|
|||
* Store the comment's data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
// Make sure paste exists.
|
||||
$pasteid = $this->getPaste()->getId();
|
||||
if (!$this->getPaste()->exists()) {
|
||||
throw new Exception('Invalid data.', 67);
|
||||
throw new TranslatedException(self::INVALID_DATA_ERROR, 67);
|
||||
}
|
||||
|
||||
// Make sure the discussion is opened in this paste and allowed in the configuration.
|
||||
if (!$this->getPaste()->isOpendiscussion() || !$this->_conf->getKey('discussion')) {
|
||||
throw new Exception('Invalid data.', 68);
|
||||
throw new TranslatedException(self::INVALID_DATA_ERROR, 68);
|
||||
}
|
||||
|
||||
// Check for improbable collision.
|
||||
if ($this->exists()) {
|
||||
throw new Exception('You are unlucky. Try again.', 69);
|
||||
throw new TranslatedException(self::COLLISION_ERROR, 69);
|
||||
}
|
||||
|
||||
$this->_data['meta']['created'] = time();
|
||||
|
|
@ -67,7 +67,7 @@ class Comment extends AbstractModel
|
|||
$this->_data
|
||||
) === false
|
||||
) {
|
||||
throw new Exception('Error saving comment. Sorry.', 70);
|
||||
throw new TranslatedException('Error saving comment. Sorry.', 70);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +91,6 @@ class Comment extends AbstractModel
|
|||
*
|
||||
* @access public
|
||||
* @param Paste $paste
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setPaste(Paste &$paste)
|
||||
{
|
||||
|
|
@ -115,12 +114,12 @@ class Comment extends AbstractModel
|
|||
*
|
||||
* @access public
|
||||
* @param string $id
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
public function setParentId($id)
|
||||
{
|
||||
if (!self::isValidId($id)) {
|
||||
throw new Exception('Invalid document ID.', 65);
|
||||
throw new TranslatedException('Invalid document ID.', 65);
|
||||
}
|
||||
$this->_data['parentid'] = $id;
|
||||
}
|
||||
|
|
@ -149,13 +148,13 @@ class Comment extends AbstractModel
|
|||
{
|
||||
// we generate an icon based on a SHA512 HMAC of the users IP, if configured
|
||||
$icon = $this->_conf->getKey('icon');
|
||||
if ($icon != 'none') {
|
||||
if ($icon !== 'none') {
|
||||
$pngdata = '';
|
||||
$hmac = TrafficLimiter::getHash();
|
||||
if ($icon == 'identicon') {
|
||||
if ($icon === 'identicon') {
|
||||
$identicon = new Identicon();
|
||||
$pngdata = $identicon->getImageDataUri($hmac, 16);
|
||||
} elseif ($icon == 'jdenticon') {
|
||||
} elseif ($icon === 'jdenticon') {
|
||||
$jdenticon = new Jdenticon(array(
|
||||
'hash' => $hmac,
|
||||
'size' => 16,
|
||||
|
|
@ -165,13 +164,13 @@ class Comment extends AbstractModel
|
|||
),
|
||||
));
|
||||
$pngdata = $jdenticon->getImageDataUri('png');
|
||||
} elseif ($icon == 'vizhash') {
|
||||
} elseif ($icon === 'vizhash') {
|
||||
$vh = new Vizhash16x16();
|
||||
$pngdata = 'data:image/png;base64,' . base64_encode(
|
||||
$vh->generate($hmac)
|
||||
);
|
||||
}
|
||||
if ($pngdata != '') {
|
||||
if (!empty($pngdata)) {
|
||||
if (!array_key_exists('meta', $data)) {
|
||||
$data['meta'] = array();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
namespace PrivateBin\Model;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Controller;
|
||||
use PrivateBin\Exception\TranslatedException;
|
||||
use PrivateBin\Persistence\ServerSalt;
|
||||
|
||||
/**
|
||||
|
|
@ -47,14 +47,14 @@ class Paste extends AbstractModel
|
|||
* Get paste data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
* @return array
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$data = $this->_store->read($this->getId());
|
||||
if ($data === false) {
|
||||
throw new Exception(Controller::GENERIC_ERROR, 64);
|
||||
throw new TranslatedException(Controller::GENERIC_ERROR, 64);
|
||||
}
|
||||
|
||||
// check if paste has expired and delete it if necessary.
|
||||
|
|
@ -62,7 +62,7 @@ class Paste extends AbstractModel
|
|||
$now = time();
|
||||
if ($data['meta']['expire_date'] < $now) {
|
||||
$this->delete();
|
||||
throw new Exception(Controller::GENERIC_ERROR, 63);
|
||||
throw new TranslatedException(Controller::GENERIC_ERROR, 63);
|
||||
}
|
||||
// We kindly provide the remaining time before expiration (in seconds)
|
||||
$data['meta']['time_to_live'] = $data['meta']['expire_date'] - $now;
|
||||
|
|
@ -73,10 +73,7 @@ class Paste extends AbstractModel
|
|||
}
|
||||
|
||||
// check if non-expired burn after reading paste needs to be deleted
|
||||
if (
|
||||
array_key_exists('adata', $data) &&
|
||||
$data['adata'][self::ADATA_BURN_AFTER_READING] === 1
|
||||
) {
|
||||
if (($data['adata'][self::ADATA_BURN_AFTER_READING] ?? 0) === 1) {
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
|
|
@ -93,13 +90,13 @@ class Paste extends AbstractModel
|
|||
* Store the paste's data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
// Check for improbable collision.
|
||||
if ($this->exists()) {
|
||||
throw new Exception('You are unlucky. Try again.', 75);
|
||||
throw new TranslatedException(self::COLLISION_ERROR, 75);
|
||||
}
|
||||
|
||||
$this->_data['meta']['salt'] = ServerSalt::generate();
|
||||
|
|
@ -111,7 +108,7 @@ class Paste extends AbstractModel
|
|||
$this->_data
|
||||
) === false
|
||||
) {
|
||||
throw new Exception('Error saving document. Sorry.', 76);
|
||||
throw new TranslatedException('Error saving document. Sorry.', 76);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +116,6 @@ class Paste extends AbstractModel
|
|||
* Delete the paste.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
|
|
@ -143,18 +139,18 @@ class Paste extends AbstractModel
|
|||
* @access public
|
||||
* @param string $parentId
|
||||
* @param string $commentId
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
* @return Comment
|
||||
*/
|
||||
public function getComment($parentId, $commentId = '')
|
||||
{
|
||||
if (!$this->exists()) {
|
||||
throw new Exception('Invalid data.', 62);
|
||||
throw new TranslatedException(self::INVALID_DATA_ERROR, 62);
|
||||
}
|
||||
$comment = new Comment($this->_conf, $this->_store);
|
||||
$comment->setPaste($this);
|
||||
$comment->setParentId($parentId);
|
||||
if ($commentId !== '') {
|
||||
if (!empty($commentId)) {
|
||||
$comment->setId($commentId);
|
||||
}
|
||||
return $comment;
|
||||
|
|
@ -201,7 +197,6 @@ class Paste extends AbstractModel
|
|||
* Check if paste has discussions enabled.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return bool
|
||||
*/
|
||||
public function isOpendiscussion()
|
||||
|
|
@ -209,8 +204,7 @@ class Paste extends AbstractModel
|
|||
if (!array_key_exists('adata', $this->_data) && !array_key_exists('data', $this->_data)) {
|
||||
$this->get();
|
||||
}
|
||||
return array_key_exists('adata', $this->_data) &&
|
||||
$this->_data['adata'][self::ADATA_OPEN_DISCUSSION] === 1;
|
||||
return ($this->_data['adata'][self::ADATA_OPEN_DISCUSSION] ?? 0) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -224,12 +218,9 @@ class Paste extends AbstractModel
|
|||
$expiration = $data['meta']['expire'] ?? 0;
|
||||
unset($data['meta']['expire']);
|
||||
$expire_options = $this->_conf->getSection('expire_options');
|
||||
if (array_key_exists($expiration, $expire_options)) {
|
||||
$expire = $expire_options[$expiration];
|
||||
} else {
|
||||
// using getKey() to ensure a default value is present
|
||||
$expire = $this->_conf->getKey($this->_conf->getKey('default', 'expire'), 'expire_options');
|
||||
}
|
||||
// using getKey() to ensure a default value is present
|
||||
$expire = $expire_options[$expiration] ??
|
||||
$this->_conf->getKey($this->_conf->getKey('default', 'expire'), 'expire_options');
|
||||
if ($expire > 0) {
|
||||
$data['meta']['expire_date'] = time() + $expire;
|
||||
}
|
||||
|
|
@ -240,13 +231,13 @@ class Paste extends AbstractModel
|
|||
*
|
||||
* @access protected
|
||||
* @param array $data
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
*/
|
||||
protected function _validate(array &$data)
|
||||
{
|
||||
// reject invalid or disabled formatters
|
||||
if (!array_key_exists($data['adata'][self::ADATA_FORMATTER], $this->_conf->getSection('formatter_options'))) {
|
||||
throw new Exception('Invalid data.', 75);
|
||||
throw new TranslatedException(self::INVALID_DATA_ERROR, 75);
|
||||
}
|
||||
|
||||
// discussion requested, but disabled in config or burn after reading requested as well, or invalid integer
|
||||
|
|
@ -257,7 +248,7 @@ class Paste extends AbstractModel
|
|||
)) ||
|
||||
($data['adata'][self::ADATA_OPEN_DISCUSSION] !== 0 && $data['adata'][self::ADATA_OPEN_DISCUSSION] !== 1)
|
||||
) {
|
||||
throw new Exception('Invalid data.', 74);
|
||||
throw new TranslatedException(self::INVALID_DATA_ERROR, 74);
|
||||
}
|
||||
|
||||
// reject invalid burn after reading
|
||||
|
|
@ -265,7 +256,7 @@ class Paste extends AbstractModel
|
|||
$data['adata'][self::ADATA_BURN_AFTER_READING] !== 0 &&
|
||||
$data['adata'][self::ADATA_BURN_AFTER_READING] !== 1
|
||||
) {
|
||||
throw new Exception('Invalid data.', 73);
|
||||
throw new TranslatedException(self::INVALID_DATA_ERROR, 73);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,10 @@
|
|||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
use Exception;
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
use PrivateBin\Configuration;
|
||||
use PrivateBin\I18n;
|
||||
use PrivateBin\Exception\TranslatedException;
|
||||
|
||||
/**
|
||||
* TrafficLimiter
|
||||
|
|
@ -74,7 +73,7 @@ class TrafficLimiter extends AbstractPersistence
|
|||
self::setExempted($conf->getKey('exempted', 'traffic'));
|
||||
self::setLimit($conf->getKey('limit', 'traffic'));
|
||||
|
||||
if (($option = $conf->getKey('header', 'traffic')) !== '') {
|
||||
if (!empty($option = $conf->getKey('header', 'traffic'))) {
|
||||
$httpHeader = 'HTTP_' . $option;
|
||||
if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
|
||||
self::$_ipKey = $httpHeader;
|
||||
|
|
@ -167,7 +166,7 @@ class TrafficLimiter extends AbstractPersistence
|
|||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @throws Exception
|
||||
* @throws TranslatedException
|
||||
* @return true
|
||||
*/
|
||||
public static function canPass()
|
||||
|
|
@ -181,7 +180,7 @@ class TrafficLimiter extends AbstractPersistence
|
|||
return true;
|
||||
}
|
||||
}
|
||||
throw new Exception(I18n::_('Your IP is not authorized to create documents.'));
|
||||
throw new TranslatedException('Your IP is not authorized to create documents.');
|
||||
}
|
||||
|
||||
// disable limits if set to less then 1
|
||||
|
|
@ -210,9 +209,9 @@ class TrafficLimiter extends AbstractPersistence
|
|||
}
|
||||
return true;
|
||||
}
|
||||
throw new Exception(I18n::_(
|
||||
throw new TranslatedException(array(
|
||||
'Please wait %d seconds between each post.',
|
||||
self::$_limit
|
||||
self::$_limit,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
namespace PrivateBin\Proxy;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Configuration;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
use PrivateBin\Json;
|
||||
|
||||
/**
|
||||
|
|
@ -55,7 +55,7 @@ abstract class AbstractProxy
|
|||
}
|
||||
|
||||
if (!str_starts_with($link, $conf->getKey('basepath') . '?') ||
|
||||
parse_url($link, PHP_URL_HOST) != parse_url($conf->getKey('basepath'), PHP_URL_HOST)
|
||||
parse_url($link, PHP_URL_HOST) !== parse_url($conf->getKey('basepath'), PHP_URL_HOST)
|
||||
) {
|
||||
$this->_error = 'Trying to shorten a URL that isn\'t pointing at our instance.';
|
||||
return;
|
||||
|
|
@ -90,7 +90,7 @@ abstract class AbstractProxy
|
|||
|
||||
try {
|
||||
$jsonData = Json::decode($data);
|
||||
} catch (Exception $e) {
|
||||
} catch (JsonException $e) {
|
||||
$this->_error = 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.';
|
||||
$this->logErrorWithClassName('Error calling proxy: ' . $e->getMessage());
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
namespace PrivateBin\Proxy;
|
||||
|
||||
use PrivateBin\Configuration;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
use PrivateBin\Json;
|
||||
|
||||
/**
|
||||
|
|
@ -48,12 +49,17 @@ class ShlinkProxy extends AbstractProxy
|
|||
'longUrl' => $link,
|
||||
);
|
||||
|
||||
return array(
|
||||
'method' => 'POST',
|
||||
'header' => "Content-Type: application/json\r\n" .
|
||||
'X-Api-Key: ' . $shlink_api_key . "\r\n",
|
||||
'content' => Json::encode($body),
|
||||
);
|
||||
try {
|
||||
return array(
|
||||
'method' => 'POST',
|
||||
'header' => "Content-Type: application/json\r\n" .
|
||||
'X-Api-Key: ' . $shlink_api_key . "\r\n",
|
||||
'content' => Json::encode($body),
|
||||
);
|
||||
} catch (JsonException $e) {
|
||||
error_log('[' . get_class($this) . '] Error encoding body: ' . $e->getMessage());
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -65,11 +71,6 @@ class ShlinkProxy extends AbstractProxy
|
|||
*/
|
||||
protected function _extractShortUrl(array $data): ?string
|
||||
{
|
||||
if (
|
||||
array_key_exists('shortUrl', $data)
|
||||
) {
|
||||
return $data['shortUrl'];
|
||||
}
|
||||
return null;
|
||||
return $data['shortUrl'] ?? null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,12 +65,8 @@ class YourlsProxy extends AbstractProxy
|
|||
*/
|
||||
protected function _extractShortUrl(array $data): ?string
|
||||
{
|
||||
if (
|
||||
array_key_exists('statusCode', $data) &&
|
||||
$data['statusCode'] == 200 &&
|
||||
array_key_exists('shorturl', $data)
|
||||
) {
|
||||
return $data['shorturl'];
|
||||
if (($data['statusCode'] ?? 0) === 200) {
|
||||
return $data['shorturl'] ?? 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace PrivateBin;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Exception\JsonException;
|
||||
use PrivateBin\Model\Paste;
|
||||
|
||||
/**
|
||||
|
|
@ -104,7 +104,7 @@ class Request
|
|||
$this->_isJsonApi = $this->_detectJsonRequest();
|
||||
|
||||
// parse parameters, depending on request type
|
||||
switch (array_key_exists('REQUEST_METHOD', $_SERVER) ? $_SERVER['REQUEST_METHOD'] : 'GET') {
|
||||
switch ($_SERVER['REQUEST_METHOD'] ?? 'GET') {
|
||||
case 'DELETE':
|
||||
case 'PUT':
|
||||
case 'POST':
|
||||
|
|
@ -113,7 +113,7 @@ class Request
|
|||
try {
|
||||
$data = file_get_contents(self::$_inputStream);
|
||||
$this->_params = Json::decode($data);
|
||||
} catch (Exception $e) {
|
||||
} catch (JsonException $e) {
|
||||
// ignore error, $this->_params will remain empty
|
||||
}
|
||||
break;
|
||||
|
|
@ -141,7 +141,7 @@ class Request
|
|||
if (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
|
||||
if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken'])) {
|
||||
$this->_operation = 'delete';
|
||||
} elseif ($this->_operation != 'create') {
|
||||
} elseif ($this->_operation !== 'create') {
|
||||
$this->_operation = 'read';
|
||||
}
|
||||
} elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld'])) {
|
||||
|
|
@ -187,7 +187,7 @@ class Request
|
|||
$data['meta'] = $meta;
|
||||
}
|
||||
foreach ($required_keys as $key) {
|
||||
$data[$key] = $this->getParam($key, $key == 'v' ? 1 : '');
|
||||
$data[$key] = $this->getParam($key, $key === 'v' ? 1 : '');
|
||||
}
|
||||
// forcing a cast to int or float
|
||||
$data['v'] = $data['v'] + 0;
|
||||
|
|
@ -204,8 +204,7 @@ class Request
|
|||
*/
|
||||
public function getParam($param, $default = '')
|
||||
{
|
||||
return array_key_exists($param, $this->_params) ?
|
||||
$this->_params[$param] : $default;
|
||||
return $this->_params[$param] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -263,23 +262,22 @@ class Request
|
|||
*/
|
||||
private function _detectJsonRequest()
|
||||
{
|
||||
$hasAcceptHeader = array_key_exists('HTTP_ACCEPT', $_SERVER);
|
||||
$acceptHeader = $hasAcceptHeader ? $_SERVER['HTTP_ACCEPT'] : '';
|
||||
$acceptHeader = $_SERVER['HTTP_ACCEPT'] ?? '';
|
||||
|
||||
// simple cases
|
||||
if (
|
||||
(array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) &&
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] == 'JSONHttpRequest') ||
|
||||
($hasAcceptHeader &&
|
||||
($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === 'JSONHttpRequest' ||
|
||||
(
|
||||
str_contains($acceptHeader, self::MIME_JSON) &&
|
||||
!str_contains($acceptHeader, self::MIME_HTML) &&
|
||||
!str_contains($acceptHeader, self::MIME_XHTML))
|
||||
!str_contains($acceptHeader, self::MIME_XHTML)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// advanced case: media type negotiation
|
||||
if ($hasAcceptHeader) {
|
||||
if (!empty($acceptHeader)) {
|
||||
$mediaTypes = array();
|
||||
foreach (explode(',', trim($acceptHeader)) as $mediaTypeRange) {
|
||||
if (preg_match(
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class Vizhash16x16
|
|||
|
||||
// First, create an image with a specific gradient background.
|
||||
$op = 'v';
|
||||
if (($this->getInt() % 2) == 0) {
|
||||
if (($this->getInt() % 2) === 0) {
|
||||
$op = 'h';
|
||||
}
|
||||
$image = $this->degrade($image, $op, array($r0, $g0, $b0), array(0, 0, 0));
|
||||
|
|
@ -179,7 +179,7 @@ class Vizhash16x16
|
|||
*/
|
||||
private function degrade($img, $direction, $color1, $color2)
|
||||
{
|
||||
if ($direction == 'h') {
|
||||
if ($direction === 'h') {
|
||||
$size = imagesx($img);
|
||||
$sizeinv = imagesy($img);
|
||||
} else {
|
||||
|
|
@ -195,7 +195,7 @@ class Vizhash16x16
|
|||
$r = $color1[0] + ((int) $diffs[0] * $i);
|
||||
$g = $color1[1] + ((int) $diffs[1] * $i);
|
||||
$b = $color1[2] + ((int) $diffs[2] * $i);
|
||||
if ($direction == 'h') {
|
||||
if ($direction === 'h') {
|
||||
imageline($img, $i, 0, $i, $sizeinv, imagecolorallocate($img, $r, $g, $b));
|
||||
} else {
|
||||
imageline($img, 0, $i, $sizeinv, $i, imagecolorallocate($img, $r, $g, $b));
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ endif;
|
|||
foreach ($EXPIRE as $key => $value) :
|
||||
?>
|
||||
<option value="<?php echo $key; ?>"<?php
|
||||
if ($key == $EXPIREDEFAULT) :
|
||||
if ($key === $EXPIREDEFAULT) :
|
||||
?> selected="selected"<?php
|
||||
endif;
|
||||
?>><?php echo $value; ?></option>
|
||||
|
|
@ -327,7 +327,7 @@ if ($isCpct) :
|
|||
foreach ($FORMATTER as $key => $value) :
|
||||
?>
|
||||
<option value="<?php echo $key; ?>"<?php
|
||||
if ($key == $FORMATTERDEFAULT) :
|
||||
if ($key === $FORMATTERDEFAULT) :
|
||||
?> selected="selected"<?php
|
||||
endif;
|
||||
?>><?php echo $value; ?></option>
|
||||
|
|
@ -412,7 +412,7 @@ if (!$isCpct) :
|
|||
foreach ($FORMATTER as $key => $value) :
|
||||
?>
|
||||
<option value="<?php echo $key; ?>"<?php
|
||||
if ($key == $FORMATTERDEFAULT) :
|
||||
if ($key === $FORMATTERDEFAULT) :
|
||||
?> selected="selected"<?php
|
||||
endif;
|
||||
?>><?php echo $value; ?></option>
|
||||
|
|
@ -513,16 +513,19 @@ if ($FILEUPLOAD) :
|
|||
<?php
|
||||
endif;
|
||||
?>
|
||||
<div id="status" role="alert" class="clearfix alert alert-<?php echo (bool)$ISDELETED ? 'success' : 'info'; echo empty($STATUS) ? ' hidden' : '' ?>">
|
||||
<div id="status" role="alert" class="clearfix alert alert-<?php echo $ISDELETED ? 'success' : 'info'; echo empty($STATUS) ? ' hidden' : '' ?>">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
||||
<?php echo I18n::encode($STATUS), PHP_EOL; ?>
|
||||
<?php
|
||||
if ((bool)$ISDELETED):
|
||||
?>
|
||||
<button type="button" class="btn btn-default pull-right" id="new-from-alert">
|
||||
<span class="glyphicon glyphicon-repeat"></span> <?php echo I18n::_('Start over'), PHP_EOL; ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
if ($ISDELETED) :
|
||||
?>
|
||||
<button type="button" class="btn btn-default pull-right" id="new-from-alert">
|
||||
<span class="glyphicon glyphicon-repeat"></span>
|
||||
<?php echo I18n::_('Start over'), PHP_EOL; ?>
|
||||
</button>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
<div id="errormessage" role="alert" class="<?php echo empty($ERROR) ? 'hidden' : '' ?> alert alert-danger">
|
||||
<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ endif;
|
|||
foreach ($EXPIRE as $key => $value) :
|
||||
?>
|
||||
<option value="<?php echo $key; ?>"<?php
|
||||
if ($key == $EXPIREDEFAULT) :
|
||||
if ($key === $EXPIREDEFAULT) :
|
||||
?> selected="selected"<?php
|
||||
endif;
|
||||
?>><?php echo $value; ?></option>
|
||||
|
|
@ -287,7 +287,7 @@ endif;
|
|||
foreach ($FORMATTER as $key => $value) :
|
||||
?>
|
||||
<option value="<?php echo $key; ?>"<?php
|
||||
if ($key == $FORMATTERDEFAULT) :
|
||||
if ($key === $FORMATTERDEFAULT) :
|
||||
?> selected="selected"<?php
|
||||
endif;
|
||||
?>><?php echo $value; ?></option>
|
||||
|
|
@ -378,18 +378,21 @@ if ($FILEUPLOAD) :
|
|||
<?php
|
||||
endif;
|
||||
?>
|
||||
<div id="status" role="alert" class="d-flex justify-content-between align-items-center alert alert-<?php echo (bool)$ISDELETED ? 'success' : 'info'; echo empty($STATUS) ? ' hidden' : '' ?>">
|
||||
<div id="status" role="alert" class="d-flex justify-content-between align-items-center alert alert-<?php echo $ISDELETED ? 'success' : 'info'; echo empty($STATUS) ? ' hidden' : '' ?>">
|
||||
<div>
|
||||
<svg width="16" height="16" fill="currentColor" aria-hidden="true"><use href="img/bootstrap-icons.svg#info-circle" /></svg>
|
||||
<?php echo I18n::encode($STATUS), PHP_EOL; ?>
|
||||
</div>
|
||||
<?php
|
||||
if ((bool)$ISDELETED):
|
||||
?>
|
||||
<button type="button" class="btn btn-secondary d-flex justify-content-center align-items-center gap-1" id="new-from-alert">
|
||||
<svg width="16" height="16" fill="currentColor" aria-hidden="true"><use href="img/bootstrap-icons.svg#repeat" /></svg> <?php echo I18n::_('Start over'), PHP_EOL; ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
if ($ISDELETED) :
|
||||
?>
|
||||
<button type="button" class="btn btn-secondary d-flex justify-content-center align-items-center gap-1" id="new-from-alert">
|
||||
<svg width="16" height="16" fill="currentColor" aria-hidden="true"><use href="img/bootstrap-icons.svg#repeat" /></svg>
|
||||
<?php echo I18n::_('Start over'), PHP_EOL; ?>
|
||||
</button>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
<div id="errormessage" role="alert" class="<?php echo empty($ERROR) ? 'hidden' : '' ?> alert alert-danger">
|
||||
<svg width="16" height="16" fill="currentColor" aria-hidden="true"><use href="img/bootstrap-icons.svg#exclamation-triangle" /></svg>
|
||||
|
|
|
|||
|
|
@ -212,20 +212,15 @@ class Helper
|
|||
public static function rmDir($path): void
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
$path .= DIRECTORY_SEPARATOR;
|
||||
$dir = dir($path);
|
||||
while (false !== ($file = $dir->read())) {
|
||||
if ($file != '.' && $file != '..') {
|
||||
if (is_dir($path . $file)) {
|
||||
self::rmDir($path . $file);
|
||||
} elseif (is_file($path . $file)) {
|
||||
if (!unlink($path . $file)) {
|
||||
throw new Exception('Error deleting file "' . $path . $file . '".');
|
||||
}
|
||||
foreach (new DirectoryIterator($path) as $file) {
|
||||
if ($file->isFile()) {
|
||||
if (!unlink($file->getPathname())) {
|
||||
throw new Exception('Error deleting file "' . $file->getPathname() . '".');
|
||||
}
|
||||
} elseif ($file->isDir() && !$file->isDot()) {
|
||||
self::rmDir($file->getPathname());
|
||||
}
|
||||
}
|
||||
$dir->close();
|
||||
if (!rmdir($path)) {
|
||||
throw new Exception('Error deleting directory "' . $path . '".');
|
||||
}
|
||||
|
|
@ -361,7 +356,7 @@ class Helper
|
|||
file_get_contents($file)
|
||||
);
|
||||
file_put_contents($file, $content);
|
||||
if ($counter != count(self::$hashes)) {
|
||||
if ($counter !== count(self::$hashes)) {
|
||||
throw new Exception('Mismatch between ' . count(self::$hashes) . ' found js files and ' . $counter . ' SRI hashes in lib/Configuration.php, please update lib/Configuration.php to match the list of js files.');
|
||||
}
|
||||
}
|
||||
|
|
@ -404,7 +399,7 @@ class BucketStub extends Bucket
|
|||
|
||||
public function upload($data, array $options = array())
|
||||
{
|
||||
if (!is_string($data) || !key_exists('name', $options)) {
|
||||
if (!is_string($data) || !array_key_exists('name', $options)) {
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
|
|
@ -432,21 +427,17 @@ class BucketStub extends Bucket
|
|||
|
||||
public function object($name, array $options = array())
|
||||
{
|
||||
if (key_exists($name, $this->_objects)) {
|
||||
return $this->_objects[$name];
|
||||
} else {
|
||||
return new StorageObjectStub($this->_connection, $name, $this, null, $options);
|
||||
}
|
||||
return $this->_objects[$name] ?? new StorageObjectStub($this->_connection, $name, $this, null, $options);
|
||||
}
|
||||
|
||||
public function objects(array $options = array())
|
||||
{
|
||||
$prefix = key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
$prefix = $options['prefix'] ?? '';
|
||||
|
||||
return new CallbackFilterIterator(
|
||||
new ArrayIterator($this->_objects),
|
||||
function ($current, $key, $iterator) use ($prefix) {
|
||||
return substr($key, 0, strlen($prefix)) == $prefix;
|
||||
return substr($key, 0, strlen($prefix)) === $prefix;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -563,7 +554,7 @@ class StorageObjectStub extends StorageObject
|
|||
|
||||
public function exists(array $options = array())
|
||||
{
|
||||
return key_exists($this->_name, $this->_bucket->_objects);
|
||||
return array_key_exists($this->_name, $this->_bucket->_objects);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -571,7 +562,7 @@ class StorageObjectStub extends StorageObject
|
|||
*/
|
||||
public function delete(array $options = array())
|
||||
{
|
||||
if (key_exists($this->_name, $this->_bucket->_objects)) {
|
||||
if (array_key_exists($this->_name, $this->_bucket->_objects)) {
|
||||
unset($this->_bucket->_objects[$this->_name]);
|
||||
} else {
|
||||
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
||||
|
|
@ -647,7 +638,7 @@ class StorageObjectStub extends StorageObject
|
|||
|
||||
public function info(array $options = array())
|
||||
{
|
||||
return key_exists('metadata',$this->_info) ? $this->_info['metadata'] : array();
|
||||
return $this->_info['metadata'] ?? array();
|
||||
}
|
||||
|
||||
public function reload(array $options = array())
|
||||
|
|
@ -884,7 +875,7 @@ class StorageClientStub extends StorageClient
|
|||
|
||||
public function bucket($name, $userProject = false, array $config = array())
|
||||
{
|
||||
if (!key_exists($name, self::$_buckets)) {
|
||||
if (!array_key_exists($name, self::$_buckets)) {
|
||||
self::$_buckets[$name] = new BucketStub($this->_connection, $name, array(), $this);
|
||||
}
|
||||
return self::$_buckets[$name];
|
||||
|
|
@ -895,7 +886,7 @@ class StorageClientStub extends StorageClient
|
|||
*/
|
||||
public function deleteBucket($name)
|
||||
{
|
||||
if (key_exists($name, self::$_buckets)) {
|
||||
if (array_key_exists($name, self::$_buckets)) {
|
||||
unset(self::$_buckets[$name]);
|
||||
} else {
|
||||
throw new NotFoundException();
|
||||
|
|
@ -949,7 +940,7 @@ class StorageClientStub extends StorageClient
|
|||
|
||||
public function createBucket($name, array $options = array())
|
||||
{
|
||||
if (key_exists($name, self::$_buckets)) {
|
||||
if (array_key_exists($name, self::$_buckets)) {
|
||||
throw new BadRequestException('already exists');
|
||||
}
|
||||
$b = new BucketStub($this->_connection, $name, array(), $this);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PrivateBin\I18n;
|
||||
use PrivateBin\Json;
|
||||
|
||||
class I18nMock extends I18n
|
||||
{
|
||||
|
|
@ -221,19 +222,19 @@ class I18nTest extends TestCase
|
|||
{
|
||||
$messageIds = array();
|
||||
$languages = array();
|
||||
$dir = dir(PATH . 'i18n');
|
||||
while (false !== ($file = $dir->read())) {
|
||||
if (strlen($file) === 7) {
|
||||
$language = substr($file, 0, 2);
|
||||
$languageMessageIds = array_keys(
|
||||
json_decode(
|
||||
file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file),
|
||||
true
|
||||
)
|
||||
);
|
||||
$messageIds = array_unique(array_merge($messageIds, $languageMessageIds));
|
||||
$languages[$language] = $languageMessageIds;
|
||||
foreach (new DirectoryIterator(PATH . 'i18n') as $file) {
|
||||
$fileNameLength = strlen($file->getFilename());
|
||||
if ($fileNameLength === 7) { // xx.json
|
||||
$language = substr($file->getFilename(), 0, 2);
|
||||
} elseif ($fileNameLength === 8) { // jbo.json
|
||||
$language = substr($file->getFilename(), 0, 3);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
$languageJson = file_get_contents($file->getPathname());
|
||||
$languageMessageIds = array_keys(Json::decode($languageJson));
|
||||
$messageIds = array_unique(array_merge($messageIds, $languageMessageIds));
|
||||
$languages[$language] = $languageMessageIds;
|
||||
}
|
||||
foreach ($messageIds as $messageId) {
|
||||
foreach (array_keys($languages) as $language) {
|
||||
|
|
|
|||
|
|
@ -67,10 +67,9 @@ class ViewTest extends TestCase
|
|||
$page->assign('CSPHEADER', 'default-src \'none\'');
|
||||
$page->assign('SRI', array());
|
||||
|
||||
$dir = dir(PATH . 'tpl');
|
||||
while (false !== ($file = $dir->read())) {
|
||||
if (substr($file, -4) === '.php') {
|
||||
$template = substr($file, 0, -4);
|
||||
foreach (new DirectoryIterator(PATH . 'tpl') as $file) {
|
||||
if ($file->getExtension() === 'php') {
|
||||
$template = $file->getBasename('.php');
|
||||
ob_start();
|
||||
$page->draw($template);
|
||||
$this->_content[$template] = ob_get_contents();
|
||||
|
|
|
|||
2
vendor/composer/autoload_classmap.php
vendored
2
vendor/composer/autoload_classmap.php
vendored
|
|
@ -74,6 +74,8 @@ return array(
|
|||
'PrivateBin\\Data\\Filesystem' => $baseDir . '/lib/Data/Filesystem.php',
|
||||
'PrivateBin\\Data\\GoogleCloudStorage' => $baseDir . '/lib/Data/GoogleCloudStorage.php',
|
||||
'PrivateBin\\Data\\S3Storage' => $baseDir . '/lib/Data/S3Storage.php',
|
||||
'PrivateBin\\Exception\\JsonException' => $baseDir . '/lib/Exception/JsonException.php',
|
||||
'PrivateBin\\Exception\\TranslatedException' => $baseDir . '/lib/Exception/TranslatedException.php',
|
||||
'PrivateBin\\Filter' => $baseDir . '/lib/Filter.php',
|
||||
'PrivateBin\\FormatV2' => $baseDir . '/lib/FormatV2.php',
|
||||
'PrivateBin\\I18n' => $baseDir . '/lib/I18n.php',
|
||||
|
|
|
|||
2
vendor/composer/autoload_static.php
vendored
2
vendor/composer/autoload_static.php
vendored
|
|
@ -122,6 +122,8 @@ class ComposerStaticInitDontChange
|
|||
'PrivateBin\\Data\\Filesystem' => __DIR__ . '/../..' . '/lib/Data/Filesystem.php',
|
||||
'PrivateBin\\Data\\GoogleCloudStorage' => __DIR__ . '/../..' . '/lib/Data/GoogleCloudStorage.php',
|
||||
'PrivateBin\\Data\\S3Storage' => __DIR__ . '/../..' . '/lib/Data/S3Storage.php',
|
||||
'PrivateBin\\Exception\\JsonException' => __DIR__ . '/../..' . '/lib/Exception/JsonException.php',
|
||||
'PrivateBin\\Exception\\TranslatedException' => __DIR__ . '/../..' . '/lib/Exception/TranslatedException.php',
|
||||
'PrivateBin\\Filter' => __DIR__ . '/../..' . '/lib/Filter.php',
|
||||
'PrivateBin\\FormatV2' => __DIR__ . '/../..' . '/lib/FormatV2.php',
|
||||
'PrivateBin\\I18n' => __DIR__ . '/../..' . '/lib/I18n.php',
|
||||
|
|
|
|||
4
vendor/composer/installed.php
vendored
4
vendor/composer/installed.php
vendored
|
|
@ -3,7 +3,7 @@
|
|||
'name' => 'privatebin/privatebin',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '06496a1b0e975b79c5a7abc0bd54b492ca264640',
|
||||
'reference' => '8eb39b4ffa8daed3d8ff0279c9870ee5b6b0bd95',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
'privatebin/privatebin' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '06496a1b0e975b79c5a7abc0bd54b492ca264640',
|
||||
'reference' => '8eb39b4ffa8daed3d8ff0279c9870ee5b6b0bd95',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue