Exemplo n.º 1
0
 /**
  * set configuration options of the traffic limiter
  *
  * @access public
  * @static
  * @param configuration $conf
  * @return void
  */
 public static function setConfiguration(configuration $conf)
 {
     self::setLimit($conf->getKey('limit', 'traffic'));
     self::setPath($conf->getKey('dir', 'traffic'));
     if (($option = $conf->getKey('header', 'traffic')) !== null) {
         $httpHeader = 'HTTP_' . $option;
         if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
             self::$_ipKey = $httpHeader;
         }
     }
 }
Exemplo n.º 2
0
 public function testTrafficGetsLimited()
 {
     $this->assertEquals($this->_path, trafficlimiter::getPath());
     $file = 'baz';
     $this->assertEquals($this->_path . DIRECTORY_SEPARATOR . $file, trafficlimiter::getPath($file));
     trafficlimiter::setLimit(4);
     $this->assertTrue(trafficlimiter::canPass('127.0.0.1'), 'first request may pass');
     sleep(2);
     $this->assertFalse(trafficlimiter::canPass('127.0.0.1'), 'second request is to fast, may not pass');
     sleep(3);
     $this->assertTrue(trafficlimiter::canPass('127.0.0.1'), 'third request waited long enough and may pass');
     $this->assertTrue(trafficlimiter::canPass('2001:1620:2057:dead:beef::cafe:babe'), 'fourth request has different ip and may pass');
     $this->assertFalse(trafficlimiter::canPass('127.0.0.1'), 'fifth request is to fast, may not pass');
 }
Exemplo n.º 3
0
 /**
  * Store new paste or comment
  *
  * POST contains:
  * data (mandatory) = json encoded SJCL encrypted text (containing keys: iv,salt,ct)
  *
  * All optional data will go to meta information:
  * expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never)
  * opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
  * nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,salt,ct)
  * parentid (optional) = in discussion, which comment this comment replies to.
  * pasteid (optional) = in discussion, which paste this comment belongs to.
  *
  * @access private
  * @param  string $data
  * @return void
  */
 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'])) {
         $this->_return_message(1, 'Please wait ' . $this->_conf['traffic']['limit'] . ' seconds between each post.');
     }
     // Make sure content is not too big.
     $sizelimit = (int) $this->_getMainConfig('sizelimit', 2097152);
     if (strlen($data) > $sizelimit) {
         $this->_return_message(1, 'Paste is limited to ' . filter::size_humanreadable($sizelimit) . ' of encrypted data.');
     }
     // Make sure format is correct.
     if (!sjcl::isValid($data)) {
         $this->_return_message(1, 'Invalid data.');
     }
     // Read additional meta-information.
     $meta = array();
     // Read expiration date
     if (!empty($_POST['expire'])) {
         $selected_expire = (string) $_POST['expire'];
         if (array_key_exists($selected_expire, $this->_conf['expire_options'])) {
             $expire = $this->_conf['expire_options'][$selected_expire];
         } else {
             $expire = $this->_conf['expire_options'][$this->_conf['expire']['default']];
         }
         if ($expire > 0) {
             $meta['expire_date'] = time() + $expire;
         }
     }
     // Destroy the paste when it is read.
     if (!empty($_POST['burnafterreading'])) {
         $burnafterreading = $_POST['burnafterreading'];
         if ($burnafterreading !== '0') {
             if ($burnafterreading !== '1') {
                 $error = true;
             }
             $meta['burnafterreading'] = true;
         }
     }
     // Read open discussion flag.
     if ($this->_conf['main']['opendiscussion'] && !empty($_POST['opendiscussion'])) {
         $opendiscussion = $_POST['opendiscussion'];
         if ($opendiscussion !== '0') {
             if ($opendiscussion !== '1') {
                 $error = true;
             }
             $meta['opendiscussion'] = true;
         }
     }
     // You can't have an open discussion on a "Burn after reading" paste:
     if (isset($meta['burnafterreading'])) {
         unset($meta['opendiscussion']);
     }
     // Optional nickname for comments
     if (!empty($_POST['nickname'])) {
         // Generation of the anonymous avatar (Vizhash):
         // If a nickname is provided, we generate a Vizhash.
         // (We assume that if the user did not enter a nickname, he/she wants
         // to be anonymous and we will not generate the vizhash.)
         $nick = $_POST['nickname'];
         if (!sjcl::isValid($nick)) {
             $error = true;
         } else {
             $meta['nickname'] = $nick;
             $vz = new vizhash16x16();
             $pngdata = $vz->generate($_SERVER['REMOTE_ADDR']);
             if ($pngdata != '') {
                 $meta['vizhash'] = 'data:image/png;base64,' . base64_encode($pngdata);
             }
             // Once the avatar is generated, we do not keep the IP address, nor its hash.
         }
     }
     if ($error) {
         $this->_return_message(1, 'Invalid data.');
     }
     // Add post date to meta.
     $meta['postdate'] = time();
     // We just want a small hash to avoid collisions:
     // Half-MD5 (64 bits) will do the trick
     $dataid = substr(hash('md5', $data), 0, 16);
     $storage = array('data' => $data);
     // Add meta-information only if necessary.
     if (count($meta)) {
         $storage['meta'] = $meta;
     }
     // The user posts a comment.
     if (!empty($_POST['parentid']) && !empty($_POST['pasteid'])) {
         $pasteid = (string) $_POST['pasteid'];
         $parentid = (string) $_POST['parentid'];
         if (!filter::is_valid_paste_id($pasteid) || !filter::is_valid_paste_id($parentid)) {
             $this->_return_message(1, 'Invalid data.');
         }
         // Comments do not expire (it's the paste that expires)
         unset($storage['expire_date']);
         unset($storage['opendiscussion']);
         // Make sure paste exists.
         if (!$this->_model()->exists($pasteid)) {
             $this->_return_message(1, 'Invalid data.');
         }
         // Make sure the discussion is opened in this paste.
         $paste = $this->_model()->read($pasteid);
         if (!$paste->meta->opendiscussion) {
             $this->_return_message(1, 'Invalid data.');
         }
         // Check for improbable collision.
         if ($this->_model()->existsComment($pasteid, $parentid, $dataid)) {
             $this->_return_message(1, 'You are unlucky. Try again.');
         }
         // New comment
         if ($this->_model()->createComment($pasteid, $parentid, $dataid, $storage) === false) {
             $this->_return_message(1, 'Error saving comment. Sorry.');
         }
         // 0 = no error
         $this->_return_message(0, $dataid);
     } else {
         // Check for improbable collision.
         if ($this->_model()->exists($dataid)) {
             $this->_return_message(1, 'You are unlucky. Try again.');
         }
         // New paste
         if ($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=<pasteid>&deletetoken=<deletetoken>
         $deletetoken = hash_hmac('sha1', $dataid, serversalt::get());
         // 0 = no error
         $this->_return_message(0, $dataid, array('deletetoken' => $deletetoken));
     }
     $this->_return_message(1, 'Server error.');
 }
Exemplo n.º 4
0
 /**
  * set the time limit in seconds
  *
  * @access public
  * @static
  * @param  int $limit
  * @return void
  */
 public static function setLimit($limit)
 {
     self::$_limit = $limit;
 }
Exemplo n.º 5
0
 /**
  * Set nickname.
  *
  * @access public
  * @param string $nickname
  * @throws Exception
  * @return void
  */
 public function setNickname($nickname)
 {
     if (!sjcl::isValid($nickname)) {
         throw new Exception('Invalid data.', 66);
     }
     $this->_data->meta->nickname = $nickname;
     // Generation of the anonymous avatar (Vizhash):
     // If a nickname is provided, we generate a Vizhash.
     // (We assume that if the user did not enter a nickname, he/she wants
     // to be anonymous and we will not generate the vizhash.)
     $vh = new vizhash16x16();
     $pngdata = $vh->generate(trafficlimiter::getIp());
     if ($pngdata != '') {
         $this->_data->meta->vizhash = 'data:image/png;base64,' . base64_encode($pngdata);
     }
     // Once the avatar is generated, we do not keep the IP address, nor its hash.
 }
Exemplo n.º 6
0
 /**
  * Store new paste or comment
  *
  * POST contains one or both:
  * data = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
  * attachment = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
  *
  * All optional data will go to meta information:
  * expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never)
  * formatter (optional) = format to display the paste as (plaintext,syntaxhighlighting,markdown) (default:syntaxhighlighting)
  * burnafterreading (optional) = if this paste may only viewed once ? (0/1) (default:0)
  * opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
  * attachmentname = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
  * nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
  * parentid (optional) = in discussion, which comment this comment replies to.
  * pasteid (optional) = in discussion, which paste this comment belongs to.
  *
  * @access private
  * @return string
  */
 private function _create()
 {
     $error = false;
     // Ensure last paste from visitors IP address was more than configured amount of seconds ago.
     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')));
     }
     $data = $this->_request->getParam('data');
     $attachment = $this->_request->getParam('attachment');
     $attachmentname = $this->_request->getParam('attachmentname');
     // Ensure content is not too big.
     $sizelimit = $this->_conf->getKey('sizelimit');
     if (strlen($data) + strlen($attachment) + strlen($attachmentname) > $sizelimit) {
         return $this->_return_message(1, i18n::_('Paste is limited to %s of encrypted data.', filter::size_humanreadable($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 ($paste->exists()) {
             try {
                 $comment = $paste->getComment($parentid);
                 $nickname = $this->_request->getParam('nickname');
                 if (!empty($nickname)) {
                     $comment->setNickname($nickname);
                 }
                 $comment->setData($data);
                 $comment->store();
             } catch (Exception $e) {
                 return $this->_return_message(1, $e->getMessage());
             }
             $this->_return_message(0, $comment->getId());
         } else {
             $this->_return_message(1, 'Invalid data.');
         }
     } else {
         $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());
         }
         $this->_return_message(0, $paste->getId(), array('deletetoken' => $paste->getDeleteToken()));
     }
 }
Exemplo n.º 7
0
 /**
  * @runInSeparateProcess
  */
 public function testCreateInvalidTimelimit()
 {
     $this->reset();
     $_POST = helper::getPaste();
     $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
     $_SERVER['REQUEST_METHOD'] = 'POST';
     $_SERVER['REMOTE_ADDR'] = '::1';
     trafficlimiter::canPass();
     ob_start();
     new zerobin();
     $content = ob_get_contents();
     $response = json_decode($content, true);
     $this->assertEquals(1, $response['status'], 'outputs error status');
     $this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
 }