Răsfoiți Sursa

Merge branch 'master' into prng, resolve merge conflicts

rugk 10 ani în urmă
părinte
comite
75cb771e4b
47 a modificat fișierele cu 1970 adăugiri și 346 ștergeri
  1. 1 0
      .gitattributes
  2. 4 7
      .gitignore
  3. 23 0
      .php_cs
  4. 4 2
      CHANGELOG.md
  5. 25 2
      LICENSE.md
  6. 10 14
      cfg/conf.ini.sample
  7. 2 1
      composer.json
  8. 858 0
      composer.lock
  9. 8 4
      css/bootstrap/privatebin.css
  10. 52 9
      js/privatebin.js
  11. 3 3
      lib/Configuration.php
  12. 31 31
      lib/Data/Database.php
  13. 15 12
      lib/Data/Filesystem.php
  14. 1 1
      lib/Filter.php
  15. 6 6
      lib/I18n.php
  16. 48 0
      lib/Json.php
  17. 3 3
      lib/Model/AbstractModel.php
  18. 21 11
      lib/Model/Comment.php
  19. 8 8
      lib/Model/Paste.php
  20. 1 1
      lib/Persistence/AbstractPersistence.php
  21. 2 2
      lib/Persistence/PurgeLimiter.php
  22. 11 10
      lib/Persistence/TrafficLimiter.php
  23. 10 10
      lib/PrivateBin.php
  24. 2 2
      lib/Request.php
  25. 1 0
      lib/View.php
  26. 39 56
      lib/Vizhash16x16.php
  27. 0 1
      privatebin
  28. 16 16
      tpl/bootstrap-compact.php
  29. 16 16
      tpl/bootstrap-dark-page.php
  30. 18 18
      tpl/bootstrap-dark.php
  31. 16 16
      tpl/bootstrap-page.php
  32. 16 16
      tpl/bootstrap.php
  33. 3 3
      tpl/page.php
  34. 30 1
      tst/Data/FilesystemTest.php
  35. 82 4
      tst/ModelTest.php
  36. 3 3
      tst/Vizhash16x16Test.php
  37. 7 1
      vendor/composer/autoload_classmap.php
  38. 0 10
      vendor/composer/autoload_files.php
  39. 1 0
      vendor/composer/autoload_namespaces.php
  40. 0 18
      vendor/composer/autoload_real.php
  41. 18 5
      vendor/composer/autoload_static.php
  42. 25 23
      vendor/composer/installed.json
  43. 241 0
      vendor/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php
  44. 76 0
      vendor/yzalis/identicon/src/Identicon/Generator/GdGenerator.php
  45. 33 0
      vendor/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php
  46. 80 0
      vendor/yzalis/identicon/src/Identicon/Generator/ImageMagickGenerator.php
  47. 100 0
      vendor/yzalis/identicon/src/Identicon/Identicon.php

+ 1 - 0
.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

+ 4 - 7
.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

+ 23 - 0
.php_cs

@@ -0,0 +1,23 @@
+<?php
+/**
+ * Configuration file for PHP Coding Standards Fixer (php-cs-fixer).
+ *
+ * On GitHub: https://github.com/FriendsOfPhp/php-cs-fixer
+ * More information: http://cs.sensiolabs.org/
+ */
+
+$finder = Symfony\CS\Finder\DefaultFinder::create()
+    ->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)
+;

+ 4 - 2
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

+ 25 - 2
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 <benjamin@yzalis.com>
+
+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_

+ 10 - 14
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

+ 2 - 1
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",

+ 858 - 0
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": []
+}

+ 8 - 4
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;

+ 52 - 9
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() {
                 '<div class="reply">' +
                 '<input type="text" id="nickname" class="form-control" title="' + hint + '" placeholder="' + hint + '" />' +
                 '<textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea>' +
-                '<br /><button id="replybutton" class="btn btn-default btn-sm">' + i18n._('Post comment') + '</button>' +
-                '<div id="replystatus"> </div>' +
-                '</div>'
+                '<br /><div id="replystatus"></div><button id="replybutton" class="btn btn-default btn-sm">' +
+                i18n._('Post comment') + '</button></div>'
             );
             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('<pre>' + paste + '</pre>');
             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 = '<img src="img/busy.gif" style="width:16px;height:9px;margin:0 4px 0 0;" />';
                 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();

+ 3 - 3
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);

+ 31 - 31
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, " .

+ 15 - 12
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;
                 }

+ 1 - 1
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++;

+ 6 - 6
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;
             }

+ 48 - 0
lib/Json.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * 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
+ */
+
+namespace PrivateBin;
+
+use Exception;
+
+/**
+ * Json
+ *
+ * Provides JSON functions in an object oriented way.
+ */
+class Json
+{
+    /**
+     * Returns a string containing the JSON representation of the given input
+     *
+     * @access public
+     * @static
+     * @param  mixed $input
+     * @throws Exception
+     * @return string
+     */
+    public static function encode($input)
+    {
+        $jsonString = json_encode($input);
+        $errorCode  = json_last_error();
+        if ($errorCode === JSON_ERROR_NONE) {
+            return $jsonString;
+        }
+
+        $message = 'A JSON error occurred';
+        if (function_exists('json_last_error_msg')) {
+            $message .= ': ' . json_last_error_msg();
+        }
+        $message .= ' (' . $errorCode . ')';
+        throw new Exception($message, 90);
+    }
+}

+ 3 - 3
lib/Model/AbstractModel.php

@@ -67,9 +67,9 @@ abstract class AbstractModel
      */
     public function __construct(Configuration $configuration, AbstractData $storage)
     {
-        $this->_conf = $configuration;
-        $this->_store = $storage;
-        $this->_data = new stdClass;
+        $this->_conf       = $configuration;
+        $this->_store      = $storage;
+        $this->_data       = new stdClass;
         $this->_data->meta = new stdClass;
     }
 

+ 21 - 11
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 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 = 'data:image/png;base64,' . base64_encode($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.
     }
 }

+ 8 - 8
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()
     {

+ 1 - 1
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);

+ 2 - 2
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 = '<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $now . ';' . PHP_EOL;
         if (!self::_exists($file)) {
             self::_store($file, $content);

+ 11 - 10
lib/Persistence/TrafficLimiter.php

@@ -73,15 +73,16 @@ class TrafficLimiter extends AbstractPersistence
     }
 
     /**
-     * get the current visitors IP address
+     * get a HMAC of the current visitors IP address
      *
      * @access public
      * @static
+     * @param  string $algo
      * @return string
      */
-    public static function getIp()
+    public static function getHash($algo = 'sha512')
     {
-        return $_SERVER[self::$_ipKey];
+        return hash_hmac($algo, $_SERVER[self::$_ipKey], ServerSalt::get());
     }
 
     /**
@@ -101,8 +102,6 @@ class TrafficLimiter extends AbstractPersistence
             return true;
         }
 
-        $ip = hash_hmac('sha256', self::getIp(), ServerSalt::get());
-
         $file = 'traffic_limiter.php';
         if (!self::_exists($file)) {
             self::_store(
@@ -115,20 +114,22 @@ class TrafficLimiter extends AbstractPersistence
         $path = self::getPath($file);
         require $path;
         $now = time();
-        $tl = $GLOBALS['traffic_limiter'];
+        $tl  = $GLOBALS['traffic_limiter'];
 
-        // purge file of expired IPs to keep it small
+        // purge file of expired hashes to keep it small
         foreach ($tl as $key => $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,

+ 10 - 10
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;

+ 2 - 2
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 (

+ 1 - 0
lib/View.php

@@ -11,6 +11,7 @@
  */
 
 namespace PrivateBin;
+
 use Exception;
 
 /**

+ 39 - 56
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);
         }
     }

+ 0 - 1
privatebin

@@ -1 +0,0 @@
-privatebin

+ 16 - 16
tpl/bootstrap-compact.php

@@ -72,19 +72,19 @@ endif;
 					<ul class="nav navbar-nav">
 						<li>
 							<button id="sendbutton" type="button" class="hidden btn btn-default navbar-btn">
-								<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'); ?>
+								<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL; ?>
 							</button>
 <?php
 if ($EXPIRECLONE):
 ?>
 							<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
-								<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'); ?>
+								<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'), PHP_EOL; ?>
 							</button>
 <?php
 endif;
 ?>
 							<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn">
-								<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'); ?>
+								<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'), PHP_EOL; ?>
 							</button>
 						</li>
 						<li class="dropdown">
@@ -108,7 +108,7 @@ foreach ($EXPIRE as $key => $value):
 ?>
 								<li>
 									<a href="#" data-expiration="<?php echo $key; ?>">
-										<?php echo $value; ?>
+										<?php echo $value, PHP_EOL; ?>
 									</a>
 								</li>
 <?php
@@ -121,12 +121,12 @@ endforeach;
 							<ul class="dropdown-menu">
 								<li id="burnafterreadingoption" class="checkbox hidden">
 									<label>
-										<input type="checkbox" id="burnafterreading" name="burnafterreading" <?php
+										<input type="checkbox" id="burnafterreading" name="burnafterreading"<?php
 if ($BURNAFTERREADINGSELECTED):
 ?> checked="checked"<?php
 endif;
 ?> />
-										<?php echo I18n::_('Burn after reading'); ?>
+										<?php echo I18n::_('Burn after reading'), PHP_EOL; ?>
 									</label>
 								</li>
 <?php
@@ -134,12 +134,12 @@ if ($DISCUSSION):
 ?>
 								<li id="opendisc" class="checkbox hidden">
 									<label>
-										<input type="checkbox" id="opendiscussion" name="opendiscussion" <?php
+										<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
     if ($OPENDISCUSSION):
 ?> checked="checked"<?php
     endif;
 ?> />
-										<?php echo I18n::_('Open discussion'); ?>
+										<?php echo I18n::_('Open discussion'), PHP_EOL; ?>
 									</label>
 								</li>
 <?php
@@ -156,7 +156,7 @@ foreach ($FORMATTER as $key => $value):
 ?>
 								<li>
 									<a href="#" data-format="<?php echo $key; ?>">
-										<?php echo $value; ?>
+										<?php echo $value, PHP_EOL; ?>
 									</a>
 								</li>
 <?php
@@ -199,7 +199,7 @@ if ($FILEUPLOAD):
 								</li>
 								<li>
 									<a id="fileremovebutton"  href="#">
-										<?php echo I18n::_('Remove attachment'); ?>
+										<?php echo I18n::_('Remove attachment'), PHP_EOL; ?>
 									</a>
 								</li>
 							</ul>
@@ -233,7 +233,7 @@ endif;
 ?>
 						<li>
 							<button id="newbutton" type="button" class="reloadlink hidden btn btn-default navbar-btn">
-								<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'); ?>
+								<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'), PHP_EOL; ?>
 							</button>
 						</li>
 					</ul>
@@ -245,7 +245,7 @@ endif;
 if (strlen($NOTICE)):
 ?>
 			<div role="alert" class="alert alert-info">
-				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE); ?>
+				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -264,7 +264,7 @@ endif;
 if (strlen($STATUS)):
 ?>
 			<div id="status" role="alert" class="alert alert-success">
-				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS); ?>
+				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -276,7 +276,7 @@ endif;
 ?>alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo htmlspecialchars($ERROR); ?></div>
 			<noscript><div id="noscript" role="alert" class="nonworking alert alert-warning"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <?php echo I18n::_('Javascript is required for PrivateBin to work.<br />Sorry for the inconvenience.'); ?></div></noscript>
 			<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo I18n::_('PrivateBin requires a modern browser to work.'); ?></div>
-			<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'); ?>
+			<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.opera.com/">Opera</a>,
 				<a href="https://www.google.com/chrome">Chrome</a>,
@@ -290,7 +290,7 @@ endif;
 if (strlen($URLSHORTENER)):
 ?>
 					<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-primary">
-						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'); ?>
+						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
 					</button>
 <?php
 endif;
@@ -323,7 +323,7 @@ endif;
 				<h4 class="col-md-5 col-xs-8"><?php echo I18n::_('PrivateBin'); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
 				<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
 				<p id="aboutbox" class="col-md-6 col-xs-12">
-					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'); ?>
+					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'), PHP_EOL; ?>
 				</p>
 			</div>
 		</footer>

+ 16 - 16
tpl/bootstrap-dark-page.php

@@ -71,19 +71,19 @@ endif;
 				<ul class="nav navbar-nav">
 					<li>
 						<button id="sendbutton" type="button" class="hidden btn btn-warning navbar-btn">
-							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'); ?>
+							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL; ?>
 						</button>
 <?php
 if ($EXPIRECLONE):
 ?>
 						<button id="clonebutton" type="button" class="hidden btn btn-warning navbar-btn">
-							<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'); ?>
+							<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'), PHP_EOL; ?>
 						</button>
 <?php
 endif;
 ?>
 						<button id="rawtextbutton" type="button" class="hidden btn btn-warning navbar-btn">
-							<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'); ?>
+							<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'), PHP_EOL; ?>
 						</button>
 					</li>
 					<li class="dropdown">
@@ -107,7 +107,7 @@ foreach ($EXPIRE as $key => $value):
 ?>
 							<li>
 								<a href="#" data-expiration="<?php echo $key; ?>">
-									<?php echo $value; ?>
+									<?php echo $value, PHP_EOL; ?>
 								</a>
 							</li>
 <?php
@@ -118,12 +118,12 @@ endforeach;
 					<li>
 						<div id="burnafterreadingoption" class="navbar-text checkbox hidden">
 							<label>
-								<input type="checkbox" id="burnafterreading" name="burnafterreading" <?php
+								<input type="checkbox" id="burnafterreading" name="burnafterreading"<?php
 if ($BURNAFTERREADINGSELECTED):
 ?> checked="checked"<?php
 endif;
 ?> />
-								<?php echo I18n::_('Burn after reading'); ?>
+								<?php echo I18n::_('Burn after reading'), PHP_EOL; ?>
 							</label>
 						</div>
 					</li>
@@ -133,12 +133,12 @@ if ($DISCUSSION):
 					<li>
 						<div id="opendisc" class="navbar-text checkbox hidden">
 							<label>
-								<input type="checkbox" id="opendiscussion" name="opendiscussion" <?php
+								<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
     if ($OPENDISCUSSION):
 ?> checked="checked"<?php
     endif;
 ?> />
-								<?php echo I18n::_('Open discussion'); ?>
+								<?php echo I18n::_('Open discussion'), PHP_EOL; ?>
 						 	</label>
 						</div>
 					</li>
@@ -165,7 +165,7 @@ if ($FILEUPLOAD):
 							</li>
 							<li>
 								<a id="fileremovebutton"  href="#">
-									<?php echo I18n::_('Remove attachment'); ?>
+									<?php echo I18n::_('Remove attachment'), PHP_EOL; ?>
 								</a>
 							</li>
 						</ul>
@@ -194,7 +194,7 @@ foreach ($FORMATTER as $key => $value):
 ?>
 							<li>
 								<a href="#" data-format="<?php echo $key; ?>">
-									<?php echo $value; ?>
+									<?php echo $value, PHP_EOL; ?>
 								</a>
 							</li>
 <?php
@@ -228,7 +228,7 @@ endif;
 ?>
 					<li>
 						<button id="newbutton" type="button" class="reloadlink hidden btn btn-warning navbar-btn">
-							<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'); ?>
+							<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'), PHP_EOL; ?>
 						</button>
 					</li>
 				</ul>
@@ -239,7 +239,7 @@ endif;
 if (strlen($NOTICE)):
 ?>
 			<div role="alert" class="alert alert-info">
-				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE); ?>
+				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -258,7 +258,7 @@ endif;
 if (strlen($STATUS)):
 ?>
 			<div id="status" role="alert" class="alert alert-success">
-				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS); ?>
+				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -270,7 +270,7 @@ endif;
 ?>alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo htmlspecialchars($ERROR); ?></div>
 			<noscript><div id="noscript" role="alert" class="nonworking alert alert-error"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <?php echo I18n::_('Javascript is required for PrivateBin to work.<br />Sorry for the inconvenience.'); ?></div></noscript>
 			<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo I18n::_('PrivateBin requires a modern browser to work.'); ?></div>
-			<div id="ienotice" role="alert" class="hidden alert alert-error"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'); ?>
+			<div id="ienotice" role="alert" class="hidden alert alert-error"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.opera.com/">Opera</a>,
 				<a href="https://www.google.com/chrome">Chrome</a>,
@@ -284,7 +284,7 @@ endif;
 if (strlen($URLSHORTENER)):
 ?>
 					<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-warning">
-						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'); ?>
+						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
 					</button>
 <?php
 endif;
@@ -317,7 +317,7 @@ endif;
 				<h4 class="col-md-5 col-xs-8"><?php echo I18n::_('PrivateBin'); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
 				<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
 				<p id="aboutbox" class="col-md-6 col-xs-12">
-					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'); ?>
+					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'), PHP_EOL; ?>
 				</p>
 			</div>
 		</footer>

+ 18 - 18
tpl/bootstrap-dark.php

@@ -71,19 +71,19 @@ endif;
 				<ul class="nav navbar-nav">
 					<li>
 						<button id="newbutton" type="button" class="reloadlink hidden btn btn-warning navbar-btn">
-							<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'); ?>
+							<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'), PHP_EOL; ?>
 						</button>
 <?php
 if ($EXPIRECLONE):
 ?>
 						<button id="clonebutton" type="button" class="hidden btn btn-warning navbar-btn">
-							<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'); ?>
+							<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'), PHP_EOL; ?>
 						</button>
 <?php
 endif;
 ?>
 						<button id="rawtextbutton" type="button" class="hidden btn btn-warning navbar-btn">
-							<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'); ?>
+							<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'), PHP_EOL; ?>
 						</button>
 					</li>
 					<li class="dropdown">
@@ -107,7 +107,7 @@ foreach ($EXPIRE as $key => $value):
 ?>
 							<li>
 								<a href="#" data-expiration="<?php echo $key; ?>">
-									<?php echo $value; ?>
+									<?php echo $value, PHP_EOL; ?>
 								</a>
 							</li>
 <?php
@@ -118,12 +118,12 @@ endforeach;
 					<li>
 						<div id="burnafterreadingoption" class="navbar-text checkbox hidden">
 							<label>
-								<input type="checkbox" id="burnafterreading" name="burnafterreading" <?php
+								<input type="checkbox" id="burnafterreading" name="burnafterreading"<?php
 if ($BURNAFTERREADINGSELECTED):
-?> checked="checked"<?php
+?> checked<?php
 endif;
 ?> />
-								<?php echo I18n::_('Burn after reading'); ?>
+								<?php echo I18n::_('Burn after reading'), PHP_EOL; ?>
 							</label>
 						</div>
 					</li>
@@ -133,12 +133,12 @@ if ($DISCUSSION):
 					<li>
 						<div id="opendisc" class="navbar-text checkbox hidden">
 							<label>
-								<input type="checkbox" id="opendiscussion" name="opendiscussion" <?php
+								<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
     if ($OPENDISCUSSION):
-?> checked="checked"<?php
+?> checked<?php
     endif;
 ?> />
-								<?php echo I18n::_('Open discussion'); ?>
+								<?php echo I18n::_('Open discussion'), PHP_EOL; ?>
 						 	</label>
 						</div>
 					</li>
@@ -165,7 +165,7 @@ if ($FILEUPLOAD):
 							</li>
 							<li>
 								<a id="fileremovebutton"  href="#">
-									<?php echo I18n::_('Remove attachment'); ?>
+									<?php echo I18n::_('Remove attachment'), PHP_EOL; ?>
 								</a>
 							</li>
 						</ul>
@@ -194,7 +194,7 @@ foreach ($FORMATTER as $key => $value):
 ?>
 							<li>
 								<a href="#" data-format="<?php echo $key; ?>">
-									<?php echo $value; ?>
+									<?php echo $value, PHP_EOL; ?>
 								</a>
 							</li>
 <?php
@@ -228,7 +228,7 @@ endif;
 ?>
 					<li>
 						<button id="sendbutton" type="button" class="hidden btn btn-warning navbar-btn">
-							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'); ?>
+							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL; ?>
 						</button>
 					</li>
 				</ul>
@@ -239,7 +239,7 @@ endif;
 if (strlen($NOTICE)):
 ?>
 			<div role="alert" class="alert alert-info">
-				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE); ?>
+				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -258,7 +258,7 @@ endif;
 if (strlen($STATUS)):
 ?>
 			<div id="status" role="alert" class="alert alert-success">
-				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS); ?>
+				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -270,7 +270,7 @@ endif;
 ?>alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo htmlspecialchars($ERROR); ?></div>
 			<noscript><div id="noscript" role="alert" class="nonworking alert alert-error"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <?php echo I18n::_('Javascript is required for PrivateBin to work.<br />Sorry for the inconvenience.'); ?></div></noscript>
 			<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo I18n::_('PrivateBin requires a modern browser to work.'); ?></div>
-			<div id="ienotice" role="alert" class="hidden alert alert-error"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'); ?>
+			<div id="ienotice" role="alert" class="hidden alert alert-error"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.opera.com/">Opera</a>,
 				<a href="https://www.google.com/chrome">Chrome</a>,
@@ -284,7 +284,7 @@ endif;
 if (strlen($URLSHORTENER)):
 ?>
 					<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-warning">
-						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'); ?>
+						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
 					</button>
 <?php
 endif;
@@ -317,7 +317,7 @@ endif;
 				<h4 class="col-md-5 col-xs-8"><?php echo I18n::_('PrivateBin'); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
 				<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
 				<p id="aboutbox" class="col-md-6 col-xs-12">
-					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'); ?>
+					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'), PHP_EOL; ?>
 				</p>
 			</div>
 		</footer>

+ 16 - 16
tpl/bootstrap-page.php

@@ -71,19 +71,19 @@ endif;
 				<ul class="nav navbar-nav">
 					<li>
 						<button id="sendbutton" type="button" class="hidden btn btn-primary navbar-btn">
-							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'); ?>
+							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL; ?>
 						</button>
 <?php
 if ($EXPIRECLONE):
 ?>
 						<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
-							<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'); ?>
+							<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'), PHP_EOL; ?>
 						</button>
 <?php
 endif;
 ?>
 						<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn">
-							<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'); ?>
+							<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'), PHP_EOL; ?>
 						</button>
 					</li>
 					<li class="dropdown">
@@ -107,7 +107,7 @@ foreach ($EXPIRE as $key => $value):
 ?>
 							<li>
 								<a href="#" data-expiration="<?php echo $key; ?>">
-									<?php echo $value; ?>
+									<?php echo $value, PHP_EOL; ?>
 								</a>
 							</li>
 <?php
@@ -118,12 +118,12 @@ endforeach;
 					<li>
 						<div id="burnafterreadingoption" class="navbar-text checkbox hidden">
 							<label>
-								<input type="checkbox" id="burnafterreading" name="burnafterreading" <?php
+								<input type="checkbox" id="burnafterreading" name="burnafterreading"<?php
 if ($BURNAFTERREADINGSELECTED):
 ?> checked="checked"<?php
 endif;
 ?> />
-								<?php echo I18n::_('Burn after reading'); ?>
+								<?php echo I18n::_('Burn after reading'), PHP_EOL; ?>
 							</label>
 						</div>
 					</li>
@@ -133,12 +133,12 @@ if ($DISCUSSION):
 					<li>
 						<div id="opendisc" class="navbar-text checkbox hidden">
 							<label>
-								<input type="checkbox" id="opendiscussion" name="opendiscussion" <?php
+								<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
     if ($OPENDISCUSSION):
 ?> checked="checked"<?php
     endif;
 ?> />
-								<?php echo I18n::_('Open discussion'); ?>
+								<?php echo I18n::_('Open discussion'), PHP_EOL; ?>
 						 	</label>
 						</div>
 					</li>
@@ -165,7 +165,7 @@ if ($FILEUPLOAD):
 							</li>
 							<li>
 								<a id="fileremovebutton"  href="#">
-									<?php echo I18n::_('Remove attachment'); ?>
+									<?php echo I18n::_('Remove attachment'), PHP_EOL; ?>
 								</a>
 							</li>
 						</ul>
@@ -194,7 +194,7 @@ foreach ($FORMATTER as $key => $value):
 ?>
 							<li>
 								<a href="#" data-format="<?php echo $key; ?>">
-									<?php echo $value; ?>
+									<?php echo $value, PHP_EOL; ?>
 								</a>
 							</li>
 <?php
@@ -228,7 +228,7 @@ endif;
 ?>
 					<li>
 						<button id="newbutton" type="button" class="reloadlink hidden btn btn-default navbar-btn">
-							<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'); ?>
+							<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'), PHP_EOL; ?>
 						</button>
 					</li>
 				</ul>
@@ -239,7 +239,7 @@ endif;
 if (strlen($NOTICE)):
 ?>
 			<div role="alert" class="alert alert-info">
-				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE); ?>
+				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -258,7 +258,7 @@ endif;
 if (strlen($STATUS)):
 ?>
 			<div id="status" role="alert" class="alert alert-success">
-				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS); ?>
+				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -270,7 +270,7 @@ endif;
 ?>alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo htmlspecialchars($ERROR); ?></div>
 			<noscript><div id="noscript" role="alert" class="nonworking alert alert-warning"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <?php echo I18n::_('Javascript is required for PrivateBin to work.<br />Sorry for the inconvenience.'); ?></div></noscript>
 			<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo I18n::_('PrivateBin requires a modern browser to work.'); ?></div>
-			<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'); ?>
+			<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.opera.com/">Opera</a>,
 				<a href="https://www.google.com/chrome">Chrome</a>,
@@ -284,7 +284,7 @@ endif;
 if (strlen($URLSHORTENER)):
 ?>
 					<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-primary">
-						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'); ?>
+						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
 					</button>
 <?php
 endif;
@@ -317,7 +317,7 @@ endif;
 				<h4 class="col-md-5 col-xs-8"><?php echo I18n::_('PrivateBin'); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
 				<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
 				<p id="aboutbox" class="col-md-6 col-xs-12">
-					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'); ?>
+					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'), PHP_EOL; ?>
 				</p>
 			</div>
 		</footer>

+ 16 - 16
tpl/bootstrap.php

@@ -71,19 +71,19 @@ endif;
 				<ul class="nav navbar-nav">
 					<li>
 						<button id="newbutton" type="button" class="reloadlink hidden btn btn-default navbar-btn">
-							<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'); ?>
+							<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'), PHP_EOL; ?>
 						</button>
 <?php
 if ($EXPIRECLONE):
 ?>
 						<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
-							<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'); ?>
+							<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'), PHP_EOL; ?>
 						</button>
 <?php
 endif;
 ?>
 						<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn">
-							<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'); ?>
+							<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'), PHP_EOL; ?>
 						</button>
 					</li>
 					<li class="dropdown">
@@ -107,7 +107,7 @@ foreach ($EXPIRE as $key => $value):
 ?>
 							<li>
 								<a href="#" data-expiration="<?php echo $key; ?>">
-									<?php echo $value; ?>
+									<?php echo $value, PHP_EOL; ?>
 								</a>
 							</li>
 <?php
@@ -118,12 +118,12 @@ endforeach;
 					<li>
 						<div id="burnafterreadingoption" class="navbar-text checkbox hidden">
 							<label>
-								<input type="checkbox" id="burnafterreading" name="burnafterreading" <?php
+								<input type="checkbox" id="burnafterreading" name="burnafterreading"<?php
 if ($BURNAFTERREADINGSELECTED):
 ?> checked="checked"<?php
 endif;
 ?> />
-								<?php echo I18n::_('Burn after reading'); ?>
+								<?php echo I18n::_('Burn after reading'), PHP_EOL; ?>
 							</label>
 						</div>
 					</li>
@@ -133,12 +133,12 @@ if ($DISCUSSION):
 					<li>
 						<div id="opendisc" class="navbar-text checkbox hidden">
 							<label>
-								<input type="checkbox" id="opendiscussion" name="opendiscussion" <?php
+								<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
     if ($OPENDISCUSSION):
 ?> checked="checked"<?php
     endif;
 ?> />
-								<?php echo I18n::_('Open discussion'); ?>
+								<?php echo I18n::_('Open discussion'), PHP_EOL; ?>
 						 	</label>
 						</div>
 					</li>
@@ -165,7 +165,7 @@ if ($FILEUPLOAD):
 							</li>
 							<li>
 								<a id="fileremovebutton"  href="#">
-									<?php echo I18n::_('Remove attachment'); ?>
+									<?php echo I18n::_('Remove attachment'), PHP_EOL; ?>
 								</a>
 							</li>
 						</ul>
@@ -194,7 +194,7 @@ foreach ($FORMATTER as $key => $value):
 ?>
 							<li>
 								<a href="#" data-format="<?php echo $key; ?>">
-									<?php echo $value; ?>
+									<?php echo $value, PHP_EOL; ?>
 								</a>
 							</li>
 <?php
@@ -228,7 +228,7 @@ endif;
 ?>
 					<li>
 						<button id="sendbutton" type="button" class="hidden btn btn-primary navbar-btn">
-							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'); ?>
+							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL; ?>
 						</button>
 					</li>
 				</ul>
@@ -239,7 +239,7 @@ endif;
 if (strlen($NOTICE)):
 ?>
 			<div role="alert" class="alert alert-info">
-				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE); ?>
+				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -258,7 +258,7 @@ endif;
 if (strlen($STATUS)):
 ?>
 			<div id="status" role="alert" class="alert alert-success">
-				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS); ?>
+				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
 			</div>
 <?php
 endif;
@@ -270,7 +270,7 @@ endif;
 ?>alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo htmlspecialchars($ERROR); ?></div>
 			<noscript><div id="noscript" role="alert" class="nonworking alert alert-warning"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <?php echo I18n::_('Javascript is required for PrivateBin to work.<br />Sorry for the inconvenience.'); ?></div></noscript>
 			<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo I18n::_('PrivateBin requires a modern browser to work.'); ?></div>
-			<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'); ?>
+			<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.opera.com/">Opera</a>,
 				<a href="https://www.google.com/chrome">Chrome</a>,
@@ -284,7 +284,7 @@ endif;
 if (strlen($URLSHORTENER)):
 ?>
 					<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-primary">
-						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'); ?>
+						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
 					</button>
 <?php
 endif;
@@ -317,7 +317,7 @@ endif;
 				<h4 class="col-md-5 col-xs-8"><?php echo I18n::_('PrivateBin'); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
 				<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
 				<p id="aboutbox" class="col-md-6 col-xs-12">
-					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'); ?>
+					<?php echo I18n::_('PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/PrivateBin/PrivateBin/wiki">project page</a>.'), PHP_EOL; ?>
 				</p>
 			</div>
 		</footer>

+ 3 - 3
tpl/page.php

@@ -65,7 +65,7 @@ endif;
 			<h3 class="title"><?php echo $VERSION; ?></h3>
 			<noscript><div id="noscript" class="nonworking"><?php echo I18n::_('Javascript is required for PrivateBin to work.<br />Sorry for the inconvenience.'); ?></div></noscript>
 			<div id="oldienotice" class="nonworking"><?php echo I18n::_('PrivateBin requires a modern browser to work.'); ?></div>
-			<div id="ienotice"><?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'); ?>
+			<div id="ienotice"><?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.opera.com/">Opera</a>,
 				<a href="https://www.google.com/chrome">Chrome</a>,
@@ -104,7 +104,7 @@ endforeach;
 					</div>
 					<div id="remainingtime" class="hidden"></div>
 					<div id="burnafterreadingoption" class="button hidden">
-						<input type="checkbox" id="burnafterreading" name="burnafterreading" <?php
+						<input type="checkbox" id="burnafterreading" name="burnafterreading"<?php
 if ($BURNAFTERREADINGSELECTED):
 ?> checked="checked"<?php
 endif;
@@ -115,7 +115,7 @@ endif;
 if ($DISCUSSION):
 ?>
 					<div id="opendisc" class="button hidden">
-						<input type="checkbox" id="opendiscussion" name="opendiscussion" <?php
+						<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
     if ($OPENDISCUSSION):
 ?> checked="checked"<?php
     endif;

+ 30 - 1
tst/Data/FilesystemTest.php

@@ -35,7 +35,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
 
         // storing comments
         $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
-        $this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()) !== false, 'store comment');
+        $this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment');
         $this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
         $comment = json_decode(json_encode(Helper::getComment()));
         $comment->id = Helper::getCommentId();
@@ -93,4 +93,33 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
             }
         }
     }
+
+    /**
+     * @expectedException Exception
+     * @expectedExceptionCode 90
+     */
+    public function testErrorDetection()
+    {
+        $this->_model->delete(Helper::getPasteId());
+        $paste = Helper::getPaste(array('formatter' => "Invalid UTF-8 sequence: \xB1\x31"));
+        $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
+        $this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store broken paste');
+        $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
+    }
+
+    /**
+     * @expectedException Exception
+     * @expectedExceptionCode 90
+     */
+    public function testCommentErrorDetection()
+    {
+        $this->_model->delete(Helper::getPasteId());
+        $comment = Helper::getComment(array('formatter' => "Invalid UTF-8 sequence: \xB1\x31"));
+        $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
+        $this->assertTrue($this->_model->create(Helper::getPasteId(), Helper::getPaste()), 'store new paste');
+        $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
+        $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
+        $this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment');
+        $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist');
+    }
 }

+ 82 - 4
tst/ModelTest.php

@@ -5,7 +5,9 @@ use PrivateBin\Data\Database;
 use PrivateBin\Model;
 use PrivateBin\Model\Paste;
 use PrivateBin\Persistence\ServerSalt;
+use PrivateBin\Persistence\TrafficLimiter;
 use PrivateBin\Vizhash16x16;
+use Identicon\Identicon;
 
 class ModelTest extends PHPUnit_Framework_TestCase
 {
@@ -167,13 +169,13 @@ class ModelTest extends PHPUnit_Framework_TestCase
         $paste->setOpendiscussion();
         $paste->store();
 
-        $vz = new Vizhash16x16();
-        $pngdata = 'data:image/png;base64,' . base64_encode($vz->generate($_SERVER['REMOTE_ADDR']));
         $comment = $paste->getComment(Helper::getPasteId());
         $comment->setData($commentData['data']);
         $comment->setNickname($commentData['meta']['nickname']);
         $comment->store();
 
+        $identicon = new Identicon();
+        $pngdata = $identicon->getImageDataUri(TrafficLimiter::getHash(), 16);
         $comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId())->get();
         $this->assertEquals($pngdata, $comment->meta->vizhash, 'nickname triggers vizhash to be set');
     }
@@ -260,9 +262,9 @@ class ModelTest extends PHPUnit_Framework_TestCase
     public function testCommentWithDisabledVizhash()
     {
         $options = parse_ini_file(CONF, true);
-        $options['main']['vizhash'] = false;
+        $options['main']['icon'] = 'none';
         $options['model'] = array(
-            'class' => 'privatebin_db',
+            'class' => 'Database',
         );
         $options['model_options'] = array(
             'dsn' => 'sqlite::memory:',
@@ -311,4 +313,80 @@ class ModelTest extends PHPUnit_Framework_TestCase
         $this->assertEquals($commentData['meta']['nickname'], $comment->meta->nickname);
         $this->assertFalse(property_exists($comment->meta, 'vizhash'), 'vizhash was not generated');
     }
+
+    public function testCommentIdenticon()
+    {
+        $options = parse_ini_file(CONF, true);
+        $options['main']['icon'] = 'identicon';
+        $options['model'] = array(
+            'class' => 'Database',
+        );
+        $options['model_options'] = array(
+            'dsn' => 'sqlite::memory:',
+            'usr' => null,
+            'pwd' => null,
+            'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
+        );
+        Helper::confBackup();
+        Helper::createIniFile(CONF, $options);
+        $model = new Model(new Configuration);
+
+        $pasteData = Helper::getPaste();
+        $commentData = Helper::getComment();
+        $model->getPaste(Helper::getPasteId())->delete();
+
+        $paste = $model->getPaste();
+        $paste->setData($pasteData['data']);
+        $paste->setOpendiscussion();
+        $paste->setFormatter($pasteData['meta']['formatter']);
+        $paste->store();
+
+        $comment = $paste->getComment(Helper::getPasteId());
+        $comment->setData($commentData['data']);
+        $comment->setNickname($commentData['meta']['nickname']);
+        $comment->store();
+
+        $identicon = new Identicon();
+        $pngdata = $identicon->getImageDataUri(TrafficLimiter::getHash(), 16);
+        $comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId())->get();
+        $this->assertEquals($pngdata, $comment->meta->vizhash, 'nickname triggers vizhash to be set');
+    }
+
+    public function testCommentVizhash()
+    {
+        $options = parse_ini_file(CONF, true);
+        $options['main']['icon'] = 'vizhash';
+        $options['model'] = array(
+            'class' => 'Database',
+        );
+        $options['model_options'] = array(
+            'dsn' => 'sqlite::memory:',
+            'usr' => null,
+            'pwd' => null,
+            'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
+        );
+        Helper::confBackup();
+        Helper::createIniFile(CONF, $options);
+        $model = new Model(new Configuration);
+
+        $pasteData = Helper::getPaste();
+        $commentData = Helper::getComment();
+        $model->getPaste(Helper::getPasteId())->delete();
+
+        $paste = $model->getPaste();
+        $paste->setData($pasteData['data']);
+        $paste->setOpendiscussion();
+        $paste->setFormatter($pasteData['meta']['formatter']);
+        $paste->store();
+
+        $comment = $paste->getComment(Helper::getPasteId());
+        $comment->setData($commentData['data']);
+        $comment->setNickname($commentData['meta']['nickname']);
+        $comment->store();
+
+        $vz = new Vizhash16x16();
+        $pngdata = 'data:image/png;base64,' . base64_encode($vz->generate(TrafficLimiter::getHash()));
+        $comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId())->get();
+        $this->assertEquals($pngdata, $comment->meta->vizhash, 'nickname triggers vizhash to be set');
+    }
 }

+ 3 - 3
tst/Vizhash16x16Test.php

@@ -30,11 +30,11 @@ class Vizhash16x16Test extends PHPUnit_Framework_TestCase
     public function testVizhashGeneratesUniquePngsPerIp()
     {
         $vz = new Vizhash16x16();
-        $pngdata = $vz->generate('127.0.0.1');
+        $pngdata = $vz->generate(hash('sha512', '127.0.0.1'));
         file_put_contents($this->_file, $pngdata);
         $finfo = new finfo(FILEINFO_MIME_TYPE);
         $this->assertEquals('image/png', $finfo->file($this->_file));
-        $this->assertNotEquals($pngdata, $vz->generate('2001:1620:2057:dead:beef::cafe:babe'));
-        $this->assertEquals($pngdata, $vz->generate('127.0.0.1'));
+        $this->assertNotEquals($pngdata, $vz->generate(hash('sha512', '2001:1620:2057:dead:beef::cafe:babe')));
+        $this->assertEquals($pngdata, $vz->generate(hash('sha512', '127.0.0.1')));
     }
 }

+ 7 - 1
vendor/composer/autoload_classmap.php

@@ -6,14 +6,21 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = dirname($vendorDir);
 
 return array(
+    'Identicon\\Generator\\BaseGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php',
+    'Identicon\\Generator\\GdGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php',
+    'Identicon\\Generator\\GeneratorInterface' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php',
+    'Identicon\\Generator\\ImageMagickGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/ImageMagickGenerator.php',
+    'Identicon\\Identicon' => $vendorDir . '/yzalis/identicon/src/Identicon/Identicon.php',
     'PrivateBin\\Configuration' => $baseDir . '/lib/Configuration.php',
     'PrivateBin\\Data\\AbstractData' => $baseDir . '/lib/Data/AbstractData.php',
     'PrivateBin\\Data\\Database' => $baseDir . '/lib/Data/Database.php',
     'PrivateBin\\Data\\Filesystem' => $baseDir . '/lib/Data/Filesystem.php',
     'PrivateBin\\Filter' => $baseDir . '/lib/Filter.php',
     'PrivateBin\\I18n' => $baseDir . '/lib/I18n.php',
+    'PrivateBin\\Json' => $baseDir . '/lib/Json.php',
     'PrivateBin\\Model' => $baseDir . '/lib/Model.php',
     'PrivateBin\\Model\\AbstractModel' => $baseDir . '/lib/Model/AbstractModel.php',
+    'PrivateBin\\Model\\Comment' => $baseDir . '/lib/Model/Comment.php',
     'PrivateBin\\Model\\Paste' => $baseDir . '/lib/Model/Paste.php',
     'PrivateBin\\Persistence\\AbstractPersistence' => $baseDir . '/lib/Persistence/AbstractPersistence.php',
     'PrivateBin\\Persistence\\PurgeLimiter' => $baseDir . '/lib/Persistence/PurgeLimiter.php',
@@ -24,5 +31,4 @@ return array(
     'PrivateBin\\Sjcl' => $baseDir . '/lib/Sjcl.php',
     'PrivateBin\\View' => $baseDir . '/lib/View.php',
     'PrivateBin\\Vizhash16x16' => $baseDir . '/lib/Vizhash16x16.php',
-    'PrivateBin\\model\\Comment' => $baseDir . '/lib/Model/Comment.php',
 );

+ 0 - 10
vendor/composer/autoload_files.php

@@ -1,10 +0,0 @@
-<?php
-
-// autoload_files.php @generated by Composer
-
-$vendorDir = dirname(dirname(__FILE__));
-$baseDir = dirname($vendorDir);
-
-return array(
-    '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
-);

+ 1 - 0
vendor/composer/autoload_namespaces.php

@@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = dirname($vendorDir);
 
 return array(
+    'Identicon' => array($vendorDir . '/yzalis/identicon/src'),
 );

+ 0 - 18
vendor/composer/autoload_real.php

@@ -47,24 +47,6 @@ class ComposerAutoloaderInitDontChange
 
         $loader->register(true);
 
-        if ($useStaticLoader) {
-            $includeFiles = Composer\Autoload\ComposerStaticInitDontChange::$files;
-        } else {
-            $includeFiles = require __DIR__ . '/autoload_files.php';
-        }
-        foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequireDontChange($fileIdentifier, $file);
-        }
-
         return $loader;
     }
 }
-
-function composerRequireDontChange($fileIdentifier, $file)
-{
-    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
-        require $file;
-
-        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
-    }
-}

+ 18 - 5
vendor/composer/autoload_static.php

@@ -6,10 +6,6 @@ namespace Composer\Autoload;
 
 class ComposerStaticInitDontChange
 {
-    public static $files = array (
-        '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
-    );
-
     public static $prefixLengthsPsr4 = array (
         'P' => 
         array (
@@ -24,15 +20,32 @@ class ComposerStaticInitDontChange
         ),
     );
 
+    public static $prefixesPsr0 = array (
+        'I' => 
+        array (
+            'Identicon' => 
+            array (
+                0 => __DIR__ . '/..' . '/yzalis/identicon/src',
+            ),
+        ),
+    );
+
     public static $classMap = array (
+        'Identicon\\Generator\\BaseGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php',
+        'Identicon\\Generator\\GdGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php',
+        'Identicon\\Generator\\GeneratorInterface' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php',
+        'Identicon\\Generator\\ImageMagickGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/ImageMagickGenerator.php',
+        'Identicon\\Identicon' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Identicon.php',
         'PrivateBin\\Configuration' => __DIR__ . '/../..' . '/lib/Configuration.php',
         'PrivateBin\\Data\\AbstractData' => __DIR__ . '/../..' . '/lib/Data/AbstractData.php',
         'PrivateBin\\Data\\Database' => __DIR__ . '/../..' . '/lib/Data/Database.php',
         'PrivateBin\\Data\\Filesystem' => __DIR__ . '/../..' . '/lib/Data/Filesystem.php',
         'PrivateBin\\Filter' => __DIR__ . '/../..' . '/lib/Filter.php',
         'PrivateBin\\I18n' => __DIR__ . '/../..' . '/lib/I18n.php',
+        'PrivateBin\\Json' => __DIR__ . '/../..' . '/lib/Json.php',
         'PrivateBin\\Model' => __DIR__ . '/../..' . '/lib/Model.php',
         'PrivateBin\\Model\\AbstractModel' => __DIR__ . '/../..' . '/lib/Model/AbstractModel.php',
+        'PrivateBin\\Model\\Comment' => __DIR__ . '/../..' . '/lib/Model/Comment.php',
         'PrivateBin\\Model\\Paste' => __DIR__ . '/../..' . '/lib/Model/Paste.php',
         'PrivateBin\\Persistence\\AbstractPersistence' => __DIR__ . '/../..' . '/lib/Persistence/AbstractPersistence.php',
         'PrivateBin\\Persistence\\PurgeLimiter' => __DIR__ . '/../..' . '/lib/Persistence/PurgeLimiter.php',
@@ -43,7 +56,6 @@ class ComposerStaticInitDontChange
         'PrivateBin\\Sjcl' => __DIR__ . '/../..' . '/lib/Sjcl.php',
         'PrivateBin\\View' => __DIR__ . '/../..' . '/lib/View.php',
         'PrivateBin\\Vizhash16x16' => __DIR__ . '/../..' . '/lib/Vizhash16x16.php',
-        'PrivateBin\\model\\Comment' => __DIR__ . '/../..' . '/lib/Model/Comment.php',
     );
 
     public static function getInitializer(ClassLoader $loader)
@@ -51,6 +63,7 @@ class ComposerStaticInitDontChange
         return \Closure::bind(function () use ($loader) {
             $loader->prefixLengthsPsr4 = ComposerStaticInitDontChange::$prefixLengthsPsr4;
             $loader->prefixDirsPsr4 = ComposerStaticInitDontChange::$prefixDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInitDontChange::$prefixesPsr0;
             $loader->classMap = ComposerStaticInitDontChange::$classMap;
 
         }, null, ClassLoader::class);

+ 25 - 23
vendor/composer/installed.json

@@ -1,35 +1,37 @@
 [
     {
-        "name": "paragonie/random_compat",
-        "version": "v2.0.2",
-        "version_normalized": "2.0.2.0",
+        "name": "yzalis/identicon",
+        "version": "1.1.0",
+        "version_normalized": "1.1.0.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/paragonie/random_compat.git",
-            "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf"
+            "url": "https://github.com/yzalis/Identicon.git",
+            "reference": "a99fc2a3d018512f7914bc6f972952536c0f309b"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/paragonie/random_compat/zipball/088c04e2f261c33bed6ca5245491cfca69195ccf",
-            "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf",
+            "url": "https://api.github.com/repos/yzalis/Identicon/zipball/a99fc2a3d018512f7914bc6f972952536c0f309b",
+            "reference": "a99fc2a3d018512f7914bc6f972952536c0f309b",
             "shasum": ""
         },
         "require": {
-            "php": ">=5.2.0"
+            "php": ">=5.3.0"
         },
         "require-dev": {
-            "phpunit/phpunit": "4.*|5.*"
+            "fzaninotto/faker": "1.2.*@dev"
         },
-        "suggest": {
-            "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
-        },
-        "time": "2016-04-03 06:00:07",
+        "time": "2014-07-13 09:19:12",
         "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.1-dev"
+            }
+        },
         "installation-source": "dist",
         "autoload": {
-            "files": [
-                "lib/random.php"
-            ]
+            "psr-0": {
+                "Identicon": "src/"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -37,16 +39,16 @@
         ],
         "authors": [
             {
-                "name": "Paragon Initiative Enterprises",
-                "email": "security@paragonie.com",
-                "homepage": "https://paragonie.com"
+                "name": "Benjamin Laugueux",
+                "email": "benjamin@yzalis.com"
             }
         ],
-        "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+        "description": "Create awesome unique avatar.",
+        "homepage": "http://identicon-php.org",
         "keywords": [
-            "csprng",
-            "pseudorandom",
-            "random"
+            "avatar",
+            "identicon",
+            "image"
         ]
     }
 ]

+ 241 - 0
vendor/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php

@@ -0,0 +1,241 @@
+<?php
+
+namespace Identicon\Generator;
+
+use Identicon\Generator\GeneratorInterface;
+
+/**
+ * @author Benjamin Laugueux <benjamin@yzalis.com>
+ */
+class BaseGenerator
+{
+    /**
+     * @var mixed
+     */
+    protected $generatedImage;
+
+    /**
+     * @var integer
+     */
+    protected $color;
+
+    /**
+     * @var integer
+     */
+    protected $backgroundColor;
+
+    /**
+     * @var integer
+     */
+    protected $size;
+
+    /**
+     * @var integer
+     */
+    protected $pixelRatio;
+
+    /**
+     * @var string
+     */
+    private $hash;
+
+    /**
+     * @var array
+     */
+    private $arrayOfSquare = array();
+
+    /**
+     * Set the image color
+     *
+     * @param string|array $color The color in hexa (6 chars) or rgb array
+     *
+     * @return this
+     */
+    public function setColor($color)
+    {
+        if (null === $color) {
+            return $this;
+        }
+
+        $this->color = $this->convertColor($color);
+
+        return $this;
+    }
+
+    /**
+     * Set the image background color
+     *
+     * @param string|array $backgroundColor The color in hexa (6 chars) or rgb array
+     *
+     * @return this
+     */
+    public function setBackgroundColor($backgroundColor)
+    {
+        if (null === $backgroundColor) {
+            return $this;
+        }
+
+        $this->backgroundColor = $this->convertColor($backgroundColor);
+
+        return $this;
+    }
+
+    private function convertColor($color)
+    {
+        $convertedColor = array();
+        if (is_array($color)) {
+            $convertedColor[0] = $color[0];
+            $convertedColor[1] = $color[1];
+            $convertedColor[2] = $color[2];
+        } else {
+            if (false !== strpos($color, '#')) {
+                $color = substr($color, 1);
+            }
+            $convertedColor[0] = hexdec(substr($color, 0, 2));
+            $convertedColor[1] = hexdec(substr($color, 2, 2));
+            $convertedColor[2] = hexdec(substr($color, 4, 2));
+        }
+
+        return $convertedColor;
+    }
+
+    /**
+     * Get the color
+     *
+     * @return array
+     */
+    public function getColor()
+    {
+        return $this->color;
+    }
+
+
+    /**
+     * Get the background color
+     *
+     * @return array
+     */
+    public function getBackgroundColor()
+    {
+        return $this->backgroundColor;
+    }
+
+    /**
+     * Convert the hash into an multidimensionnal array of boolean
+     *
+     * @return this
+     */
+    private function convertHashToArrayOfBoolean()
+    {
+        preg_match_all('/(\w)(\w)/', $this->hash, $chars);
+        foreach ($chars[1] as $i => $char) {
+            if ($i % 3 == 0) {
+                $this->arrayOfSquare[$i/3][0] = $this->convertHexaToBoolean($char);
+                $this->arrayOfSquare[$i/3][4] = $this->convertHexaToBoolean($char);
+            } elseif ($i % 3 == 1) {
+                $this->arrayOfSquare[$i/3][1] = $this->convertHexaToBoolean($char);
+                $this->arrayOfSquare[$i/3][3] = $this->convertHexaToBoolean($char);
+            } else {
+                $this->arrayOfSquare[$i/3][2] = $this->convertHexaToBoolean($char);
+            }
+            ksort($this->arrayOfSquare[$i/3]);
+        }
+
+        $this->color[0] = hexdec(array_pop($chars[1]))*16;
+        $this->color[1] = hexdec(array_pop($chars[1]))*16;
+        $this->color[2] = hexdec(array_pop($chars[1]))*16;
+
+        return $this;
+    }
+
+    /**
+     * Convert an heaxecimal number into a boolean
+     *
+     * @param string $hexa
+     *
+     * @return boolean
+     */
+    private function convertHexaToBoolean($hexa)
+    {
+        return (bool) intval(round(hexdec($hexa)/10));
+    }
+
+    /**
+     *
+     *
+     * @return array
+     */
+    public function getArrayOfSquare()
+    {
+        return $this->arrayOfSquare;
+    }
+
+    /**
+     * Get the identicon string hash
+     *
+     * @return string
+     */
+    public function getHash()
+    {
+        return $this->hash;
+    }
+
+    /**
+     * Generate a hash fron the original string
+     *
+     * @param string $string
+     *
+     * @return this
+     */
+    public function setString($string)
+    {
+        if (null === $string) {
+            throw new \Exception('The string cannot be null.');
+        }
+
+        $this->hash = md5($string);
+
+        $this->convertHashToArrayOfBoolean();
+
+        return $this;
+    }
+
+    /**
+     * Set the image size
+     *
+     * @param integer $size
+     *
+     * @return this
+     */
+    public function setSize($size)
+    {
+        if (null === $size) {
+            return $this;
+        }
+
+        $this->size = $size;
+        $this->pixelRatio = round($size / 5);
+
+        return $this;
+    }
+
+    /**
+     * Get the image size
+     *
+     * @return integer
+     */
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    /**
+     * Get the pixel ratio
+     *
+     * @return array
+     */
+    public function getPixelRatio()
+    {
+        return $this->pixelRatio;
+    }
+}

+ 76 - 0
vendor/yzalis/identicon/src/Identicon/Generator/GdGenerator.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace Identicon\Generator;
+
+use Identicon\Generator\GeneratorInterface;
+
+/**
+ * @author Benjamin Laugueux <benjamin@yzalis.com>
+ */
+class GdGenerator extends BaseGenerator implements GeneratorInterface
+{
+    public function __construct()
+    {
+        if (!extension_loaded('gd')) {
+            throw new \Exception('GD does not appear to be avaliable in your PHP installation. Please try another generator');
+        }
+    }
+
+    private function generateImage()
+    {
+        // prepare image
+        $this->generatedImage = imagecreatetruecolor($this->getPixelRatio() * 5, $this->getPixelRatio() * 5);
+
+        $rgbBackgroundColor = $this->getBackgroundColor();
+        if (null === $rgbBackgroundColor) {
+            $background = imagecolorallocate($this->generatedImage, 0, 0, 0);
+            imagecolortransparent($this->generatedImage, $background);
+        } else {
+            $background = imagecolorallocate($this->generatedImage, $rgbBackgroundColor[0], $rgbBackgroundColor[1], $rgbBackgroundColor[2]);
+            imagefill($this->generatedImage, 0, 0, $background);
+        }
+
+        // prepage color
+        $rgbColor = $this->getColor();
+        $gdColor = imagecolorallocate($this->generatedImage, $rgbColor[0], $rgbColor[1], $rgbColor[2]);
+
+        // draw content
+        foreach ($this->getArrayOfSquare() as $lineKey => $lineValue) {
+            foreach ($lineValue as $colKey => $colValue) {
+                if (true === $colValue) {
+                    imagefilledrectangle($this->generatedImage, $colKey * $this->getPixelRatio(), $lineKey * $this->getPixelRatio(), ($colKey + 1) * $this->getPixelRatio(), ($lineKey + 1) * $this->getPixelRatio(), $gdColor);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null)
+    {
+        ob_start();
+        imagepng($this->getImageResource($string, $size, $color, $backgroundColor));
+        $imageData = ob_get_contents();
+        ob_end_clean();
+
+        return $imageData;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getImageResource($string, $size = null, $color = null, $backgroundColor = null)
+    {
+        $this
+            ->setString($string)
+            ->setSize($size)
+            ->setColor($color)
+            ->setBackgroundColor($backgroundColor)
+            ->generateImage();
+
+        return $this->generatedImage;
+    }
+}

+ 33 - 0
vendor/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Identicon\Generator;
+
+/**
+ * @author Benjamin Laugueux <benjamin@yzalis.com>
+ */
+interface GeneratorInterface
+{
+    /**
+     *
+     *
+     * @param  string       $string
+     * @param  integer      $size
+     * @param  array|string $color
+     * @param  array|string $backgroundColor
+     *
+     * @return mixed
+     */
+    function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null);
+
+    /**
+     *
+     *
+     * @param  string       $string
+     * @param  integer      $size
+     * @param  array|string $color
+     * @param  array|string $backgroundColor
+     *
+     * @return string
+     */
+    function getImageResource($string, $size = null, $color = null, $backgroundColor = null);
+}

+ 80 - 0
vendor/yzalis/identicon/src/Identicon/Generator/ImageMagickGenerator.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace Identicon\Generator;
+
+use Identicon\Generator\GeneratorInterface;
+
+/**
+ * @author Francis Chuang <francis.chuang@gmail.com>
+ */
+class ImageMagickGenerator extends BaseGenerator implements GeneratorInterface
+{
+    public function __construct()
+    {
+        if (!extension_loaded('imagick')) {
+            throw new \Exception('ImageMagick does not appear to be avaliable in your PHP installation. Please try another generator');
+        }
+    }
+
+    private function generateImage()
+    {
+        $this->generatedImage = new \Imagick();
+        $rgbBackgroundColor = $this->getBackgroundColor();
+
+        if (null === $rgbBackgroundColor) {
+            $background = 'none';
+        } else {
+            $background = new \ImagickPixel("rgb($rgbBackgroundColor[0],$rgbBackgroundColor[1],$rgbBackgroundColor[2])");
+        }
+
+        $this->generatedImage->newImage($this->pixelRatio * 5, $this->pixelRatio * 5, $background, 'png');
+
+        // prepare color
+        $rgbColor = $this->getColor();
+        $color = new \ImagickPixel("rgb($rgbColor[0],$rgbColor[1],$rgbColor[2])");
+
+        $draw = new \ImagickDraw();
+        $draw->setFillColor($color);
+
+        // draw the content
+        foreach ($this->getArrayOfSquare() as $lineKey => $lineValue) {
+            foreach ($lineValue as $colKey => $colValue) {
+                if (true === $colValue) {
+                    $draw->rectangle( $colKey * $this->pixelRatio, $lineKey * $this->pixelRatio, ($colKey + 1) * $this->pixelRatio, ($lineKey + 1) * $this->pixelRatio);
+                }
+            }
+        }
+
+        $this->generatedImage->drawImage($draw);
+
+        return $this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null)
+    {
+        ob_start();
+        echo $this->getImageResource($string, $size, $color, $backgroundColor);
+        $imageData = ob_get_contents();
+        ob_end_clean();
+
+        return $imageData;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getImageResource($string, $size = null, $color = null, $backgroundColor = null)
+    {
+        $this
+            ->setString($string)
+            ->setSize($size)
+            ->setColor($color)
+            ->setBackgroundColor($backgroundColor)
+            ->generateImage();
+
+        return $this->generatedImage;
+    }
+}

+ 100 - 0
vendor/yzalis/identicon/src/Identicon/Identicon.php

@@ -0,0 +1,100 @@
+<?php
+
+namespace Identicon;
+
+use Identicon\Generator\GdGenerator;
+use Identicon\Generator\GeneratorInterface;
+
+/**
+ * @author Benjamin Laugueux <benjamin@yzalis.com>
+ */
+class Identicon
+{
+    /**
+     * @var GeneratorInterface
+     */
+    private $generator;
+
+    public function __construct($generator = null)
+    {
+        if (null === $generator) {
+            $this->generator = new GdGenerator();
+        } else {
+            $this->generator = $generator;
+        }
+
+    }
+
+    /**
+     * Set the image generetor
+     *
+     * @param GeneratorInterface $generator
+     *
+     * @throws \Exception
+     */
+    public function setGenerator(GeneratorInterface $generator)
+    {
+        $this->generator = $generator;
+
+        return $this;
+    }
+
+    /**
+     * Display an Identicon image
+     *
+     * @param string  $string
+     * @param integer $size
+     * @param string  $color
+     * @param string  $backgroundColor
+     */
+    public function displayImage($string, $size = 64, $color = null, $backgroundColor = null)
+    {
+        header("Content-Type: image/png");
+        echo $this->getImageData($string, $size, $color, $backgroundColor);
+    }
+
+    /**
+     * Get an Identicon PNG image data
+     *
+     * @param string  $string
+     * @param integer $size
+     * @param string  $color
+     * @param string  $backgroundColor
+     *
+     * @return string
+     */
+    public function getImageData($string, $size = 64, $color = null, $backgroundColor = null)
+    {
+        return $this->generator->getImageBinaryData($string, $size, $color, $backgroundColor);
+    }
+
+    /**
+     * Get an Identicon PNG image resource
+     *
+     * @param string  $string
+     * @param integer $size
+     * @param string  $color
+     * @param string  $backgroundColor
+     *
+     * @return string
+     */
+    public function getImageResource($string, $size = 64, $color = null, $backgroundColor = null)
+    {
+        return $this->generator->getImageResource($string, $size, $color, $backgroundColor);
+    }
+
+    /**
+     * Get an Identicon PNG image data as base 64 encoded
+     *
+     * @param string  $string
+     * @param integer $size
+     * @param string  $color
+     * @param string  $backgroundColor
+     *
+     * @return string
+     */
+    public function getImageDataUri($string, $size = 64, $color = null, $backgroundColor = null)
+    {
+        return sprintf('data:image/png;base64,%s', base64_encode($this->getImageData($string, $size, $color, $backgroundColor)));
+    }
+}