refactored JSON API, its now possible to retrieve pastes as JSON, which

is now used when posting comments, eliminating the need to store the
password in sessionStorage
This commit is contained in:
El RIDO 2015-09-01 22:33:07 +02:00
parent ded24b43ab
commit b25022e403
3 changed files with 157 additions and 70 deletions

View file

@ -213,11 +213,6 @@ function setElementText(element, text) {
* @param array comments : Array of messages to display (items = array with keys ('data','meta')
*/
function displayMessages(key, comments) {
// restore password if set in previous visit, then clear the session
if (window.sessionStorage && sessionStorage.getItem(pageKey())) {
$('#passwordinput').val(sessionStorage.getItem(pageKey()));
sessionStorage.clear();
}
try { // Try to decrypt the paste.
var cleartext = zeroDecipher(key, comments[0].data);
if (cleartext == null) throw "password prompt canceled";
@ -327,7 +322,7 @@ function send_comment(parentid) {
showStatus('Sending comment...', spin=true);
var cipherdata = zeroCipher(pageKey(), $('#replymessage').val());
var ciphernickname = '';
var nick=$('#nickname').val();
var nick = $('#nickname').val();
if (nick != '' && nick != 'Optional nickname...') {
ciphernickname = zeroCipher(pageKey(), nick);
}
@ -337,29 +332,38 @@ function send_comment(parentid) {
nickname: ciphernickname
};
$.post(scriptLocation(), data_to_send, 'json')
.error(function() {
showError('Comment could not be sent (server error or not responding).');
})
.success(function(data) {
$.post(scriptLocation(), data_to_send, function(data) {
if (data.status == 0) {
showStatus('Comment posted.');
// store password temporarily between page loads
if ($('#passwordinput').val().length > 0 && window.sessionStorage) {
sessionStorage.setItem(pageKey(), $('#passwordinput').val());
}
location.reload();
$.get(scriptLocation() + "?" + pasteID() + "&json", function(data) {
if (data.status == 0) {
displayMessages(pageKey(), data.messages);
}
else if (data.status == 1) {
showError('Could not refresh display: ' + data.message);
}
else
{
showError('Could not refresh display: unknown status');
}
}, 'json')
.fail(function() {
showError('Could not refresh display (server error or not responding).');
});
}
else if (data.status==1) {
showError('Could not post comment: '+data.message);
else if (data.status == 1) {
showError('Could not post comment: ' + data.message);
}
else {
showError('Could not post comment.');
else
{
showError('Could not post comment: unknown status');
}
}, 'json')
.fail(function() {
showError('Comment could not be sent (server error or not responding).');
});
}
/**
* Send a new paste to server
*/
@ -407,7 +411,7 @@ function send_data() {
if (typeof prettyPrint == 'function') prettyPrint();
}
else if (data.status==1) {
showError('Could not create paste: '+data.message);
showError('Could not create paste: ' + data.message);
}
else {
showError('Could not create paste.');

View file

@ -65,6 +65,14 @@ class zerobin
*/
private $_status = '';
/**
* JSON message
*
* @access private
* @var string
*/
private $_json = '';
/**
* data storage model
*
@ -84,7 +92,9 @@ class zerobin
public function __construct()
{
if (version_compare(PHP_VERSION, '5.2.6') < 0)
{
throw new Exception('ZeroBin requires php 5.2.6 or above to work. Sorry.', 1);
}
// in case stupid admin has left magic_quotes enabled in php.ini
if (get_magic_quotes_gpc())
@ -100,17 +110,12 @@ class zerobin
// create new paste or comment
if (!empty($_POST['data']))
{
echo $this->_create($_POST['data']);
return;
$this->_create($_POST['data']);
}
// delete an existing paste
elseif (!empty($_GET['deletetoken']) && !empty($_GET['pasteid']))
{
$result = $this->_delete($_GET['pasteid'], $_GET['deletetoken']);
if (strlen($result)) {
echo $result;
return;
}
$this->_delete($_GET['pasteid'], $_GET['deletetoken']);
}
// display an existing paste
elseif (!empty($_SERVER['QUERY_STRING']))
@ -118,8 +123,16 @@ class zerobin
$this->_read($_SERVER['QUERY_STRING']);
}
// display ZeroBin frontend
$this->_view();
// output JSON or HTML
if (strlen($this->_json))
{
header('Content-type: application/json');
echo $this->_json;
}
else
{
$this->_view();
}
}
/**
@ -186,31 +199,34 @@ class zerobin
*/
private function _create($data)
{
header('Content-type: application/json');
$error = false;
// Make sure last paste from the IP address was more than X seconds ago.
trafficlimiter::setLimit($this->_conf['traffic']['limit']);
trafficlimiter::setPath($this->_conf['traffic']['dir']);
if (
!trafficlimiter::canPass($_SERVER['REMOTE_ADDR'])
) return $this->_return_message(
1,
'Please wait ' .
$this->_conf['traffic']['limit'] .
' seconds between each post.'
);
if (!trafficlimiter::canPass($_SERVER['REMOTE_ADDR']))
{
$this->_return_message(
1,
'Please wait ' .
$this->_conf['traffic']['limit'] .
' seconds between each post.'
);
return;
}
// Make sure content is not too big.
$sizelimit = (int) $this->_getMainConfig('sizelimit', 2097152);
if (
strlen($data) > $sizelimit
) return $this->_return_message(
1,
'Paste is limited to ' .
filter::size_humanreadable($sizelimit) .
' of encrypted data.'
);
if (strlen($data) > $sizelimit)
{
$this->_return_message(
1,
'Paste is limited to ' .
filter::size_humanreadable($sizelimit) .
' of encrypted data.'
);
return;
}
// Make sure format is correct.
if (!sjcl::isValid($data)) return $this->_return_message(1, 'Invalid data.');
@ -280,7 +296,11 @@ class zerobin
}
}
if ($error) return $this->_return_message(1, 'Invalid data.');
if ($error)
{
$this->_return_message(1, 'Invalid data.');
return;
}
// Add post date to meta.
$meta['postdate'] = time();
@ -305,7 +325,11 @@ class zerobin
if (
!filter::is_valid_paste_id($pasteid) ||
!filter::is_valid_paste_id($parentid)
) return $this->_return_message(1, 'Invalid data.');
)
{
$this->_return_message(1, 'Invalid data.');
return;
}
// Comments do not expire (it's the paste that expires)
unset($storage['expire_date']);
@ -314,26 +338,43 @@ class zerobin
// Make sure paste exists.
if (
!$this->_model()->exists($pasteid)
) return $this->_return_message(1, 'Invalid data.');
)
{
$this->_return_message(1, 'Invalid data.');
return;
}
// Make sure the discussion is opened in this paste.
$paste = $this->_model()->read($pasteid);
if (
!$paste->meta->opendiscussion
) return $this->_return_message(1, 'Invalid data.');
)
{
$this->_return_message(1, 'Invalid data.');
return;
}
// Check for improbable collision.
if (
$this->_model()->existsComment($pasteid, $parentid, $dataid)
) return $this->_return_message(1, 'You are unlucky. Try again.');
)
{
$this->_return_message(1, 'You are unlucky. Try again.');
return;
}
// New comment
if (
$this->_model()->createComment($pasteid, $parentid, $dataid, $storage) === false
) return $this->_return_message(1, 'Error saving comment. Sorry.');
)
{
$this->_return_message(1, 'Error saving comment. Sorry.');
return;
}
// 0 = no error
return $this->_return_message(0, $dataid);
$this->_return_message(0, $dataid);
return;
}
// The user posts a standard paste.
else
@ -341,12 +382,19 @@ class zerobin
// Check for improbable collision.
if (
$this->_model()->exists($dataid)
) return $this->_return_message(1, 'You are unlucky. Try again.');
)
{
$this->_return_message(1, 'You are unlucky. Try again.');
return;
}
// New paste
if (
$this->_model()->create($dataid, $storage) === false
) return $this->_return_message(1, 'Error saving paste. Sorry.');
) {
$this->_return_message(1, 'Error saving paste. Sorry.');
return;
}
// Generate the "delete" token.
// The token is the hmac of the pasteid signed with the server salt.
@ -354,10 +402,9 @@ class zerobin
$deletetoken = hash_hmac('sha1', $dataid, serversalt::get());
// 0 = no error
return $this->_return_message(0, $dataid, array('deletetoken' => $deletetoken));
$this->_return_message(0, $dataid, array('deletetoken' => $deletetoken));
return;
}
return $this->_return_message(1, 'Server error.');
}
/**
@ -366,7 +413,7 @@ class zerobin
* @access private
* @param string $dataid
* @param string $deletetoken
* @return string
* @return void
*/
private function _delete($dataid, $deletetoken)
{
@ -374,14 +421,14 @@ 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 = self::GENERIC_ERROR;
return '';
return;
}
// Get the paste itself.
@ -396,10 +443,10 @@ class zerobin
// Delete the paste
$this->_model()->delete($dataid);
$this->_error = self::GENERIC_ERROR;
return;
}
if ($deletetoken == 'burnafterreading') {
header('Content-type: application/json');
if (
isset($paste->meta->burnafterreading) &&
$paste->meta->burnafterreading
@ -407,9 +454,13 @@ class zerobin
{
// Delete the paste
$this->_model()->delete($dataid);
return $this->_return_message(0, 'Paste was properly deleted.');
$this->_return_message(0, 'Paste was properly deleted.');
}
return $this->_return_message(1, 'Paste is not of burn-after-reading type.');
else
{
$this->_return_message(1, 'Paste is not of burn-after-reading type.');
}
return;
}
// Make sure token is valid.
@ -417,13 +468,12 @@ 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 '';
}
/**
@ -435,6 +485,12 @@ class zerobin
*/
private function _read($dataid)
{
$isJson = false;
if (($pos = strpos($dataid, '&json')) !== false) {
$isJson = true;
$dataid = substr($dataid, 0, $pos);
}
// Is this a valid paste identifier?
if (!filter::is_valid_paste_id($dataid))
{
@ -487,6 +543,17 @@ class zerobin
{
$this->_error = self::GENERIC_ERROR;
}
if ($isJson)
{
if (strlen($this->_error))
{
$this->_return_message(1, $this->_error);
}
else
{
$this->_return_message(0, $dataid, array('messages' => $messages));
}
}
}
/**
@ -555,7 +622,7 @@ class zerobin
* @param bool $status
* @param string $message
* @param array $other
* @return string
* @return void
*/
private function _return_message($status, $message, $other = array())
{
@ -569,6 +636,6 @@ class zerobin
$result['id'] = $message;
}
$result += $other;
return json_encode($result);
$this->_json = json_encode($result);
}
}

View file

@ -437,6 +437,22 @@ class zerobinTest extends PHPUnit_Framework_TestCase
);
}
/**
* @runInSeparateProcess
*/
public function testReadJson()
{
$this->reset();
$this->_model->create(self::$pasteid, self::$paste);
$_SERVER['QUERY_STRING'] = self::$pasteid . '&json';
ob_start();
new zerobin;
$content = ob_get_contents();
$response = json_decode($content, true);
$this->assertEquals(0, $response['status'], 'outputs success status');
$this->assertEquals(array(self::$paste), $response['messages'], 'outputs data correctly');
}
/**
* @runInSeparateProcess
*/