<?php
$VERSION='Alpha 0.11';
/*
ZeroBin - a zero-knowledge paste bin
Please see project page: http://sebsauvage.net/wiki/doku.php?id=php:zerobin
*/
if (version_compare(PHP_VERSION, '5.2.6') < 0) die('ZeroBin requires php 5.2.6 or above to work.');
/* Convert paste id to storage path.
eg. 'e3570978f9e4aa90' --> 'data/e3/57/'
*/
function dataid2path($dataid)
{
return 'data/'.substr($dataid,0,2).'/'.substr($dataid,2,2).'/';
}
// In case stupid admin has left magic_quotes enabled in php.ini:
if (get_magic_quotes_gpc())
{
function stripslashes_deep($value) { $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value); return $value; }
$_POST = array_map('stripslashes_deep', $_POST);
$_GET = array_map('stripslashes_deep', $_GET);
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
}
if (!empty($_POST['data'])) // Create new paste
{
header('Content-type: application/json');
// Create storage directory if it does not exist.
if (!is_dir('data'))
{
mkdir('data',0705);
chmod('data',0705);
file_put_contents('data/.htaccess',"Allow from none\nDeny from all\n");
}
// Make sure last paste from the IP address was more than 10 seconds ago.
$tfilename='trafic_limiter.php';
if (!is_file($tfilename))
{
file_put_contents($tfilename,"<?php\n\$GLOBALS['trafic_limiter']=array();\n?>");
chmod($tfilename,0705);
}
include $tfilename;
$tl=$GLOBALS['trafic_limiter'];
$ip=$_SERVER['REMOTE_ADDR'];
if (!empty($tl[$ip]) && ($tl[$ip]+10>=time()))
{
echo json_encode(array('status'=>1,'message'=>'Please wait 10 seconds between each paste.'));
exit;
}
$tl[$ip]=time();
file_put_contents($tfilename, "<?php\n\$GLOBALS['trafic_limiter']=".var_export($tl,true).";\n?>");
$data = $_POST['data'];
// Make sure content is not too big.
if (strlen($data)>2000000)
{
echo json_encode(array('status'=>1,'message'=>'Paste is limited to 2 Mb of encrypted data.'));
exit;
}
// Make sure content is valid json
$decoded = json_decode($data);
if ($decoded==null)
{
echo json_encode(array('status'=>1,'message'=>'Data is not in json format.'));
exit;
}
$decoded = (array)$decoded;
// Make sure required fields are present and that they are base64 data.
$error = false;
foreach(array('iv','salt','ct') as $k)
{
if (!array_key_exists($k,$decoded)) { $error=true; }
if (base64_decode($decoded[$k],$strict=true)==null) { $error=true; }
}
if ($error)
{
echo json_encode(array('status'=>1,'message'=>'Invalid data.'));
exit;
}
// FIXME: Reject data if entropy is too low ?
// We want a small hash and avoid collisions: Truncated MD5 will do the trick:
$dataid = substr(hash('md5',$data),0,16);
// The $prefixdir system creates subdirectories in order to limit the number of files per directory.
// (A high number of files in a single directory can slow things down.)
// eg. "f468483c313401e8" will be stored in "data/f4/68/f468483c313401e8"
// High-trafic websites may want to deepen the directory structure (like Squid does).
$storagedir = dataid2path($dataid);
if (!is_dir($storagedir)) mkdir($storagedir,$mode=0705,$recursive=true);
if (is_file($storagedir.$dataid)) // Oups... improbable collision.
{
echo json_encode(array('status'=>1,'message'=>'You are unlucky. Try again.'));
exit;
}
// Write expiration date if required
if (!empty($_POST['expire']))
{
$expire=$_POST['expire'];
$d = null;
if ($expire=='10min') $d=time()+10*60;
elseif ($expire=='1hour') $d=time()+60*60;
elseif ($expire=='1day') $d=time()+24*60*60;
elseif ($expire=='1month') $d=time()+30*24*60*60; // Well this is not *exactly* one month, it's 30 days.
elseif ($expire=='1year') $d=time()+365*24*60*60;
if ($d!=null) file_put_contents($storagedir.$dataid.'.expire',$d);
}
file_put_contents($storagedir.$dataid,$data);
echo json_encode(array('status'=>0,'id'=>$dataid)); // 0 = no error
exit;
}
$CIPHERDATA='';
$ERRORMESSAGE='';
if (!empty($_SERVER['QUERY_STRING'])) // Display an existing paste.
{
$dataid = $_SERVER['QUERY_STRING'];
if (preg_match('/[a-z\d]{16}/',$dataid)) // Is this a valid paste identifier ?
{
$filename = dataid2path($dataid).$dataid;
if (is_file($filename)) // Check that paste exists.
{
// If expiration data is present, check if paste has expired.
$expire_filename=$filename.'.expire';
if (is_file($expire_filename))
{
$expire=intval(file_get_contents($expire_filename));
if ($expire<time())
{
unlink($filename); // Delete the files
unlink($expire_filename);
$ERRORMESSAGE='Paste does not exist or has expired.';
}
}
if ($ERRORMESSAGE=='') // If no error, return the paste.
{
$CIPHERDATA = file_get_contents($filename);
}
}
else
{
$ERRORMESSAGE='Paste does not exist or has expired.';
}
}
}
include "lib/rain.tpl.class.php";
header('Content-Type: text/html; charset=utf-8');
$page = new RainTPL;
$page->assign('CIPHERDATA',$CIPHERDATA);
$page->assign('VERSION',$VERSION);
$page->assign('ERRORMESSAGE',$ERRORMESSAGE);
$page->draw('page');
?>