From 5b253cf77c5eb5aaa135e08259c3546554e5057b Mon Sep 17 00:00:00 2001 From: Sebastien SAUVAGE Date: Fri, 1 Nov 2013 01:15:14 +0100 Subject: [PATCH] ZeroBin 0.17 * added deletion link. * small refactoring. * improved regex checks. * larger server alt on installation. --- cfg/conf.ini | 2 +- css/zerobin.css | 12 +++--- index.php | 2 +- js/zerobin.js | 21 ++++++++-- lib/serversalt.php | 71 +++++++++++++++++++++++++++++++ lib/trafficlimiter.php | 49 ++++------------------ lib/vizhash16x16.php | 45 ++++++++++---------- lib/zerobin.php | 95 ++++++++++++++++++++++++++++++++---------- tpl/page.html | 15 ++++--- tst/RainTPL.php | 5 ++- tst/trafficlimiter.php | 4 +- 11 files changed, 217 insertions(+), 104 deletions(-) create mode 100644 lib/serversalt.php diff --git a/cfg/conf.ini b/cfg/conf.ini index 5b91ece..e997b23 100644 --- a/cfg/conf.ini +++ b/cfg/conf.ini @@ -5,7 +5,7 @@ ; @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin ; @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) ; @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License -; @version 0.15 +; @version 0.17 [main] ; enable or disable discussions diff --git a/css/zerobin.css b/css/zerobin.css index c2372fe..269f827 100644 --- a/css/zerobin.css +++ b/css/zerobin.css @@ -1,6 +1,5 @@ -/* ZeroBin 0.15 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */ +/* ZeroBin 0.17 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */ -/* Hé, t'as vu Idleman, j'ai fait un effort sur les CSS ! ;-) */ /* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved. Licensed under the BSD License. - http://yuilibrary.com/license/ */ @@ -100,8 +99,7 @@ h3 { padding: 5px 10px; } - -#pastelink { +#pasteresult { background-color: #1F2833; color: #fff; padding: 4px 12px; @@ -111,9 +109,11 @@ h3 { box-shadow: inset 0 2px 2px #000; } -#pastelink a { color: #fff; } +#pasteresult a { color: #fff; } -#pastelink button { margin-left: 11px } +#pasteresult button { margin-left: 11px } + +#deletelink { float: right; } #toolbar, #status { margin-bottom: 5px; } diff --git a/index.php b/index.php index 42576dc..7e662e3 100644 --- a/index.php +++ b/index.php @@ -7,7 +7,7 @@ * @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) * @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License - * @version 0.15 + * @version 0.17 */ // change this, if your php files and data is outside of your webservers document root diff --git a/js/zerobin.js b/js/zerobin.js index c255016..3fe4c07 100644 --- a/js/zerobin.js +++ b/js/zerobin.js @@ -6,7 +6,7 @@ * @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) * @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License - * @version 0.15 + * @version 0.17 */ // Immediately start random number generator collector. @@ -328,8 +328,13 @@ function send_data() { if (data.status == 0) { stateExistingPaste(); var url = scriptLocation() + "?" + data.id + '#' + randomkey; + var deleteUrl = scriptLocation() + "?pasteid=" + data.id + '&deletetoken=' + data.deletetoken; showStatus(''); - $('div#pastelink').html('Your paste is ' + url + '').removeClass('hidden'); + + $('div#pastelink').html('Your paste is ' + url + ''); + $('div#deletelink').html('Delete link'); + $('div#pasteresult').show(); + setElementText($('div#cleartext'), $('textarea#message').val()); setElementText($('pre#prettyprint'), $('textarea#message').val()); urls2links($('div#cleartext')); @@ -357,7 +362,7 @@ function stateNewPaste() { $('input#password').addClass('hidden'); //$('#password').removeClass('hidden'); $('div#opendisc').removeClass('hidden'); $('button#newbutton').removeClass('hidden'); - $('div#pastelink').addClass('hidden'); + $('div#pasteresult').addClass('hidden'); $('textarea#message').text(''); $('textarea#message').removeClass('hidden'); $('div#cleartext').addClass('hidden'); @@ -385,7 +390,7 @@ function stateExistingPaste() { $('input#password').addClass('hidden'); $('div#opendisc').addClass('hidden'); $('button#newbutton').removeClass('hidden'); - $('div#pastelink').addClass('hidden'); + $('div#pasteresult').addClass('hidden'); $('textarea#message').addClass('hidden'); $('div#cleartext').addClass('hidden'); $('div#prettymessage').removeClass('hidden'); @@ -505,6 +510,14 @@ $(function() { } }); + // Display status returned by php code if any (eg. Paste was properly deleted.) + if ($('div#status').text().length > 0) { + showStatus($('div#status').text(),false); + return; + } + + $('div#status').html(' '); // Keep line height even if content empty. + // Display an existing paste if ($('div#cipherdata').text().length > 1) { // Missing decryption key in URL ? diff --git a/lib/serversalt.php b/lib/serversalt.php new file mode 100644 index 0000000..caa7e95 --- /dev/null +++ b/lib/serversalt.php @@ -0,0 +1,71 @@ +' + ); + } + $items = explode('|', file_get_contents(self::getPath($file))); + self::$_salt = $items[1]; + return $items[1]; + } +} diff --git a/lib/trafficlimiter.php b/lib/trafficlimiter.php index 9764fc1..9dd4a44 100644 --- a/lib/trafficlimiter.php +++ b/lib/trafficlimiter.php @@ -7,15 +7,15 @@ * @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) * @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License - * @version 0.15 + * @version 0.17 */ /** - * traffic_limiter + * trafficlimiter * * Handles traffic limiting, so no user does more than one call per 10 seconds. */ -class trafficlimiter +class trafficlimiter extends persistence { /** * @access private @@ -24,13 +24,6 @@ class trafficlimiter */ private static $_limit = 10; - /** - * @access private - * @static - * @var string - */ - private static $_path = 'data'; - /** * set the time limit in seconds * @@ -44,19 +37,6 @@ class trafficlimiter self::$_limit = $limit; } - /** - * set the path - * - * @access public - * @static - * @param string $path - * @return void - */ - public static function setPath($path) - { - self::$_path = $path; - } - /** * traffic limiter * @@ -72,29 +52,18 @@ class trafficlimiter // disable limits if set to less then 1 if (self::$_limit < 1) return true; - // Create storage directory if it does not exist. - if (!is_dir(self::$_path)) mkdir(self::$_path, 0705); - // Create .htaccess file if it does not exist. - if (!is_file(self::$_path . '/.htaccess')) + $file = 'traffic_limiter.php'; + if (!self::_exists($file)) { - file_put_contents( - self::$_path . '/.htaccess', - 'Allow from none' . PHP_EOL . - 'Deny from all'. PHP_EOL - ); - } - $file = self::$_path . '/traffic_limiter.php'; - if (!is_file($file)) - { - file_put_contents( + self::_store( $file, 'generate('hello'); - header('Content-type: image/png'); - echo $data; - exit; -*/ +/** + * VizHash_GD + * + * Visual Hash implementation in php4+GD, + * stripped down and modified version for ZeroBin + * + * @link http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd + * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) + * @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License + * @version 0.0.4 beta ZeroBin 0.17 + */ + +/** + * vizhash16x16 + * + * Example: + * $vz = new vizhash16x16(); + * $data = $vz->generate('hello'); + * header('Content-type: image/png'); + * echo $data; + * exit; + */ + class vizhash16x16 { private $VALUES; @@ -22,15 +33,7 @@ class vizhash16x16 { $this->width=16; $this->height=16; - - // Read salt from file (and create it if does not exist). - // The salt will make vizhash avatar unique on each ZeroBin installation - // to prevent IP checking. - $saltfile = PATH . 'data/salt.php'; - if (!is_file($saltfile)) - file_put_contents($saltfile,'randomSalt().'| */ ?>'); - $items=explode('|',file_get_contents($saltfile)); - $this->salt = $items[1]; + $this->salt = serversalt::get(); } // Generate a 16x16 png corresponding to $text. diff --git a/lib/zerobin.php b/lib/zerobin.php index 19d63ba..2d2923f 100644 --- a/lib/zerobin.php +++ b/lib/zerobin.php @@ -7,7 +7,7 @@ * @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) * @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License - * @version 0.15 + * @version 0.17 */ /** @@ -20,7 +20,7 @@ class zerobin /* * @const string version */ - const VERSION = 'Alpha 0.15'; + const VERSION = 'Alpha 0.17'; /** * @access private @@ -42,6 +42,12 @@ class zerobin */ private $_error = ''; + /** + * @access private + * @var string + */ + private $_status = ''; + /** * @access private * @var zerobin_data @@ -60,7 +66,7 @@ class zerobin if (version_compare(PHP_VERSION, '5.2.6') < 0) die('ZeroBin requires php 5.2.6 or above to work. Sorry.'); - // In case stupid admin has left magic_quotes enabled in php.ini. + // in case stupid admin has left magic_quotes enabled in php.ini if (get_magic_quotes_gpc()) { $_POST = array_map('filter::stripslashes_deep', $_POST); @@ -68,21 +74,26 @@ class zerobin $_COOKIE = array_map('filter::stripslashes_deep', $_COOKIE); } - // Load config from ini file. + // load config from ini file $this->_init(); - // Create new paste or comment. + // create new paste or comment if (!empty($_POST['data'])) { - $this->_create(); + $this->_create($_POST['data']); } - // Display an existing paste. + // delete an existing paste + elseif (!empty($_GET['deletetoken']) && !empty($_GET['pasteid'])) + { + $this->_delete($_GET['pasteid'], $_GET['deletetoken']); + } + // display an existing paste elseif (!empty($_SERVER['QUERY_STRING'])) { - $this->_read(); + $this->_read($_SERVER['QUERY_STRING']); } - // Display ZeroBin frontend + // display ZeroBin frontend $this->_view(); } @@ -126,7 +137,7 @@ class zerobin } /** - * Store new paste or comment. + * Store new paste or comment * * POST contains: * data (mandatory) = json encoded SJCL encrypted text (containing keys: iv,salt,ct) @@ -139,9 +150,10 @@ class zerobin * pasteid (optional) = in discussion, which paste this comment belongs to. * * @access private + * @param string $data * @return void */ - private function _create() + private function _create($data) { header('Content-type: application/json'); $error = false; @@ -159,7 +171,6 @@ class zerobin ); // Make sure content is not too big. - $data = $_POST['data']; if ( strlen($data) > $this->_conf['main']['sizelimit'] ) $this->_return_message( @@ -252,8 +263,8 @@ class zerobin $pasteid = $_POST['pasteid']; $parentid = $_POST['parentid']; if ( - !preg_match('/[a-f\d]{16}/', $pasteid) || - !preg_match('/[a-f\d]{16}/', $parentid) + !preg_match('/\A[a-f\d]{16}\z/', $pasteid) || + !preg_match('/\A[a-f\d]{16}\z/', $parentid) ) $this->_return_message(1, 'Invalid data.'); // Comments do not expire (it's the paste that expires) @@ -297,23 +308,60 @@ class zerobin $this->_model()->create($dataid, $storage) === false ) $this->_return_message(1, 'Error saving paste. Sorry.'); + // Generate the "delete" token. + // The token is the hmac of the pasteid signed with the server salt. + // The paste can be delete by calling http://myserver.com/zerobin/?pasteid=&deletetoken= + $deletetoken = hash_hmac('sha1', $dataid , serversalt::get()); + // 0 = no error - $this->_return_message(0, $dataid); + $this->_return_message(0, $dataid, array('deletetoken' => $deletetoken)); } $this->_return_message(1, 'Server error.'); } + /** + * Delete an existing paste + * + * @access private + * @param string $dataid + * @param string $deletetoken + * @return void + */ + private function _delete($dataid, $deletetoken) + { + // Is this a valid paste identifier? + if (preg_match('\A[a-f\d]{16}\z', $dataid)) + { + // Check that paste exists. + if (!$this->_model()->exists($dataid)) + { + $this->_error = 'Paste does not exist, has expired or has been deleted.'; + return; + } + } + + // Make sure token is valid. + if ($deletetoken != hash_hmac('sha1', $dataid , serversalt::get())) + { + $this->_error = 'Wrong deletion token. Paste was not deleted.'; + return; + } + + // Paste exists and deletion token is valid: Delete the paste. + $this->_model()->delete($dataid); + $this->_status = 'Paste was properly deleted.'; + } + /** - * Read an existing paste or comment. + * Read an existing paste or comment * * @access private + * @param string $dataid * @return void */ - private function _read() + private function _read($dataid) { - $dataid = $_SERVER['QUERY_STRING']; - // Is this a valid paste identifier? if (preg_match('\A[a-f\d]{16}\z', $dataid)) { @@ -331,7 +379,7 @@ class zerobin { // Delete the paste $this->_model()->delete($dataid); - $this->_error = 'Paste does not exist or has expired.'; + $this->_error = 'Paste does not exist, has expired or has been deleted.'; } // If no error, return the paste. else @@ -398,7 +446,8 @@ class zerobin $page = new RainTPL; // we escape it here because ENT_NOQUOTES can't be used in RainTPL templates $page->assign('CIPHERDATA', htmlspecialchars($this->_data, ENT_NOQUOTES)); - $page->assign('ERRORMESSAGE', $this->_error); + $page->assign('ERROR', $this->_error); + $page->assign('STATUS', $this->_status); $page->assign('VERSION', self::VERSION); $page->assign('BURNAFTERREADINGSELECTED', $this->_conf['main']['burnafterreadingselected']); $page->assign('OPENDISCUSSION', $this->_conf['main']['opendiscussion']); @@ -414,9 +463,10 @@ class zerobin * @access private * @param bool $status * @param string $message + * @param array $other * @return void */ - private function _return_message($status, $message) + private function _return_message($status, $message, $other = array()) { $result = array('status' => $status); if ($status) @@ -427,6 +477,7 @@ class zerobin { $result['id'] = $message; } + $result += $other; exit(json_encode($result)); } } diff --git a/tpl/page.html b/tpl/page.html index abff849..601e65a 100644 --- a/tpl/page.html +++ b/tpl/page.html @@ -43,14 +43,14 @@
-
- +
{$STATUS|htmlspecialchars}
+
-
- + @@ -85,5 +88,5 @@
- + diff --git a/tst/RainTPL.php b/tst/RainTPL.php index fb9a293..d6f31d6 100644 --- a/tst/RainTPL.php +++ b/tst/RainTPL.php @@ -5,6 +5,8 @@ class RainTPLTest extends PHPUnit_Framework_TestCase private static $error = 'foo bar'; + private static $status = '!*#@?$+'; + private static $expire = array( '5min' => '5 minutes', '1hour' => '1 hour', @@ -26,7 +28,8 @@ class RainTPLTest extends PHPUnit_Framework_TestCase $page = new RainTPL; // We escape it here because ENT_NOQUOTES can't be used in RainTPL templates. $page->assign('CIPHERDATA', htmlspecialchars(self::$data, ENT_NOQUOTES)); - $page->assign('ERRORMESSAGE', self::$error); + $page->assign('ERROR', self::$error); + $page->assign('STATUS', self::$status); $page->assign('VERSION', self::$version); $page->assign('BURNAFTERREADINGSELECTED', false); $page->assign('OPENDISCUSSION', false); diff --git a/tst/trafficlimiter.php b/tst/trafficlimiter.php index 68947b9..67e0a5a 100644 --- a/tst/trafficlimiter.php +++ b/tst/trafficlimiter.php @@ -6,14 +6,14 @@ class trafficlimiterTest extends PHPUnit_Framework_TestCase public function setUp() { /* Setup Routine */ - $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'trafficlimit' . DIRECTORY_SEPARATOR; + $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'trafficlimit'; trafficlimiter::setPath($this->_path); } public function tearDown() { /* Tear Down Routine */ - helper::rmdir($this->_path); + helper::rmdir($this->_path . DIRECTORY_SEPARATOR); } public function testTrafficGetsLimited()