Ver Fonte

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

El RIDO há 11 anos atrás
pai
commit
b25022e403
3 ficheiros alterados com 157 adições e 70 exclusões
  1. 26 22
      js/zerobin.js
  2. 115 48
      lib/zerobin.php
  3. 16 0
      tst/zerobin.php

+ 26 - 22
js/zerobin.js

@@ -213,11 +213,6 @@ function setElementText(element, text) {
  * @param array comments : Array of messages to display (items = array with keys ('data','meta')
  * @param array comments : Array of messages to display (items = array with keys ('data','meta')
  */
  */
 function displayMessages(key, comments) {
 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.
     try { // Try to decrypt the paste.
         var cleartext = zeroDecipher(key, comments[0].data);
         var cleartext = zeroDecipher(key, comments[0].data);
         if (cleartext == null) throw "password prompt canceled";
         if (cleartext == null) throw "password prompt canceled";
@@ -327,7 +322,7 @@ function send_comment(parentid) {
     showStatus('Sending comment...', spin=true);
     showStatus('Sending comment...', spin=true);
     var cipherdata = zeroCipher(pageKey(), $('#replymessage').val());
     var cipherdata = zeroCipher(pageKey(), $('#replymessage').val());
     var ciphernickname = '';
     var ciphernickname = '';
-    var nick=$('#nickname').val();
+    var nick = $('#nickname').val();
     if (nick != '' && nick != 'Optional nickname...') {
     if (nick != '' && nick != 'Optional nickname...') {
         ciphernickname = zeroCipher(pageKey(), nick);
         ciphernickname = zeroCipher(pageKey(), nick);
     }
     }
@@ -337,29 +332,38 @@ function send_comment(parentid) {
                          nickname: ciphernickname
                          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) {
         if (data.status == 0) {
             showStatus('Comment posted.');
             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
  * Send a new paste to server
  */
  */
@@ -407,7 +411,7 @@ function send_data() {
             if (typeof prettyPrint == 'function') prettyPrint();
             if (typeof prettyPrint == 'function') prettyPrint();
         }
         }
         else if (data.status==1) {
         else if (data.status==1) {
-            showError('Could not create paste: '+data.message);
+            showError('Could not create paste: ' + data.message);
         }
         }
         else {
         else {
             showError('Could not create paste.');
             showError('Could not create paste.');

+ 115 - 48
lib/zerobin.php

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

+ 16 - 0
tst/zerobin.php

@@ -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
      * @runInSeparateProcess
      */
      */