From 802a0b26b95d725f36418a37e551a115eda81425 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Mon, 31 Aug 2015 22:10:41 +0200 Subject: [PATCH] burn after reading messages are only deleted after callback by JS when successfully decrypted, resolves #11 --- js/zerobin.js | 13 ++++++---- lib/zerobin.php | 65 ++++++++++++++++++++++++++++++++++++------------- tst/zerobin.php | 38 +++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 22 deletions(-) diff --git a/js/zerobin.js b/js/zerobin.js index e199fda6..8e9c4a39 100644 --- a/js/zerobin.js +++ b/js/zerobin.js @@ -238,6 +238,10 @@ function displayMessages(key, comments) { // Display paste expiration. if (comments[0].meta.expire_date) $('#remainingtime').removeClass('foryoureyesonly').text('This document will expire in '+secondsToHuman(comments[0].meta.remaining_time)+'.').removeClass('hidden'); if (comments[0].meta.burnafterreading) { + $.get(scriptLocation() + "?pasteid=" + pasteID() + '&deletetoken=burnafterreading', 'json') + .fail(function() { + showError('Could not delete the paste, it was not stored in burn after reading mode.'); + }); $('#remainingtime').addClass('foryoureyesonly').text('FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.').removeClass('hidden'); $('#clonebutton').addClass('hidden'); // Discourage cloning (as it can't really be prevented). } @@ -382,11 +386,7 @@ function send_data() { burnafterreading: $('#burnafterreading').is(':checked') ? 1 : 0, opendiscussion: $('#opendiscussion').is(':checked') ? 1 : 0 }; - $.post(scriptLocation(), data_to_send, 'json') - .error(function() { - showError('Data could not be sent (serveur error or not responding).'); - }) - .success(function(data) { + $.post(scriptLocation(), data_to_send, function(data) { if (data.status == 0) { stateExistingPaste(); var url = scriptLocation() + "?" + data.id + '#' + randomkey; @@ -412,6 +412,9 @@ function send_data() { else { showError('Could not create paste.'); } + }, 'json') + .fail(function() { + showError('Data could not be sent (server error or not responding).'); }); } diff --git a/lib/zerobin.php b/lib/zerobin.php index 5e5106d5..42a76af3 100644 --- a/lib/zerobin.php +++ b/lib/zerobin.php @@ -24,6 +24,13 @@ class zerobin */ const VERSION = 'Alpha 0.19'; + /** + * show the same error message if the paste expired or does not exist + * + * @const string + */ + const GENERIC_ERROR = 'Paste does not exist, has expired or has been deleted.'; + /** * configuration array * @@ -99,7 +106,11 @@ class zerobin // delete an existing paste elseif (!empty($_GET['deletetoken']) && !empty($_GET['pasteid'])) { - $this->_delete($_GET['pasteid'], $_GET['deletetoken']); + $result = $this->_delete($_GET['pasteid'], $_GET['deletetoken']); + if (strlen($result)) { + echo $result; + return; + } } // display an existing paste elseif (!empty($_SERVER['QUERY_STRING'])) @@ -355,7 +366,7 @@ class zerobin * @access private * @param string $dataid * @param string $deletetoken - * @return void + * @return string */ private function _delete($dataid, $deletetoken) { @@ -363,14 +374,42 @@ class zerobin if (!filter::is_valid_paste_id($dataid)) { $this->_error = 'Invalid paste ID.'; - return; + return ''; } // Check that paste exists. if (!$this->_model()->exists($dataid)) { - $this->_error = 'Paste does not exist, has expired or has been deleted.'; - return; + $this->_error = self::GENERIC_ERROR; + return ''; + } + + // Get the paste itself. + $paste = $this->_model()->read($dataid); + + // See if paste has expired. + if ( + isset($paste->meta->expire_date) && + $paste->meta->expire_date < time() + ) + { + // Delete the paste + $this->_model()->delete($dataid); + $this->_error = self::GENERIC_ERROR; + } + + if ($deletetoken == 'burnafterreading') { + header('Content-type: application/json'); + if ( + isset($paste->meta->burnafterreading) && + $paste->meta->burnafterreading + ) + { + // Delete the paste + $this->_model()->delete($dataid); + return $this->_return_message(0, 'Paste was properly deleted.'); + } + return $this->_return_message(1, 'Paste is not of burn-after-reading type.'); } // Make sure token is valid. @@ -378,12 +417,13 @@ class zerobin if (!filter::slow_equals($deletetoken, hash_hmac('sha1', $dataid, serversalt::get()))) { $this->_error = 'Wrong deletion token. Paste was not deleted.'; - return; + return ''; } // Paste exists and deletion token is valid: Delete the paste. $this->_model()->delete($dataid); $this->_status = 'Paste was properly deleted.'; + return ''; } /** @@ -402,9 +442,6 @@ class zerobin return; } - // show the same error message if the paste expired or does not exist - $genericError = 'Paste does not exist, has expired or has been deleted.'; - // Check that paste exists. if ($this->_model()->exists($dataid)) { @@ -419,7 +456,7 @@ class zerobin { // Delete the paste $this->_model()->delete($dataid); - $this->_error = $genericError; + $this->_error = self::GENERIC_ERROR; } // If no error, return the paste. else @@ -444,17 +481,11 @@ class zerobin ); } $this->_data = json_encode($messages); - - // If the paste was meant to be read only once, delete it. - if ( - property_exists($paste->meta, 'burnafterreading') && - $paste->meta->burnafterreading - ) $this->_model()->delete($dataid); } } else { - $this->_error = $genericError; + $this->_error = self::GENERIC_ERROR; } } diff --git a/tst/zerobin.php b/tst/zerobin.php index 3f64ad41..23da5565 100644 --- a/tst/zerobin.php +++ b/tst/zerobin.php @@ -527,4 +527,42 @@ class zerobinTest extends PHPUnit_Framework_TestCase ); $this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after failing to delete data'); } + + /** + * @runInSeparateProcess + */ + public function testDeleteBurnAfterReading() + { + $this->reset(); + $burnPaste = self::$paste; + $burnPaste['meta']['burnafterreading'] = true; + $this->_model->create(self::$pasteid, $burnPaste); + $this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data'); + $_GET['pasteid'] = self::$pasteid; + $_GET['deletetoken'] = 'burnafterreading'; + ob_start(); + new zerobin; + $content = ob_get_contents(); + $response = json_decode($content, true); + $this->assertEquals(0, $response['status'], 'outputs status'); + $this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted'); + } + + /** + * @runInSeparateProcess + */ + public function testDeleteInvalidBurnAfterReading() + { + $this->reset(); + $this->_model->create(self::$pasteid, self::$paste); + $this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data'); + $_GET['pasteid'] = self::$pasteid; + $_GET['deletetoken'] = 'burnafterreading'; + ob_start(); + new zerobin; + $content = ob_get_contents(); + $response = json_decode($content, true); + $this->assertEquals(1, $response['status'], 'outputs status'); + $this->assertTrue($this->_model->exists(self::$pasteid), 'paste successfully deleted'); + } } \ No newline at end of file