implemented zerobin_db model, added more options for paste expiration, made comments and max data size configurable

This commit is contained in:
Simon Rupf
2012-05-19 23:59:41 +02:00
parent 7cee995cd7
commit 421e6cba97
7 changed files with 432 additions and 49 deletions

View File

@@ -103,8 +103,8 @@ class zerobin
);
}
$this->_conf = parse_ini_file(PATH . 'cfg/conf.ini');
$this->_model = $this->_conf['model'];
$this->_conf = parse_ini_file(PATH . 'cfg/conf.ini', true);
$this->_model = $this->_conf['model']['class'];
}
/**
@@ -117,7 +117,10 @@ class zerobin
{
// if needed, initialize the model
if(is_string($this->_model)) {
$this->_model = forward_static_call(array($this->_model, 'getInstance'), $this->_conf['model_options']);
$this->_model = forward_static_call(
array($this->_model, 'getInstance'),
$this->_conf['model_options']
);
}
return $this->_model;
}
@@ -129,7 +132,7 @@ class zerobin
* 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,10min,1hour,1day,1month,1year,burn) (default:never)
* 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.
@@ -143,18 +146,30 @@ class zerobin
header('Content-type: application/json');
$error = false;
// Make sure last paste from the IP address was more than 10 seconds ago.
trafficlimiter::setLimit($this->_conf['traffic_limit']);
trafficlimiter::setPath($this->_conf['traffic_dir']);
// 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 10 seconds between each post.');
) $this->_return_message(
1,
'Please wait ' .
$this->_conf['traffic']['limit'] .
' seconds between each post.'
);
// Make sure content is not too big.
$data = $_POST['data'];
if (
strlen($data) > 2000000
) $this->_return_message(1, 'Paste is limited to 2 MB of encrypted data.');
strlen($data) > $this->_conf['main']['sizelimit']
) $this->_return_message(
1,
'Paste is limited to ' .
$this->_conf['main']['sizelimit'] .
' ' .
filter::size_humanreadable($this->_conf['main']['sizelimit']) .
' of encrypted data.'
);
// Make sure format is correct.
if (!sjcl::isValid($data)) $this->_return_message(1, 'Invalid data.');
@@ -167,6 +182,12 @@ class zerobin
{
switch ($_POST['expire'])
{
case 'burn':
$meta['burnafterreading'] = true;
break;
case '5min':
$meta['expire_date'] = time()+5*60;
break;
case '10min':
$meta['expire_date'] = time()+10*60;
break;
@@ -176,19 +197,19 @@ class zerobin
case '1day':
$meta['expire_date'] = time()+24*60*60;
break;
case '1week':
$meta['expire_date'] = time()+7*24*60*60;
break;
case '1month':
$meta['expire_date'] = strtotime('+1 month');
break;
case '1year':
$meta['expire_date'] = strtotime('+1 year');
break;
case 'burn':
$meta['burnafterreading'] = true;
}
}
// Read open discussion flag.
if (!empty($_POST['opendiscussion']))
if ($this->_conf['main']['opendiscussion'] && !empty($_POST['opendiscussion']))
{
$opendiscussion = $_POST['opendiscussion'];
if ($opendiscussion != 0)
@@ -381,6 +402,7 @@ class zerobin
// We escape it here because ENT_NOQUOTES can't be used in RainTPL templates.
$page->assign('CIPHERDATA', htmlspecialchars($this->_data, ENT_NOQUOTES));
$page->assign('ERRORMESSAGE', $this->_error);
$page->assign('OPENDISCUSSION', $this->_conf['main']['opendiscussion']);
$page->assign('VERSION', self::VERSION);
$page->draw('page');
}

View File

@@ -49,7 +49,7 @@ abstract class zerobin_abstract
*
* @access public
* @static
* @return zerobin
* @return zerobin_abstract
*/
abstract public static function getInstance($options);

View File

@@ -29,9 +29,9 @@ class zerobin_data extends zerobin_abstract
*
* @access public
* @static
* @return zerobin
* @return zerobin_data
*/
public static function getInstance($options)
public static function getInstance($options = null)
{
// if given update the data directory
if (

View File

@@ -17,6 +17,13 @@
*/
class zerobin_db extends zerobin_abstract
{
/*
* @access private
* @static
* @var array to cache select queries
*/
private static $_cache = array();
/*
* @access private
* @static
@@ -24,31 +31,137 @@ class zerobin_db extends zerobin_abstract
*/
private static $_db;
/*
* @access private
* @static
* @var string table prefix
*/
private static $_prefix = '';
/*
* @access private
* @static
* @var string database type
*/
private static $_type = '';
/**
* get instance of singleton
*
* @access public
* @static
* @return zerobin
* @throws Exception
* @return zerobin_db
*/
public static function getInstance($options)
public static function getInstance($options = null)
{
// if needed initialize the singleton
if(null === self::$_instance) {
parent::$_instance = new self;
}
if (
is_array($options) &&
array_key_exists('dsn', $options) &&
array_key_exists('usr', $options) &&
array_key_exists('pwd', $options) &&
array_key_exists('opt', $options)
) self::$_db = new PDO(
$options['dsn'],
$options['usr'],
$options['pwd'],
$options['opt']
);
if (is_array($options))
{
// set table prefix if given
if (array_key_exists('tbl', $options)) self::$_prefix = $options['tbl'];
// initialize the db connection with new options
if (
array_key_exists('dsn', $options) &&
array_key_exists('usr', $options) &&
array_key_exists('pwd', $options) &&
array_key_exists('opt', $options)
)
{
self::$_db = new PDO(
$options['dsn'],
$options['usr'],
$options['pwd'],
$options['opt']
);
// check if the database contains the required tables
self::$_type = strtolower(
substr($options['dsn'], 0, strpos($options['dsn'], ':'))
);
switch(self::$_type)
{
case 'ibm':
$sql = 'SELECT tabname FROM SYSCAT.TABLES ';
break;
case 'informix':
$sql = 'SELECT tabname FROM systables ';
break;
case 'mssql':
$sql = "SELECT name FROM sysobjects "
. "WHERE type = 'U' ORDER BY name";
break;
case 'mysql':
$sql = 'SHOW TABLES';
break;
case 'oci':
$sql = 'SELECT table_name FROM all_tables';
break;
case 'pgsql':
$sql = "SELECT c.relname AS table_name "
. "FROM pg_class c, pg_user u "
. "WHERE c.relowner = u.usesysid AND c.relkind = 'r' "
. "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) "
. "AND c.relname !~ '^(pg_|sql_)' "
. "UNION "
. "SELECT c.relname AS table_name "
. "FROM pg_class c "
. "WHERE c.relkind = 'r' "
. "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) "
. "AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) "
. "AND c.relname !~ '^pg_'";
break;
case 'sqlite':
$sql = "SELECT name FROM sqlite_master WHERE type='table' "
. "UNION ALL SELECT name FROM sqlite_temp_master "
. "WHERE type='table' ORDER BY name";
break;
default:
throw new Exception(
'PDO type ' .
self::$_type .
' is currently not supported.'
);
}
$statement = self::$_db->query($sql);
$tables = $statement->fetchAll(PDO::FETCH_COLUMN, 0);
// create paste table if needed
if (!array_key_exists(self::$_prefix . 'paste', $tables))
{
self::$_db->exec(
'CREATE TABLE ' . self::$_prefix . 'paste ( ' .
'dataid CHAR(16), ' .
'data TEXT, ' .
'postdate INT, ' .
'expiredate INT, ' .
'opendiscussion INT, ' .
'burnafterreading INT );'
);
}
// create comment table if needed
if (!array_key_exists(self::$_prefix . 'comment', $tables))
{
self::$_db->exec(
'CREATE TABLE ' . self::$_prefix . 'comment ( ' .
'dataid CHAR(16), ' .
'pasteid CHAR(16), ' .
'parentid CHAR(16), ' .
'data TEXT, ' .
'nickname VARCHAR(255), ' .
'vizhash TEXT, ' .
'postdate INT );'
);
}
}
}
return parent::$_instance;
}
@@ -58,10 +171,27 @@ class zerobin_db extends zerobin_abstract
* @access public
* @param string $pasteid
* @param array $paste
* @return int|false
* @return bool
*/
public function create($pasteid, $paste)
{
if (
!array_key_exists('opendiscussion', $paste['meta'])
) $paste['meta']['opendiscussion'] = false;
if (
!array_key_exists('burnafterreading', $paste['meta'])
) $paste['meta']['burnafterreading'] = false;
return self::_exec(
'INSERT INTO ' . self::$_prefix . 'paste VALUES(?,?,?,?,?,?)',
array(
$pasteid,
$paste['data'],
$paste['meta']['postdate'],
$paste['meta']['expire_date'],
(int) $paste['meta']['opendiscussion'],
(int) $paste['meta']['burnafterreading'],
)
);
}
/**
@@ -73,6 +203,27 @@ class zerobin_db extends zerobin_abstract
*/
public function read($pasteid)
{
if (
!array_key_exists($pasteid, self::$_cache)
) self::$_cache[$pasteid] = self::_select(
'SELECT * FROM ' . self::$_prefix . 'paste WHERE dataid = ?',
array($pasteid), true
);
// create object
$paste = new stdClass;
$paste->data = self::$_cache[$pasteid]['data'];
$paste->meta = new stdClass;
$paste->meta->postdate = (int) self::$_cache[$pasteid]['postdate'];
$paste->meta->expire_date = (int) self::$_cache[$pasteid]['expiredate'];
if (
self::$_cache[$pasteid]['opendiscussion']
) $paste->meta->opendiscussion = true;
if (
self::$_cache[$pasteid]['burnafterreading']
) $paste->meta->burnafterreading = true;
return $paste;
}
/**
@@ -84,6 +235,14 @@ class zerobin_db extends zerobin_abstract
*/
public function delete($pasteid)
{
self::_exec(
'DELETE FROM ' . self::$_prefix . 'paste WHERE dataid = ?',
array($pasteid)
);
self::_exec(
'DELETE FROM ' . self::$_prefix . 'comment WHERE pasteid = ?',
array($pasteid)
);
}
/**
@@ -95,6 +254,13 @@ class zerobin_db extends zerobin_abstract
*/
public function exists($pasteid)
{
if (
!array_key_exists($pasteid, self::$_cache)
) self::$_cache[$pasteid] = self::_select(
'SELECT * FROM ' . self::$_prefix . 'paste WHERE dataid = ?',
array($pasteid), true
);
return (bool) self::$_cache[$pasteid];
}
/**
@@ -109,6 +275,18 @@ class zerobin_db extends zerobin_abstract
*/
public function createComment($pasteid, $parentid, $commentid, $comment)
{
return self::_exec(
'INSERT INTO ' . self::$_prefix . 'comment VALUES(?,?,?,?,?,?,?)',
array(
$pasteid,
$parentid,
$commentid,
$comment['data'],
$comment['meta']['nickname'],
$comment['meta']['vizhash'],
$comment['meta']['postdate'],
)
);
}
/**
@@ -120,6 +298,33 @@ class zerobin_db extends zerobin_abstract
*/
public function readComments($pasteid)
{
$rows = self::_select(
'SELECT * FROM ' . self::$_prefix . 'comment WHERE pasteid = ?',
array($pasteid)
);
// create object
$commentTemplate = new stdClass;
$commentTemplate->meta = new stdClass;
// create comment list
$comments = array();
if (count($rows))
{
foreach ($rows as $row)
{
$i = (int) $row['postdate'];
$comments[$i] = clone $commentTemplate;
$comments[$i]->data = $row['data'];
$comments[$i]->meta->nickname = $row['nickname'];
$comments[$i]->meta->vizhash = $row['vizhash'];
$comments[$i]->meta->postdate = $i;
$comments[$i]->meta->commentid = $row['dataid'];
$comments[$i]->meta->parentid = $row['parentid'];
}
ksort($comments);
}
return $comments;
}
/**
@@ -133,5 +338,50 @@ class zerobin_db extends zerobin_abstract
*/
public function existsComment($pasteid, $parentid, $commentid)
{
return (bool) self::_select(
'SELECT dataid FROM ' . self::$_prefix . 'comment ' .
'WHERE pasteid = ? AND parentid = ? AND dataid = ?',
array($pasteid, $parentid, $commentid), true
);
}
/**
* execute a statement
*
* @access private
* @static
* @param string $sql
* @param array $params
* @throws PDOException
* @return array
*/
private static function _exec($sql, array $params)
{
$statement = self::$_db->prepare($sql);
$result = $statement->execute($params);
$statement->closeCursor();
return $result;
}
/**
* run a select statement
*
* @access private
* @static
* @param string $sql
* @param array $params
* @param bool $firstOnly if only the first row should be returned
* @throws PDOException
* @return array
*/
private static function _select($sql, array $params, $firstOnly = false)
{
$statement = self::$_db->prepare($sql);
$statement->execute($params);
$result = $firstOnly ?
$statement->fetch(PDO::FETCH_ASSOC) :
$statement->fetchAll(PDO::FETCH_ASSOC);
$statement->closeCursor();
return $result;
}
}