diff --git a/.gitattributes b/.gitattributes index 9c8fa78..345f98d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,4 +8,5 @@ tst/ export-ignore .gitattributes export-ignore .github export-ignore .gitignore export-ignore +.php_cs export-ignore .travis.yml export-ignore diff --git a/.gitignore b/.gitignore index 0323182..8172e1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,12 @@ -# Ignore data/, tmp/ and vendor/ -data/ -!lib/data/ -tmp/ -vendor/ # Ignore for safety .htaccess .htpasswd +# Ignore data/ and vendor/ +data/ +vendor/ # Ignore unit testing logs, api docs and eclipse project files -tst/log/ doc/ -composer.lock +tst/log/ .settings .buildpath .project diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..9dc9322 --- /dev/null +++ b/.php_cs @@ -0,0 +1,23 @@ +in('lib') +; + +return Symfony\CS\Config\Config::create() + ->level(Symfony\CS\FixerInterface::PSR2_LEVEL) + ->fixers(['concat_with_spaces', 'long_array_syntax', 'standardize_not_equal', + 'operators_spaces', 'duplicate_semicolon', + 'remove_leading_slash_use', 'align_equals', + 'single_array_no_trailing_comma', 'phpdoc_indent', 'phpdoc_scalar', + 'phpdoc_to_comment', 'phpdoc_trim', + 'phpdoc_types', 'print_to_echo', 'self_accessor', 'single_quote', + 'spaces_cast', 'ternary_spaces', 'phpdoc_order']) + ->finder($finder) +; diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b2c0bd..7ebaef2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ * ADDED: re-introduced URL shortener support (optional), which was removed back in version 0.16 for privacy concerns * ADDED: Preview tab, helpful for writing markdown code or check source code rendering * ADDED: Automatic purging of expired pastes, done on paste creation - * ADDED: Option to disable vizhashs in discussions (will only affect newly created pastes) + * ADDED: Option to disable icons in discussions (will only affect newly created pastes) * ADDED: Composer support * CHANGED: Renamed the ZeroBin fork to PrivateBin * CHANGED: Removed unmaintained RainTPL template engine, replacing the templates with straight forward PHP files @@ -14,7 +14,9 @@ * CHANGED: Switched to GCM instead CCM mode for AES encryption for newly created pastes * CHANGED: Switched to a SHA256 HMAC of the IP in traffic limiter instead of storing it in plain text on the server * CHANGED: Introduced content security policy header to reduce cross site scripting (XSS) risks - * CHANGED: Refactored PHP code to conform to PSR-4 and PSR-2 standards. + * CHANGED: Refactored PHP code to conform to PSR-4 and PSR-2 standards + * CHANGED: Switched to Identicons as the default for comments with nicknames + * CHANGED: Vizhash is now optional and based on (128 byte) SHA512 HMAC instead of (144 bytes) combination of MD5, SHA1 and a reversal of that string * FIXED: Content-type negociation for HTML in certain uncommon browser configurations * FIXED: JavaScript error displayed before page is loaded or during attachment load * FIXED: Don't strip space characters at beginning or end of optional password diff --git a/LICENSE.md b/LICENSE.md index 2e7e76f..4d4f243 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -4,8 +4,9 @@ PrivateBin consists of PHP and JS code which was originally written by Sébastie Sauvage in 2012 and falls unter the Zlib/libpng license. Also included are libraries that fall under the GPLv2 (SJCL, rawinflate, rawdeflate), BSD 2-clause (SJCL), BSD 3-clause (base64.js version 2.1.9, Showdown), MIT -(base64.js version 1.7, Bootstrap), Apache (prettify.js) and CC-BY (favicon, -icon, logo) licenses. All of these license terms can be found here below: +(base64.js version 1.7, Bootstrap, Identicon), Apache (prettify.js) and CC-BY +(favicon, icon, logo) licenses. All of these license terms can be found here +below: ## Zlib/libpng license for PrivateBin @@ -440,6 +441,28 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +## MIT License for Identicon + +Copyright © 2013 Benjamin Laugueux + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + ## Apache License for prettify.js _Version 2.0, January 2004_ diff --git a/cfg/conf.ini.sample b/cfg/conf.ini.sample index 9606264..6321069 100644 --- a/cfg/conf.ini.sample +++ b/cfg/conf.ini.sample @@ -1,11 +1,6 @@ -; 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 -; @version 0.22 +; config file for PrivateBin +; +; An explanation of each setting can be find online at https://github.com/PrivateBin/PrivateBin/wiki/Configuration. [main] ; enable or disable the discussion feature, defaults to true @@ -53,18 +48,19 @@ languageselection = false ; the pastes encryption key ; urlshortener = "https://shortener.example.com/api?link=" -; (optional) vizhash is a weak mechanism to detect if a comment was from a -; different user when the same username was used in a comment. It is based on -; the IP and might be used to get the posters IP if the server salt is leaked -; and a rainbow table is generated for all IPs. Enabled by default. -; vizhash = false +; (optional) IP based icons are a weak mechanism to detect if a comment was from +; a different user when the same username was used in a comment. It might be +; used to get the IP of a non anonymous comment poster if the server salt is +; leaked and a SHA256 HMAC rainbow table is generated for all (relevant) IPs. +; Can be set to one these values: none / vizhash / identicon (default). +; icon = none ; Content Security Policy headers allow a website to restrict what sources are ; allowed to be accessed in its context. You need to change this if you added ; custom scripts from third-party domains to your templates, e.g. tracking ; scripts or run your site behind certain DDoS-protection services. ; Check the documentation at https://content-security-policy.com/ -cspheader = "default-src 'none'; connect-src *; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self';" +cspheader = "default-src 'none'; connect-src *; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:;" ; 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/composer.json b/composer.json index 227e99c..1a80bc5 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ ], "require": { "php": "^5.2.6 || ^7.0", - "paragonie/random_compat": "^2.0" + "paragonie/random_compat": "^2.0", + "yzalis/identicon": "^1.1" }, "require-dev": { "codacy/coverage": "dev-master", diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..a81b864 --- /dev/null +++ b/composer.lock @@ -0,0 +1,858 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "e3520dc72004bd92b2bd0b0febf71c7f", + "content-hash": "ac3ea1f44998ea42345107fd21d6a2e0", + "packages": [ + { + "name": "yzalis/identicon", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/yzalis/Identicon.git", + "reference": "a99fc2a3d018512f7914bc6f972952536c0f309b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yzalis/Identicon/zipball/a99fc2a3d018512f7914bc6f972952536c0f309b", + "reference": "a99fc2a3d018512f7914bc6f972952536c0f309b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "fzaninotto/faker": "1.2.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-0": { + "Identicon": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Benjamin Laugueux", + "email": "benjamin@yzalis.com" + } + ], + "description": "Create awesome unique avatar.", + "homepage": "http://identicon-php.org", + "keywords": [ + "avatar", + "identicon", + "image" + ], + "time": "2014-07-13 09:19:12" + } + ], + "packages-dev": [ + { + "name": "codacy/coverage", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/codacy/php-codacy-coverage.git", + "reference": "92194b1ece3379e56bb1f95d6f540fc6244d9946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/codacy/php-codacy-coverage/zipball/92194b1ece3379e56bb1f95d6f540fc6244d9946", + "reference": "92194b1ece3379e56bb1f95d6f540fc6244d9946", + "shasum": "" + }, + "require": { + "gitonomy/gitlib": "~0.1", + "php": ">=5.3.3", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + }, + "bin": [ + "bin/codacycoverage" + ], + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jakob Pupke", + "email": "jakob.pupke@gmail.com" + } + ], + "description": "Sends PHP test coverage information to Codacy.", + "homepage": "https://github.com/codacy/php-codacy-coverage", + "time": "2016-07-07 15:33:12" + }, + { + "name": "codeclimate/php-test-reporter", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/codeclimate/php-test-reporter.git", + "reference": "68786c391d2b859054046a7b7ed07c64e7b741a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/codeclimate/php-test-reporter/zipball/68786c391d2b859054046a7b7ed07c64e7b741a1", + "reference": "68786c391d2b859054046a7b7ed07c64e7b741a1", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3", + "satooshi/php-coveralls": "^1.0", + "symfony/console": "^2.0|^3.0" + }, + "require-dev": { + "ext-xdebug": "*", + "phpunit/phpunit": "3.7.*@stable", + "tm/tooly-composer-script": "^1.0" + }, + "bin": [ + "composer/bin/test-reporter" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.3.x-dev" + }, + "tools": { + "box": { + "url": "https://github.com/box-project/box2/releases/download/2.7.2/box-2.7.2.phar", + "only-dev": true + } + } + }, + "autoload": { + "psr-4": { + "CodeClimate\\PhpTestReporter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Code Climate", + "email": "hello@codeclimate.com", + "homepage": "https://codeclimate.com" + } + ], + "description": "PHP client for reporting test coverage to Code Climate", + "homepage": "https://github.com/codeclimate/php-test-reporter", + "keywords": [ + "codeclimate", + "coverage" + ], + "time": "2016-08-09 19:41:51" + }, + { + "name": "gitonomy/gitlib", + "version": "v0.1.8", + "source": { + "type": "git", + "url": "https://github.com/gitonomy/gitlib.git", + "reference": "f575b8f7da917ade7890c6aa705fa22545690389" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/gitonomy/gitlib/zipball/f575b8f7da917ade7890c6aa705fa22545690389", + "reference": "f575b8f7da917ade7890c6aa705fa22545690389", + "shasum": "" + }, + "require": { + "symfony/process": "^2.3|^3.0" + }, + "require-dev": { + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Add some log" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Gitonomy\\Git\\": "src/Gitonomy/Git/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexandre Salomé", + "email": "alexandre.salome@gmail.com", + "homepage": "http://alexandre-salome.fr" + }, + { + "name": "Julien DIDIER", + "email": "genzo.wm@gmail.com", + "homepage": "http://www.jdidier.net" + } + ], + "description": "Library for accessing git", + "homepage": "http://gitonomy.com", + "time": "2015-12-01 22:25:57" + }, + { + "name": "guzzle/guzzle", + "version": "v3.9.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "abandoned": "guzzlehttp/guzzle", + "time": "2015-03-18 18:23:50" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "satooshi/php-coveralls", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/satooshi/php-coveralls.git", + "reference": "da51d304fe8622bf9a6da39a8446e7afd432115c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/da51d304fe8622bf9a6da39a8446e7afd432115c", + "reference": "da51d304fe8622bf9a6da39a8446e7afd432115c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-simplexml": "*", + "guzzle/guzzle": "^2.8|^3.0", + "php": ">=5.3.3", + "psr/log": "^1.0", + "symfony/config": "^2.1|^3.0", + "symfony/console": "^2.1|^3.0", + "symfony/stopwatch": "^2.0|^3.0", + "symfony/yaml": "^2.0|^3.0" + }, + "suggest": { + "symfony/http-kernel": "Allows Symfony integration" + }, + "bin": [ + "bin/coveralls" + ], + "type": "library", + "autoload": { + "psr-4": { + "Satooshi\\": "src/Satooshi/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kitamura Satoshi", + "email": "with.no.parachute@gmail.com", + "homepage": "https://www.facebook.com/satooshi.jp" + } + ], + "description": "PHP client library for Coveralls API", + "homepage": "https://github.com/satooshi/php-coveralls", + "keywords": [ + "ci", + "coverage", + "github", + "test" + ], + "time": "2016-01-20 17:35:46" + }, + { + "name": "symfony/config", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "a7630397b91be09cdd2fe57fd13612e258700598" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/a7630397b91be09cdd2fe57fd13612e258700598", + "reference": "a7630397b91be09cdd2fe57fd13612e258700598", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/filesystem": "~2.8|~3.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2016-07-26 08:04:17" + }, + { + "name": "symfony/console", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "f9e638e8149e9e41b570ff092f8007c477ef0ce5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/f9e638e8149e9e41b570ff092f8007c477ef0ce5", + "reference": "f9e638e8149e9e41b570ff092f8007c477ef0ce5", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2016-07-26 08:04:17" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.8.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "889983a79a043dfda68f38c38b6dba092dd49cd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/889983a79a043dfda68f38c38b6dba092dd49cd8", + "reference": "889983a79a043dfda68f38c38b6dba092dd49cd8", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2016-07-28 16:56:28" + }, + { + "name": "symfony/filesystem", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "bb29adceb552d202b6416ede373529338136e84f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/bb29adceb552d202b6416ede373529338136e84f", + "reference": "bb29adceb552d202b6416ede373529338136e84f", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2016-07-20 05:44:26" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "dff51f72b0706335131b00a7f49606168c582594" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", + "reference": "dff51f72b0706335131b00a7f49606168c582594", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/process", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "04c2dfaae4ec56a5c677b0c69fac34637d815758" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/04c2dfaae4ec56a5c677b0c69fac34637d815758", + "reference": "04c2dfaae4ec56a5c677b0c69fac34637d815758", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2016-07-28 11:13:48" + }, + { + "name": "symfony/stopwatch", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "bb42806b12c5f89db4ebf64af6741afe6d8457e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/bb42806b12c5f89db4ebf64af6741afe6d8457e1", + "reference": "bb42806b12c5f89db4ebf64af6741afe6d8457e1", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2016-06-29 05:41:56" + }, + { + "name": "symfony/yaml", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "1819adf2066880c7967df7180f4f662b6f0567ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/1819adf2066880c7967df7180f4f662b6f0567ac", + "reference": "1819adf2066880c7967df7180f4f662b6f0567ac", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2016-07-17 14:02:08" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "codacy/coverage": 20, + "codeclimate/php-test-reporter": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^5.3 || ^7.0" + }, + "platform-dev": [] +} diff --git a/css/bootstrap/privatebin.css b/css/bootstrap/privatebin.css index 4c32591..d51c3ba 100644 --- a/css/bootstrap/privatebin.css +++ b/css/bootstrap/privatebin.css @@ -17,11 +17,19 @@ body.navbar-spacing { margin: 0 8px; } +.nav.navbar-nav > li { + margin-left: 8px; +} + .navbar-brand { padding: 3px; padding-left: 8px; } +.navbar-form { + padding: 0; +} + .dropdown-menu > li > label, .dropdown-menu > li > div { clear: both; display: block; @@ -49,10 +57,6 @@ body.navbar-spacing { margin-right: 8px; } -.dropdown > #expiration { - margin-left: 8px; -} - #image img { max-width: 100%; height: auto; diff --git a/js/privatebin.js b/js/privatebin.js index 8244c13..d8aa56e 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -741,6 +741,7 @@ $(function() { this.passwordInput.val(password); if (cleartext.length > 0) { + $('#pasteFormatter').val(paste.meta.formatter); this.formatPaste(paste.meta.formatter, cleartext); } } @@ -870,12 +871,12 @@ $(function() { '
' + '' + '' + - '
' + - '
' + - '
' + '
' ); reply.find('button').click({parentid: commentid}, $.proxy(this.sendComment, this)); source.after(reply); + this.replyStatus = $('#replystatus'); $('#replymessage').focus(); }, @@ -1201,6 +1202,23 @@ $(function() { } }, + /** + * If discussion is checked, disable "burn after reading". + */ + changeOpenDisc: function() + { + if (this.openDiscussion.is(':checked') ) + { + this.burnAfterReadingOption.addClass('buttondisabled'); + this.burnAfterReading.attr({checked: false, disabled: true}); + } + else + { + this.burnAfterReadingOption.removeClass('buttondisabled'); + this.burnAfterReading.removeAttr('disabled'); + } + }, + /** * Reload the page. * @@ -1220,7 +1238,8 @@ $(function() { rawText: function(event) { event.preventDefault(); - var paste = this.clearText.html(); + var paste = $('#pasteFormatter').val() === 'markdown' ? + this.prettyPrint.text() : this.clearText.text(); var newDoc = document.open('text/html', 'replace'); newDoc.write('
' + paste + '
'); newDoc.close(); @@ -1245,7 +1264,10 @@ $(function() { this.clonedFile.removeClass('hidden'); this.fileWrap.addClass('hidden'); } - this.message.text(this.clearText.text()); + this.message.text( + $('#pasteFormatter').val() === 'markdown' ? + this.prettyPrint.text() : this.clearText.text() + ); $('.navbar-toggle').click(); }, @@ -1273,6 +1295,10 @@ $(function() { var target = $(event.target); $('#pasteFormatter').val(target.data('format')); $('#pasteFormatterDisplay').text(target.text()); + + if (this.messagePreview.parent().hasClass('active')) { + this.viewPreview(event); + } }, /** @@ -1349,6 +1375,8 @@ $(function() { this.stateNewPaste(); this.showStatus('', false); this.message.text(''); + this.changeBurnAfterReading(); + this.changeOpenDisc(); }, /** @@ -1381,7 +1409,18 @@ $(function() { this.errorMessage.removeClass('hidden'); helper.setMessage(this.errorMessage, message); } - this.replyStatus.addClass('errorMessage').text(message); + if (typeof this.replyStatus !== 'undefined') { + this.replyStatus.addClass('errorMessage'); + this.replyStatus.addClass(this.errorMessage.attr('class')); + if (this.status.length) + { + this.replyStatus.html(this.status.html()); + } + else + { + this.replyStatus.html(this.errorMessage.html()); + } + } }, /** @@ -1393,7 +1432,9 @@ $(function() { */ showStatus: function(message, spin) { - this.replyStatus.removeClass('errorMessage').text(message); + if (typeof this.replyStatus !== 'undefined') { + this.replyStatus.removeClass('errorMessage').text(message); + } if (!message) { this.status.html(' '); @@ -1409,7 +1450,9 @@ $(function() { { var img = ''; this.status.prepend(img); - this.replyStatus.prepend(img); + if (typeof this.replyStatus !== 'undefined') { + this.replyStatus.prepend(img); + } } }, @@ -1419,6 +1462,7 @@ $(function() { bindEvents: function() { this.burnAfterReading.change($.proxy(this.changeBurnAfterReading, this)); + this.openDisc.change($.proxy(this.changeOpenDisc, this)); this.sendButton.click($.proxy(this.sendData, this)); this.cloneButton.click($.proxy(this.clonePaste, this)); this.rawTextButton.click($.proxy(this.rawText, this)); @@ -1477,7 +1521,6 @@ $(function() { this.preview = $('#preview'); this.rawTextButton = $('#rawtextbutton'); this.remainingTime = $('#remainingtime'); - this.replyStatus = $('#replystatus'); this.sendButton = $('#sendbutton'); this.status = $('#status'); this.bindEvents(); diff --git a/lib/Configuration.php b/lib/Configuration.php index 44a35d6..2ca46b4 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -50,8 +50,8 @@ class Configuration 'languageselection' => false, 'languagedefault' => '', 'urlshortener' => '', - 'vizhash' => true, - 'cspheader' => 'default-src \'none\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\';', + 'icon' => 'identicon', + 'cspheader' => 'default-src \'none\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:;', 'zerobincompatibility' => false, ), 'expire' => array( @@ -98,7 +98,7 @@ class Configuration */ public function __construct() { - $config = array(); + $config = array(); $configFile = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini'; if (is_readable($configFile)) { $config = parse_ini_file($configFile, true); diff --git a/lib/Data/Database.php b/lib/Data/Database.php index d774439..95fa47f 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -71,7 +71,7 @@ class Database extends AbstractData public static function getInstance($options = null) { // if needed initialize the singleton - if (!(self::$_instance instanceof Database)) { + if (!(self::$_instance instanceof self)) { self::$_instance = new self; } @@ -89,17 +89,17 @@ class Database extends AbstractData array_key_exists('opt', $options) ) { // set default options - $options['opt'][PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; + $options['opt'][PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; $options['opt'][PDO::ATTR_EMULATE_PREPARES] = false; - $options['opt'][PDO::ATTR_PERSISTENT] = true; - $db_tables_exist = true; + $options['opt'][PDO::ATTR_PERSISTENT] = true; + $db_tables_exist = true; // setup type and dabase connection self::$_type = strtolower( substr($options['dsn'], 0, strpos($options['dsn'], ':')) ); $tableQuery = self::_getTableQuery(self::$_type); - self::$_db = new PDO( + self::$_db = new PDO( $options['dsn'], $options['usr'], $options['pwd'], @@ -168,8 +168,8 @@ class Database extends AbstractData } $opendiscussion = $burnafterreading = false; - $attachment = $attachmentname = ''; - $meta = $paste['meta']; + $attachment = $attachmentname = ''; + $meta = $paste['meta']; unset($meta['postdate']); $expire_date = 0; if (array_key_exists('expire_date', $paste['meta'])) { @@ -222,14 +222,14 @@ class Database extends AbstractData !array_key_exists($pasteid, self::$_cache) ) { self::$_cache[$pasteid] = false; - $paste = self::_select( + $paste = self::_select( 'SELECT * FROM ' . self::_sanitizeIdentifier('paste') . ' WHERE dataid = ?', array($pasteid), true ); if (false !== $paste) { // create object - self::$_cache[$pasteid] = new stdClass; + self::$_cache[$pasteid] = new stdClass; self::$_cache[$pasteid]->data = $paste['data']; $meta = json_decode($paste['meta']); @@ -253,9 +253,9 @@ class Database extends AbstractData self::$_cache[$pasteid]->attachmentname = $paste['attachmentname']; } } - self::$_cache[$pasteid]->meta = $meta; + self::$_cache[$pasteid]->meta = $meta; self::$_cache[$pasteid]->meta->postdate = (int) $paste['postdate']; - $expire_date = (int) $paste['expiredate']; + $expire_date = (int) $paste['expiredate']; if ( $expire_date > 0 ) { @@ -368,12 +368,12 @@ class Database extends AbstractData $comments = array(); if (count($rows)) { foreach ($rows as $row) { - $i = $this->getOpenSlot($comments, (int) $row['postdate']); - $comments[$i] = new stdClass; - $comments[$i]->id = $row['dataid']; - $comments[$i]->parentid = $row['parentid']; - $comments[$i]->data = $row['data']; - $comments[$i]->meta = new stdClass; + $i = $this->getOpenSlot($comments, (int) $row['postdate']); + $comments[$i] = new stdClass; + $comments[$i]->id = $row['dataid']; + $comments[$i]->parentid = $row['parentid']; + $comments[$i]->data = $row['data']; + $comments[$i]->meta = new stdClass; $comments[$i]->meta->postdate = (int) $row['postdate']; if (array_key_exists('nickname', $row) && !empty($row['nickname'])) { $comments[$i]->meta->nickname = $row['nickname']; @@ -415,7 +415,7 @@ class Database extends AbstractData protected function _getExpiredPastes($batchsize) { $pastes = array(); - $rows = self::_select( + $rows = self::_select( 'SELECT dataid FROM ' . self::_sanitizeIdentifier('paste') . ' WHERE expiredate < ? LIMIT ?', array(time(), $batchsize) ); @@ -440,7 +440,7 @@ class Database extends AbstractData private static function _exec($sql, array $params) { $statement = self::$_db->prepare($sql); - $result = $statement->execute($params); + $result = $statement->execute($params); $statement->closeCursor(); return $result; } @@ -486,7 +486,7 @@ class Database extends AbstractData $sql = 'SELECT tabname FROM systables '; break; case 'mssql': - $sql = "SELECT name FROM sysobjects " + $sql = 'SELECT name FROM sysobjects ' . "WHERE type = 'U' ORDER BY name"; break; case 'mysql': @@ -496,22 +496,22 @@ class Database extends AbstractData $sql = 'SELECT table_name FROM all_tables'; break; case 'pgsql': - $sql = "SELECT c.relname AS table_name " - . "FROM pg_class c, pg_user u " + $sql = 'SELECT c.relname AS table_name ' + . 'FROM pg_class c, pg_user u ' . "WHERE c.relowner = u.usesysid AND c.relkind = 'r' " - . "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) " + . 'AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) ' . "AND c.relname !~ '^(pg_|sql_)' " - . "UNION " - . "SELECT c.relname AS table_name " - . "FROM pg_class c " + . 'UNION ' + . 'SELECT c.relname AS table_name ' + . 'FROM pg_class c ' . "WHERE c.relkind = 'r' " - . "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) " - . "AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) " + . 'AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) ' + . 'AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) ' . "AND c.relname !~ '^pg_'"; break; case 'sqlite': $sql = "SELECT name FROM sqlite_master WHERE type='table' " - . "UNION ALL SELECT name FROM sqlite_temp_master " + . 'UNION ALL SELECT name FROM sqlite_temp_master ' . "WHERE type='table' ORDER BY name"; break; default: @@ -569,7 +569,7 @@ class Database extends AbstractData private static function _createPasteTable() { list($main_key, $after_key) = self::_getPrimaryKeyClauses(); - $dataType = self::$_type === 'pgsql' ? 'TEXT' : 'BLOB'; + $dataType = self::$_type === 'pgsql' ? 'TEXT' : 'BLOB'; self::$_db->exec( 'CREATE TABLE ' . self::_sanitizeIdentifier('paste') . ' ( ' . "dataid CHAR(16) NOT NULL$main_key, " . @@ -594,7 +594,7 @@ class Database extends AbstractData private static function _createCommentTable() { list($main_key, $after_key) = self::_getPrimaryKeyClauses(); - $dataType = self::$_type === 'pgsql' ? 'text' : 'BLOB'; + $dataType = self::$_type === 'pgsql' ? 'text' : 'BLOB'; self::$_db->exec( 'CREATE TABLE ' . self::_sanitizeIdentifier('comment') . ' ( ' . "dataid CHAR(16) NOT NULL$main_key, " . diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index eabc271..3507ab6 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -13,6 +13,7 @@ namespace PrivateBin\Data; use PrivateBin\Model\Paste; +use PrivateBin\Json; /** * Filesystem @@ -48,7 +49,7 @@ class Filesystem extends AbstractData self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR; } // if needed initialize the singleton - if (!(self::$_instance instanceof Filesystem)) { + if (!(self::$_instance instanceof self)) { self::$_instance = new self; self::_init(); } @@ -61,6 +62,7 @@ class Filesystem extends AbstractData * @access public * @param string $pasteid * @param array $paste + * @throws Exception * @return bool */ public function create($pasteid, $paste) @@ -72,7 +74,7 @@ class Filesystem extends AbstractData if (!is_dir($storagedir)) { mkdir($storagedir, 0700, true); } - return (bool) file_put_contents($storagedir . $pasteid, json_encode($paste)); + return (bool) file_put_contents($storagedir . $pasteid, Json::encode($paste)); } /** @@ -153,19 +155,20 @@ class Filesystem extends AbstractData * @param string $parentid * @param string $commentid * @param array $comment + * @throws Exception * @return bool */ public function createComment($pasteid, $parentid, $commentid, $comment) { $storagedir = self::_dataid2discussionpath($pasteid); - $filename = $pasteid . '.' . $commentid . '.' . $parentid; + $filename = $pasteid . '.' . $commentid . '.' . $parentid; if (is_file($storagedir . $filename)) { return false; } if (!is_dir($storagedir)) { mkdir($storagedir, 0700, true); } - return (bool) file_put_contents($storagedir . $filename, json_encode($comment)); + return (bool) file_put_contents($storagedir . $filename, Json::encode($comment)); } /** @@ -178,7 +181,7 @@ class Filesystem extends AbstractData public function readComments($pasteid) { $comments = array(); - $discdir = self::_dataid2discussionpath($pasteid); + $discdir = self::_dataid2discussionpath($pasteid); if (is_dir($discdir)) { // Delete all files in discussion directory $dir = dir($discdir); @@ -189,13 +192,13 @@ class Filesystem extends AbstractData // - parentid is the comment this comment replies to (It can be pasteid) if (is_file($discdir . $filename)) { $comment = json_decode(file_get_contents($discdir . $filename)); - $items = explode('.', $filename); + $items = explode('.', $filename); // Add some meta information not contained in file. - $comment->id = $items[1]; + $comment->id = $items[1]; $comment->parentid = $items[2]; // Store in array - $key = $this->getOpenSlot($comments, (int) $comment->meta->postdate); + $key = $this->getOpenSlot($comments, (int) $comment->meta->postdate); $comments[$key] = $comment; } } @@ -233,7 +236,7 @@ class Filesystem extends AbstractData */ protected function _getExpiredPastes($batchsize) { - $pastes = array(); + $pastes = array(); $firstLevel = array_filter( scandir(self::$_dir), 'self::_isFirstLevelDir' @@ -241,7 +244,7 @@ class Filesystem extends AbstractData if (count($firstLevel) > 0) { // try at most 10 times the $batchsize pastes before giving up for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) { - $firstKey = array_rand($firstLevel); + $firstKey = array_rand($firstLevel); $secondLevel = array_filter( scandir(self::$_dir . $firstLevel[$firstKey]), 'self::_isSecondLevelDir' @@ -254,7 +257,7 @@ class Filesystem extends AbstractData } $secondKey = array_rand($secondLevel); - $path = self::$_dir . $firstLevel[$firstKey] . + $path = self::$_dir . $firstLevel[$firstKey] . DIRECTORY_SEPARATOR . $secondLevel[$secondKey]; if (!is_dir($path)) { continue; @@ -267,7 +270,7 @@ class Filesystem extends AbstractData continue; } $thirdKey = array_rand($thirdLevel); - $pasteid = $thirdLevel[$thirdKey]; + $pasteid = $thirdLevel[$thirdKey]; if (in_array($pasteid, $pastes)) { continue; } diff --git a/lib/Filter.php b/lib/Filter.php index 568f124..51106c6 100644 --- a/lib/Filter.php +++ b/lib/Filter.php @@ -77,7 +77,7 @@ class Filter public static function formatHumanReadableSize($size) { $iec = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'); - $i = 0; + $i = 0; while (($size / 1024) >= 1) { $size = $size / 1024; $i++; diff --git a/lib/I18n.php b/lib/I18n.php index 4a4fc3a..8b2ecad 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -114,8 +114,8 @@ class I18n $args = func_get_args(); if (is_array(self::$_translations[$messageId])) { $number = (int) $args[1]; - $key = self::_getPluralForm($number); - $max = count(self::$_translations[$messageId]) - 1; + $key = self::_getPluralForm($number); + $max = count(self::$_translations[$messageId]) - 1; if ($key > $max) { $key = $max; } @@ -143,7 +143,7 @@ class I18n // check if the lang cookie was set and that language exists if (array_key_exists('lang', $_COOKIE) && in_array($_COOKIE['lang'], $availableLanguages)) { - $match = $availableLanguages[array_search($_COOKIE['lang'], $availableLanguages)]; + $match = $_COOKIE['lang']; } // find a translation file matching the browsers language preferences else { @@ -153,7 +153,7 @@ class I18n } // load translations - self::$_language = $match; + self::$_language = $match; self::$_translations = ($match == 'en') ? array() : json_decode( file_get_contents(self::_getPath($match . '.json')), true @@ -319,7 +319,7 @@ class I18n protected static function _getMatchingLanguage($acceptedLanguages, $availableLanguages) { $matches = array(); - $any = false; + $any = false; foreach ($acceptedLanguages as $acceptedQuality => $acceptedValues) { $acceptedQuality = floatval($acceptedQuality); if ($acceptedQuality === 0.0) { @@ -372,7 +372,7 @@ class I18n { $a = explode('-', $a); $b = explode('-', $b); - for ($i=0, $n = min(count($a), count($b)); $i < $n; ++$i) { + for ($i = 0, $n = min(count($a), count($b)); $i < $n; ++$i) { if ($a[$i] !== $b[$i]) { break; } diff --git a/lib/Json.php b/lib/Json.php new file mode 100644 index 0000000..023304d --- /dev/null +++ b/lib/Json.php @@ -0,0 +1,48 @@ +_conf = $configuration; - $this->_store = $storage; - $this->_data = new stdClass; + $this->_conf = $configuration; + $this->_store = $storage; + $this->_data = new stdClass; $this->_data->meta = new stdClass; } diff --git a/lib/Model/Comment.php b/lib/Model/Comment.php index 7ea08e5..29dcfb8 100644 --- a/lib/Model/Comment.php +++ b/lib/Model/Comment.php @@ -10,11 +10,12 @@ * @version 0.22 */ -namespace PrivateBin\model; +namespace PrivateBin\Model; use PrivateBin\Sjcl; use PrivateBin\Persistence\TrafficLimiter; use PrivateBin\Vizhash16x16; +use Identicon\Identicon; use Exception; /** @@ -132,7 +133,7 @@ class Comment extends AbstractModel */ public function setPaste(Paste $paste) { - $this->_paste = $paste; + $this->_paste = $paste; $this->_data->meta->pasteid = $paste->getId(); } @@ -192,17 +193,26 @@ class Comment extends AbstractModel } $this->_data->meta->nickname = $nickname; - if ($this->_conf->getKey('vizhash')) { - // Generation of the anonymous avatar (Vizhash): - // If a nickname is provided, we generate a Vizhash. - // (We assume that if the user did not enter a nickname, he/she wants - // to be anonymous and we will not generate the vizhash.) - $vh = new Vizhash16x16(); - $pngdata = $vh->generate(TrafficLimiter::getIp()); - if ($pngdata != '') { - $this->_data->meta->vizhash = 'data:image/png;base64,' . base64_encode($pngdata); + // If a nickname is provided, we generate an icon based on a SHA512 HMAC + // of the users IP. (We assume that if the user did not enter a nickname, + // the user wants to be anonymous and we will not generate an icon.) + $icon = $this->_conf->getKey('icon'); + if ($icon != 'none') { + $pngdata = ''; + $hmac = TrafficLimiter::getHash(); + if ($icon == 'identicon') { + $identicon = new Identicon(); + $pngdata = $identicon->getImageDataUri($hmac, 16); + } elseif ($icon == 'vizhash') { + $vh = new Vizhash16x16(); + $pngdata = 'data:image/png;base64,' . base64_encode( + $vh->generate($hmac) + ); + } + if ($pngdata != '') { + $this->_data->meta->vizhash = $pngdata; } - // Once the avatar is generated, we do not keep the IP address, nor its hash. } + // Once the icon is generated, we do not keep the IP address hash. } } diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php index 8032667..ce401ca 100644 --- a/lib/Model/Paste.php +++ b/lib/Model/Paste.php @@ -62,11 +62,11 @@ class Paste extends AbstractModel if (!property_exists($data->meta, 'salt')) { $data->meta->salt = ServerSalt::get(); } - $data->comments = array_values($this->getComments()); - $data->comment_count = count($data->comments); + $data->comments = array_values($this->getComments()); + $data->comment_count = count($data->comments); $data->comment_offset = 0; - $data->{'@context'} = 'js/paste.jsonld'; - $this->_data = $data; + $data->{'@context'} = 'js/paste.jsonld'; + $this->_data = $data; return $this->_data; } @@ -85,7 +85,7 @@ class Paste extends AbstractModel } $this->_data->meta->postdate = time(); - $this->_data->meta->salt = serversalt::generate(); + $this->_data->meta->salt = serversalt::generate(); // store paste if ( @@ -247,7 +247,7 @@ class Paste extends AbstractModel throw new Exception('Invalid data.', 73); } $this->_data->meta->burnafterreading = true; - $this->_data->meta->opendiscussion = false; + $this->_data->meta->opendiscussion = false; } } @@ -296,7 +296,7 @@ class Paste extends AbstractModel * * @access public * @throws Exception - * @return boolean + * @return bool */ public function isBurnafterreading() { @@ -313,7 +313,7 @@ class Paste extends AbstractModel * * @access public * @throws Exception - * @return boolean + * @return bool */ public function isOpendiscussion() { diff --git a/lib/Persistence/AbstractPersistence.php b/lib/Persistence/AbstractPersistence.php index a888089..ff90885 100644 --- a/lib/Persistence/AbstractPersistence.php +++ b/lib/Persistence/AbstractPersistence.php @@ -119,7 +119,7 @@ abstract class AbstractPersistence protected static function _store($filename, $data) { self::_initialize(); - $file = self::$_path . DIRECTORY_SEPARATOR . $filename; + $file = self::$_path . DIRECTORY_SEPARATOR . $filename; $writtenBytes = @file_put_contents($file, $data, LOCK_EX); if ($writtenBytes === false || $writtenBytes < strlen($data)) { throw new Exception('unable to write to file ' . $file, 13); diff --git a/lib/Persistence/PurgeLimiter.php b/lib/Persistence/PurgeLimiter.php index e52d43c..1468a68 100644 --- a/lib/Persistence/PurgeLimiter.php +++ b/lib/Persistence/PurgeLimiter.php @@ -72,8 +72,8 @@ class PurgeLimiter extends AbstractPersistence return true; } - $file = 'purge_limiter.php'; - $now = time(); + $file = 'purge_limiter.php'; + $now = time(); $content = ' $time) { if ($time + self::$_limit < $now) { unset($tl[$key]); } } - if (array_key_exists($ip, $tl) && ($tl[$ip] + self::$_limit >= $now)) { + // this hash is used as an array key, hence a shorter hash is used + $hash = self::getHash('sha256'); + if (array_key_exists($hash, $tl) && ($tl[$hash] + self::$_limit >= $now)) { $result = false; } else { - $tl[$ip] = time(); - $result = true; + $tl[$hash] = time(); + $result = true; } self::_store( $file, diff --git a/lib/PrivateBin.php b/lib/PrivateBin.php index 5985bba..5d1bfd6 100644 --- a/lib/PrivateBin.php +++ b/lib/PrivateBin.php @@ -168,14 +168,14 @@ class PrivateBin file_put_contents( PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess', 'Allow from none' . PHP_EOL . - 'Deny from all'. PHP_EOL, + 'Deny from all' . PHP_EOL, LOCK_EX ); } } - $this->_conf = new Configuration; - $this->_model = new Model($this->_conf); + $this->_conf = new Configuration; + $this->_model = new Model($this->_conf); $this->_request = new Request; $this->_urlBase = array_key_exists('REQUEST_URI', $_SERVER) ? htmlspecialchars($_SERVER['REQUEST_URI']) : '/'; @@ -223,8 +223,8 @@ class PrivateBin ); } - $data = $this->_request->getParam('data'); - $attachment = $this->_request->getParam('attachment'); + $data = $this->_request->getParam('data'); + $attachment = $this->_request->getParam('attachment'); $attachmentname = $this->_request->getParam('attachmentname'); // Ensure content is not too big. @@ -247,7 +247,7 @@ class PrivateBin } // The user posts a comment. - $pasteid = $this->_request->getParam('pasteid'); + $pasteid = $this->_request->getParam('pasteid'); $parentid = $this->_request->getParam('parentid'); if (!empty($pasteid) && !empty($parentid)) { $paste = $this->_model->getPaste($pasteid); @@ -365,7 +365,7 @@ class PrivateBin try { $paste = $this->_model->getPaste($dataid); if ($paste->exists()) { - $data = $paste->get(); + $data = $paste->get(); $this->_doesExpire = property_exists($data, 'meta') && property_exists($data->meta, 'expire_date'); if (property_exists($data->meta, 'salt')) { unset($data->meta->salt); @@ -407,7 +407,7 @@ class PrivateBin // 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 @@ -462,7 +462,7 @@ class PrivateBin $type = ''; } $content = '{}'; - $file = PUBLIC_PATH . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . $type . '.jsonld'; + $file = PUBLIC_PATH . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . $type . '.jsonld'; if (is_readable($file)) { $content = str_replace( '?jsonld=', @@ -492,7 +492,7 @@ class PrivateBin if ($status) { $result['message'] = I18n::_($message); } else { - $result['id'] = $message; + $result['id'] = $message; $result['url'] = $this->_urlBase . '?' . $message; } $result += $other; diff --git a/lib/Request.php b/lib/Request.php index 90e1744..b589315 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -16,7 +16,7 @@ namespace PrivateBin; * Request * * parses request parameters and provides helper functions for routing -*/ + */ class Request { /** @@ -184,7 +184,7 @@ class Request private function _detectJsonRequest() { $hasAcceptHeader = array_key_exists('HTTP_ACCEPT', $_SERVER); - $acceptHeader = $hasAcceptHeader ? $_SERVER['HTTP_ACCEPT'] : ''; + $acceptHeader = $hasAcceptHeader ? $_SERVER['HTTP_ACCEPT'] : ''; // simple cases if ( diff --git a/lib/View.php b/lib/View.php index 1876ac4..c5f0ea2 100644 --- a/lib/View.php +++ b/lib/View.php @@ -11,6 +11,7 @@ */ namespace PrivateBin; + use Exception; /** diff --git a/lib/Vizhash16x16.php b/lib/Vizhash16x16.php index 53f0346..f88dfe9 100644 --- a/lib/Vizhash16x16.php +++ b/lib/Vizhash16x16.php @@ -13,8 +13,6 @@ namespace PrivateBin; -use PrivateBin\Persistence\ServerSalt; - /** * Vizhash16x16 * @@ -60,14 +58,6 @@ class Vizhash16x16 */ private $height; - /** - * salt used when generating the image - * - * @access private - * @var string - */ - private $salt; - /** * constructor * @@ -78,12 +68,13 @@ class Vizhash16x16 { $this->width = 16; $this->height = 16; - $this->salt = ServerSalt::get(); } /** * Generate a 16x16 png corresponding to $text. * + * The given text should to be 128 to 150 characters long + * * @access public * @param string $text * @return string PNG data. Or empty string if GD is not available. @@ -94,44 +85,35 @@ class Vizhash16x16 return ''; } - // We hash the input string. - $hash=hash('sha1', $text.$this->salt).hash('md5', $text.$this->salt); - $hash=$hash.strrev($hash); # more data to make graphics - $hashlen=strlen($hash); + $textlen = strlen($text); // We convert the hash into an array of integers. - $this->VALUES=array(); - for ($i=0; $i<$hashlen; $i=$i+2) { - array_push($this->VALUES, hexdec(substr($hash, $i, 2))); + $this->VALUES = array(); + for ($i = 0; $i < $textlen; $i = $i + 2) { + array_push($this->VALUES, hexdec(substr($text, $i, 2))); } - $this->VALUES_INDEX=0; // to walk the array. + $this->VALUES_INDEX = 0; // to walk the array. // Then use these integers to drive the creation of an image. $image = imagecreatetruecolor($this->width, $this->height); - $r0 = $this->getInt(); - $r=$r0; - $g0 = $this->getInt(); - $g=$g0; - $b0 = $this->getInt(); - $b=$b0; + $r = $r0 = $this->getInt(); + $g = $g0 = $this->getInt(); + $b = $b0 = $this->getInt(); // First, create an image with a specific gradient background. - $op='v'; - if (($this->getInt()%2)==0) { - $op='h'; + $op = 'v'; + if (($this->getInt() % 2) == 0) { + $op = 'h'; }; $image = $this->degrade($image, $op, array($r0, $g0, $b0), array(0, 0, 0)); - for ($i=0; $i<7; $i=$i+1) { - $action=$this->getInt(); - $color = imagecolorallocate($image, $r, $g, $b); - $r = ($r0 + $this->getInt()/25)%256; - $g = ($g0 + $this->getInt()/25)%256; - $b = ($b0 + $this->getInt()/25)%256; - $r0=$r; - $g0=$g; - $b0=$b; + for ($i = 0; $i < 7; ++$i) { + $action = $this->getInt(); + $color = imagecolorallocate($image, $r, $g, $b); + $r = $r0 = ($r0 + $this->getInt() / 25) % 256; + $g = $g0 = ($g0 + $this->getInt() / 25) % 256; + $b = $b0 = ($b0 + $this->getInt() / 25) % 256; $this->drawshape($image, $action, $color); } @@ -154,8 +136,8 @@ class Vizhash16x16 */ private function getInt() { - $v= $this->VALUES[$this->VALUES_INDEX]; - $this->VALUES_INDEX++; + $v = $this->VALUES[$this->VALUES_INDEX]; + ++$this->VALUES_INDEX; $this->VALUES_INDEX %= count($this->VALUES); // Warp around the array return $v; } @@ -168,7 +150,7 @@ class Vizhash16x16 */ private function getX() { - return $this->width*$this->getInt()/256; + return $this->width * $this->getInt() / 256; } /** @@ -179,7 +161,7 @@ class Vizhash16x16 */ private function getY() { - return $this->height*$this->getInt()/256; + return $this->height * $this->getInt() / 256; } /** @@ -197,23 +179,23 @@ class Vizhash16x16 */ private function degrade($img, $direction, $color1, $color2) { - if ($direction=='h') { - $size = imagesx($img); + if ($direction == 'h') { + $size = imagesx($img); $sizeinv = imagesy($img); } else { - $size = imagesy($img); + $size = imagesy($img); $sizeinv = imagesx($img); } $diffs = array( - (($color2[0]-$color1[0])/$size), - (($color2[1]-$color1[1])/$size), - (($color2[2]-$color1[2])/$size) - ); - for ($i=0;$i<$size;$i++) { - $r = $color1[0]+($diffs[0]*$i); - $g = $color1[1]+($diffs[1]*$i); - $b = $color1[2]+($diffs[2]*$i); - if ($direction=='h') { + (($color2[0] - $color1[0]) / $size), + (($color2[1] - $color1[1]) / $size), + (($color2[2] - $color1[2]) / $size) + ); + for ($i = 0; $i < $size; ++$i) { + $r = $color1[0] + ($diffs[0] * $i); + $g = $color1[1] + ($diffs[1] * $i); + $b = $color1[2] + ($diffs[2] * $i); + 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)); @@ -233,7 +215,7 @@ class Vizhash16x16 */ private function drawshape($image, $action, $color) { - switch ($action%7) { + switch ($action % 7) { case 0: ImageFilledRectangle($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(), $color); break; @@ -242,11 +224,12 @@ class Vizhash16x16 ImageFilledEllipse($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(), $color); break; case 3: - $points = array($this->getX(), $this->getY(), $this->getX(), $this->getY(), $this->getX(), $this->getY(),$this->getX(), $this->getY()); + $points = array($this->getX(), $this->getY(), $this->getX(), $this->getY(), $this->getX(), $this->getY(), $this->getX(), $this->getY()); ImageFilledPolygon($image, $points, 4, $color); break; default: - $start=$this->getInt()*360/256; $end=$start+$this->getInt()*180/256; + $start = $this->getInt() * 360 / 256; + $end = $start + $this->getInt() * 180 / 256; ImageFilledArc($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(), $start, $end, $color, IMG_ARC_PIE); } } diff --git a/privatebin b/privatebin deleted file mode 120000 index 127be32..0000000 --- a/privatebin +++ /dev/null @@ -1 +0,0 @@ -privatebin \ No newline at end of file diff --git a/tpl/bootstrap-compact.php b/tpl/bootstrap-compact.php index 161f0e8..560eda3 100644 --- a/tpl/bootstrap-compact.php +++ b/tpl/bootstrap-compact.php @@ -72,19 +72,19 @@ endif; @@ -233,7 +233,7 @@ endif; ?>
  • @@ -245,7 +245,7 @@ endif; if (strlen($NOTICE)): ?> alert alert-danger"> - - - - -