diff --git a/lib/Controller.php b/lib/Controller.php index 810fd1a7..2acab806 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -199,50 +199,32 @@ class Controller TrafficLimiter::setConfiguration($this->_conf); if (!TrafficLimiter::canPass()) { return $this->_return_message( - 1, I18n::_( - 'Please wait %d seconds between each post.', - $this->_conf->getKey('limit', 'traffic') - ) - ); + 1, I18n::_( + 'Please wait %d seconds between each post.', + $this->_conf->getKey('limit', 'traffic') + ) + ); } - $data = $this->_request->getParam('data'); - $attachment = $this->_request->getParam('attachment'); - $attachmentname = $this->_request->getParam('attachmentname'); - // Ensure content is not too big. + $data = $this->_request->getData(); $sizelimit = $this->_conf->getKey('sizelimit'); - if ( - strlen($data) + strlen($attachment) + strlen($attachmentname) > $sizelimit - ) { + if (strlen($data['ct']) > $sizelimit) { return $this->_return_message( - 1, - I18n::_( - 'Paste is limited to %s of encrypted data.', - Filter::formatHumanReadableSize($sizelimit) - ) - ); - } - - // Ensure attachment did not get lost due to webserver limits or Suhosin - if (strlen($attachmentname) > 0 && strlen($attachment) == 0) { - return $this->_return_message(1, 'Attachment missing in data received by server. Please check your webserver or suhosin configuration for maximum POST parameter limitations.'); + 1, + I18n::_( + 'Paste is limited to %s of encrypted data.', + Filter::formatHumanReadableSize($sizelimit) + ) + ); } // The user posts a comment. - $pasteid = $this->_request->getParam('pasteid'); - $parentid = $this->_request->getParam('parentid'); - if (!empty($pasteid) && !empty($parentid)) { - $paste = $this->_model->getPaste($pasteid); + if (!empty($data['pasteid']) && !empty($data['parentid'])) { + $paste = $this->_model->getPaste($data['pasteid']); if ($paste->exists()) { try { - $comment = $paste->getComment($parentid); - - $nickname = $this->_request->getParam('nickname'); - if (!empty($nickname)) { - $comment->setNickname($nickname); - } - + $comment = $paste->getComment($data['parentid']); $comment->setData($data); $comment->store(); } catch (Exception $e) { @@ -259,34 +241,6 @@ class Controller $paste = $this->_model->getPaste(); try { $paste->setData($data); - - if (!empty($attachment)) { - $paste->setAttachment($attachment); - if (!empty($attachmentname)) { - $paste->setAttachmentName($attachmentname); - } - } - - $expire = $this->_request->getParam('expire'); - if (!empty($expire)) { - $paste->setExpiration($expire); - } - - $burnafterreading = $this->_request->getParam('burnafterreading'); - if (!empty($burnafterreading)) { - $paste->setBurnafterreading($burnafterreading); - } - - $opendiscussion = $this->_request->getParam('opendiscussion'); - if (!empty($opendiscussion)) { - $paste->setOpendiscussion($opendiscussion); - } - - $formatter = $this->_request->getParam('formatter'); - if (!empty($formatter)) { - $paste->setFormatter($formatter); - } - $paste->store(); } catch (Exception $e) { return $this->_return_message(1, $e->getMessage()); @@ -307,22 +261,17 @@ class Controller try { $paste = $this->_model->getPaste($dataid); if ($paste->exists()) { - // accessing this property ensures that the paste would be + // accessing this method ensures that the paste would be // deleted if it has already expired - $burnafterreading = $paste->isBurnafterreading(); + $paste->get(); if ( - ($burnafterreading && $deletetoken == 'burnafterreading') || // either we burn-after it has been read //@TODO: not needed anymore now? - Filter::slowEquals($deletetoken, $paste->getDeleteToken()) // or we manually delete it with this secret token + Filter::slowEquals($deletetoken, $paste->getDeleteToken()) ) { - // Paste exists and deletion token (if required) is valid: Delete the paste. + // Paste exists and deletion token is valid: Delete the paste. $paste->delete(); $this->_status = 'Paste was properly deleted.'; } else { - if (!$burnafterreading && $deletetoken == 'burnafterreading') { - $this->_error = 'Paste is not of burn-after-reading type.'; - } else { - $this->_error = 'Wrong deletion token. Paste was not deleted.'; - } + $this->_error = 'Wrong deletion token. Paste was not deleted.'; } } else { $this->_error = self::GENERIC_ERROR; @@ -355,8 +304,8 @@ class Controller $paste = $this->_model->getPaste($dataid); if ($paste->exists()) { $data = $paste->get(); - if (property_exists($data->meta, 'salt')) { - unset($data->meta->salt); + if (array_key_exists('salt', $data['meta'])) { + unset($data['meta']['salt']); } $this->_return_message(0, $dataid, (array) $data); } else { diff --git a/lib/Data/AbstractData.php b/lib/Data/AbstractData.php index b8dcdc2a..cc726aaa 100644 --- a/lib/Data/AbstractData.php +++ b/lib/Data/AbstractData.php @@ -163,7 +163,7 @@ abstract class AbstractData /** * Get next free slot for comment from postdate. * - * @access public + * @access protected * @param array $comments * @param int|string $postdate * @return int|string @@ -180,4 +180,25 @@ abstract class AbstractData } return $postdate; } + + /** + * Upgrade pre-version 1 pastes with attachment to version 1 format. + * + * @access protected + * @static + * @param array $paste + * @return array + */ + protected static function upgradePreV1Format(array $paste) + { + if (array_key_exists('attachment', $paste['meta'])) { + $paste['attachment'] = $paste['meta']['attachment']; + unset($paste['meta']['attachment']); + if (array_key_exists('attachmentname', $paste['meta'])) { + $paste['attachmentname'] = $paste['meta']['attachmentname']; + unset($paste['meta']['attachmentname']); + } + } + return $paste; + } } diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 1baf0dae..18efecc4 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -249,12 +249,12 @@ class Database extends AbstractData list($createdKey) = self::_getVersionedKeys(1); } - $meta = json_decode($paste['meta'], true); - if (!is_array($meta)) { - $meta = array(); + $paste['meta'] = json_decode($paste['meta'], true); + if (!is_array($paste['meta'])) { + $paste['meta'] = array(); } - - self::$_cache[$pasteid]['meta'] = $meta; + $paste = self::upgradePreV1Format($paste); + self::$_cache[$pasteid]['meta'] = $paste['meta']; self::$_cache[$pasteid]['meta'][$createdKey] = (int) $paste['postdate']; $expire_date = (int) $paste['expiredate']; if ($expire_date > 0) { @@ -264,17 +264,8 @@ class Database extends AbstractData return self::$_cache[$pasteid]; } - // support pre v1 attachments - if (array_key_exists('attachment', $meta)) { - self::$_cache[$pasteid]['attachment'] = $meta['attachment']; - unset(self::$_cache[$pasteid]['meta']['attachment']); - if (array_key_exists('attachmentname', $meta)) { - self::$_cache[$pasteid]['attachmentname'] = $meta['attachmentname']; - unset(self::$_cache[$pasteid]['meta']['attachmentname']); - } - } // support v1 attachments - elseif (array_key_exists('attachment', $paste) && strlen($paste['attachment'])) { + if (array_key_exists('attachment', $paste) && strlen($paste['attachment'])) { self::$_cache[$pasteid]['attachment'] = $paste['attachment']; if (array_key_exists('attachmentname', $paste) && strlen($paste['attachmentname'])) { self::$_cache[$pasteid]['attachmentname'] = $paste['attachmentname']; diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index 9596d02a..6df51207 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -71,23 +71,16 @@ class Filesystem extends AbstractData * * @access public * @param string $pasteid - * @return \stdClass|false + * @return array|false */ public function read($pasteid) { if (!$this->exists($pasteid)) { return false; } - $paste = DataStore::get(self::_dataid2path($pasteid) . $pasteid . '.php'); - if (property_exists($paste->meta, 'attachment')) { - $paste->attachment = $paste->meta->attachment; - unset($paste->meta->attachment); - if (property_exists($paste->meta, 'attachmentname')) { - $paste->attachmentname = $paste->meta->attachmentname; - unset($paste->meta->attachmentname); - } - } - return $paste; + return self::upgradePreV1Format( + DataStore::get(self::_dataid2path($pasteid) . $pasteid . '.php') + ); } /** diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php index cb364eb0..f7f919d6 100644 --- a/lib/Model/Paste.php +++ b/lib/Model/Paste.php @@ -173,13 +173,31 @@ class Paste extends AbstractModel */ public function getDeleteToken() { - if (!property_exists($this->_data->meta, 'salt')) { + if (!array_key_exists('salt', $this->_data['meta'])) { $this->get(); } return hash_hmac( $this->_conf->getKey('zerobincompatibility') ? 'sha1' : 'sha256', $this->getId(), - $this->_data->meta->salt + $this->_data['meta']['salt'] + ); + } + + /** + * Check if paste is of burn-after-reading type. + * + * @access public + * @throws Exception + * @return bool + */ + public function isBurnafterreading() + { + if (!array_key_exists('adata', $this->_data) && !array_key_exists('data', $this->_data)) { + $this->get(); + } + return ( + (array_key_exists('adata', $this->_data) && $this->_data['adata'][3] === 1) || + (array_key_exists('burnafterreading', $this->_data['meta']) && $this->_data['meta']['burnafterreading']) ); } @@ -198,7 +216,7 @@ class Paste extends AbstractModel return ( (array_key_exists('adata', $this->_data) && $this->_data['adata'][2] === 1) || (array_key_exists('opendiscussion', $this->_data['meta']) && $this->_data['meta']['opendiscussion']) - ); + ); } /** diff --git a/lib/Persistence/DataStore.php b/lib/Persistence/DataStore.php index 5307811e..cf88fc21 100644 --- a/lib/Persistence/DataStore.php +++ b/lib/Persistence/DataStore.php @@ -62,7 +62,7 @@ class DataStore extends AbstractPersistence */ public static function get($filename) { - return json_decode(substr(file_get_contents($filename), strlen(self::PROTECTION_LINE . PHP_EOL))); + return json_decode(substr(file_get_contents($filename), strlen(self::PROTECTION_LINE . PHP_EOL)), true); } /** diff --git a/lib/Request.php b/lib/Request.php index 3e6e902e..7e41f4aa 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -126,8 +126,8 @@ class Request // prepare operation, depending on current parameters if ( - (array_key_exists('data', $this->_params) && !empty($this->_params['data'])) || - (array_key_exists('attachment', $this->_params) && !empty($this->_params['attachment'])) + array_key_exists('ct', $this->_params) && + !empty($this->_params['ct']) ) { $this->_operation = 'create'; } elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) { @@ -152,6 +152,33 @@ class Request return $this->_operation; } + /** + * Get data of paste or comment + * + * @access public + * @return array + */ + public function getData() + { + $data = array( + 'adata' => json_decode($this->getParam('adata', '[]'), true) + ); + $required_keys = array('v', 'ct'); + $meta = $this->getParam('meta'); + if (empty($meta)) { + $required_keys[] = 'pasteid'; + $required_keys[] = 'parentid'; + } else { + $data['meta'] = json_decode($meta, true); + } + foreach ($required_keys as $key) { + $data[$key] = $this->getParam($key); + } + // forcing a cast to int or float + $data['v'] = $data['v'] + 0; + return $data; + } + /** * Get a request parameter * diff --git a/tst/Bootstrap.php b/tst/Bootstrap.php index c9f7e96a..d2cfc094 100644 --- a/tst/Bootstrap.php +++ b/tst/Bootstrap.php @@ -176,7 +176,8 @@ class Helper public static function getPastePost() { $example = self::getPaste(); - $example['meta'] = array('expire' => $example['meta']['expire']); + $example['adata'] = json_encode($example['adata']); + $example['meta'] = json_encode(array('expire' => $example['meta']['expire'])); return $example; } diff --git a/tst/FormatV2Test.php b/tst/FormatV2Test.php index c42c117d..020b1ce0 100644 --- a/tst/FormatV2Test.php +++ b/tst/FormatV2Test.php @@ -6,10 +6,13 @@ class FormatV2Test extends PHPUnit_Framework_TestCase { public function testFormatV2ValidatorValidatesCorrectly() { - $this->assertTrue(FormatV2::isValid(Helper::getPaste()), 'valid format'); - $this->assertTrue(FormatV2::isValid(Helper::getComment(), true), 'valid format'); - $paste = Helper::getPaste(); + $paste['meta'] = array('expire' => $paste['meta']['expire']); + $this->assertTrue(FormatV2::isValid($paste), 'valid format'); + $comment = Helper::getComment(); + unset($comment['meta']); + $this->assertTrue(FormatV2::isValid($comment, true), 'valid format'); + $paste['adata'][0][0] = '$'; $this->assertFalse(FormatV2::isValid($paste), 'invalid base64 encoding of iv'); diff --git a/tst/JsonApiTest.php b/tst/JsonApiTest.php index 6914d3fb..dc8988d8 100644 --- a/tst/JsonApiTest.php +++ b/tst/JsonApiTest.php @@ -48,7 +48,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase $options = parse_ini_file(CONF, true); $options['traffic']['limit'] = 0; Helper::createIniFile(CONF, $options); - $_POST = Helper::getPaste(); + $_POST = Helper::getPastePost(); $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest'; $_SERVER['REQUEST_METHOD'] = 'POST'; $_SERVER['REMOTE_ADDR'] = '::1'; @@ -62,7 +62,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase $this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data'); $paste = $this->_model->read($response['id']); $this->assertEquals( - hash_hmac('sha256', $response['id'], $paste->meta->salt), + hash_hmac('sha256', $response['id'], $paste['meta']['salt']), $response['deletetoken'], 'outputs valid delete token' ); @@ -76,8 +76,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase $options = parse_ini_file(CONF, true); $options['traffic']['limit'] = 0; Helper::createIniFile(CONF, $options); - $paste = Helper::getPaste(); - unset($paste['meta']); + $paste = Helper::getPastePost(); $file = tempnam(sys_get_temp_dir(), 'FOO'); file_put_contents($file, http_build_query($paste)); Request::setInputStream($file); @@ -98,7 +97,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase $this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data'); $paste = $this->_model->read($response['id']); $this->assertEquals( - hash_hmac('sha256', $response['id'], $paste->meta->salt), + hash_hmac('sha256', $response['id'], $paste['meta']['salt']), $response['deletetoken'], 'outputs valid delete token' ); @@ -114,7 +113,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase $paste = $this->_model->read(Helper::getPasteId()); $file = tempnam(sys_get_temp_dir(), 'FOO'); file_put_contents($file, http_build_query(array( - 'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt), + 'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']), ))); Request::setInputStream($file); $_SERVER['QUERY_STRING'] = Helper::getPasteId(); @@ -141,7 +140,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase $paste = $this->_model->read(Helper::getPasteId()); $_POST = array( 'pasteid' => Helper::getPasteId(), - 'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt), + 'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']), ); $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest'; $_SERVER['REQUEST_METHOD'] = 'POST'; @@ -159,11 +158,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase */ public function testRead() { - $paste = Helper::getPasteWithAttachment(); - $paste['meta']['attachment'] = $paste['attachment']; - $paste['meta']['attachmentname'] = $paste['attachmentname']; - unset($paste['attachment']); - unset($paste['attachmentname']); + $paste = Helper::getPaste(); $this->_model->create(Helper::getPasteId(), $paste); $_SERVER['QUERY_STRING'] = Helper::getPasteId(); $_GET[Helper::getPasteId()] = ''; @@ -176,12 +171,8 @@ class JsonApiTest extends PHPUnit_Framework_TestCase $this->assertEquals(0, $response['status'], 'outputs success status'); $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly'); $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste'); - $this->assertEquals($paste['data'], $response['data'], 'outputs data correctly'); - $this->assertEquals($paste['meta']['attachment'], $response['attachment'], 'outputs attachment correctly'); - $this->assertEquals($paste['meta']['attachmentname'], $response['attachmentname'], 'outputs attachmentname correctly'); - $this->assertEquals($paste['meta']['formatter'], $response['meta']['formatter'], 'outputs format correctly'); - $this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly'); - $this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly'); + $this->assertEquals($paste['ct'], $response['ct'], 'outputs data correctly'); + $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs postdate correctly'); $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly'); $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly'); } @@ -191,7 +182,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase */ public function testJsonLdPaste() { - $paste = Helper::getPasteWithAttachment(); + $paste = Helper::getPaste(); $this->_model->create(Helper::getPasteId(), $paste); $_GET['jsonld'] = 'paste'; ob_start(); @@ -210,7 +201,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase */ public function testJsonLdComment() { - $paste = Helper::getPasteWithAttachment(); + $paste = Helper::getPaste(); $this->_model->create(Helper::getPasteId(), $paste); $_GET['jsonld'] = 'comment'; ob_start(); @@ -229,7 +220,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase */ public function testJsonLdPasteMeta() { - $paste = Helper::getPasteWithAttachment(); + $paste = Helper::getPaste(); $this->_model->create(Helper::getPasteId(), $paste); $_GET['jsonld'] = 'pastemeta'; ob_start(); @@ -248,7 +239,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase */ public function testJsonLdCommentMeta() { - $paste = Helper::getPasteWithAttachment(); + $paste = Helper::getPaste(); $this->_model->create(Helper::getPasteId(), $paste); $_GET['jsonld'] = 'commentmeta'; ob_start(); @@ -267,7 +258,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase */ public function testJsonLdInvalid() { - $paste = Helper::getPasteWithAttachment(); + $paste = Helper::getPaste(); $this->_model->create(Helper::getPasteId(), $paste); $_GET['jsonld'] = CONF; ob_start();