Initial commit of version 0.15 alpha.
This commit is contained in:
commit
52630374e5
17 changed files with 4972 additions and 0 deletions
33
README
Normal file
33
README
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
ZeroBin 0.15 Alpha
|
||||||
|
=== THIS IS ALPHA SOFTWARE - USE AT YOUR OWN RISKS ====
|
||||||
|
|
||||||
|
ZeroBin is a minimalist, opensource online pastebin where the server
|
||||||
|
has zero knowledge of pasted data. Data is encrypted/decrypted in the
|
||||||
|
browser using 256 bits AES.
|
||||||
|
|
||||||
|
More information on the project page:
|
||||||
|
http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied warranty.
|
||||||
|
In no event will the authors be held liable for any damages arising from
|
||||||
|
the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must
|
||||||
|
not claim that you wrote the original software. If you use this
|
||||||
|
software in a product, an acknowledgment in the product documentation
|
||||||
|
would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must
|
||||||
|
not be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
339
index.php
Normal file
339
index.php
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
ZeroBin - a zero-knowledge paste bin
|
||||||
|
Please see project page: http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
|
*/
|
||||||
|
$VERSION='Alpha 0.15';
|
||||||
|
if (version_compare(PHP_VERSION, '5.2.6') < 0) die('ZeroBin requires php 5.2.6 or above to work. Sorry.');
|
||||||
|
require_once "lib/vizhash_gd_zero.php";
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// trafic_limiter : Make sure the IP address makes at most 1 request every 10 seconds.
|
||||||
|
// Will return false if IP address made a call less than 10 seconds ago.
|
||||||
|
function trafic_limiter_canPass($ip)
|
||||||
|
{
|
||||||
|
$tfilename='./data/trafic_limiter.php';
|
||||||
|
if (!is_file($tfilename))
|
||||||
|
{
|
||||||
|
file_put_contents($tfilename,"<?php\n\$GLOBALS['trafic_limiter']=array();\n?>");
|
||||||
|
chmod($tfilename,0705);
|
||||||
|
}
|
||||||
|
require $tfilename;
|
||||||
|
$tl=$GLOBALS['trafic_limiter'];
|
||||||
|
if (!empty($tl[$ip]) && ($tl[$ip]+10>=time()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
// FIXME: purge file of expired IPs to keep it small
|
||||||
|
}
|
||||||
|
$tl[$ip]=time();
|
||||||
|
file_put_contents($tfilename, "<?php\n\$GLOBALS['trafic_limiter']=".var_export($tl,true).";\n?>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert paste id to storage path.
|
||||||
|
The idea is to 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).
|
||||||
|
|
||||||
|
eg. input 'e3570978f9e4aa90' --> output 'data/e3/57/'
|
||||||
|
*/
|
||||||
|
function dataid2path($dataid)
|
||||||
|
{
|
||||||
|
return 'data/'.substr($dataid,0,2).'/'.substr($dataid,2,2).'/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert paste id to discussion storage path.
|
||||||
|
eg. 'e3570978f9e4aa90' --> 'data/e3/57/e3570978f9e4aa90.discussion/'
|
||||||
|
*/
|
||||||
|
function dataid2discussionpath($dataid)
|
||||||
|
{
|
||||||
|
return dataid2path($dataid).$dataid.'.discussion/';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a json string is a proper SJCL encrypted message.
|
||||||
|
// False if format is incorrect.
|
||||||
|
function validSJCL($jsonstring)
|
||||||
|
{
|
||||||
|
$accepted_keys=array('iv','salt','ct');
|
||||||
|
|
||||||
|
// Make sure content is valid json
|
||||||
|
$decoded = json_decode($jsonstring);
|
||||||
|
if ($decoded==null) return false;
|
||||||
|
$decoded = (array)$decoded;
|
||||||
|
|
||||||
|
// Make sure required fields are present and that they are base64 data.
|
||||||
|
foreach($accepted_keys as $k)
|
||||||
|
{
|
||||||
|
if (!array_key_exists($k,$decoded)) { return false; }
|
||||||
|
if (base64_decode($decoded[$k],$strict=true)==null) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure no additionnal keys were added.
|
||||||
|
if (count(array_intersect(array_keys($decoded),$accepted_keys))!=3) { return false; }
|
||||||
|
|
||||||
|
// FIXME: Reject data if entropy is too low ?
|
||||||
|
|
||||||
|
// Make sure some fields have a reasonable size.
|
||||||
|
if (strlen($decoded['iv'])>24) return false;
|
||||||
|
if (strlen($decoded['salt'])>14) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a paste and its discussion.
|
||||||
|
// Input: $pasteid : the paste identifier.
|
||||||
|
function deletePaste($pasteid)
|
||||||
|
{
|
||||||
|
// Delete the paste itself
|
||||||
|
unlink(dataid2path($pasteid).$pasteid);
|
||||||
|
|
||||||
|
// Delete discussion if it exists.
|
||||||
|
$discdir = dataid2discussionpath($pasteid);
|
||||||
|
if (is_dir($discdir))
|
||||||
|
{
|
||||||
|
// Delete all files in discussion directory
|
||||||
|
$dhandle = opendir($discdir);
|
||||||
|
while (false !== ($filename = readdir($dhandle)))
|
||||||
|
{
|
||||||
|
if (is_file($discdir.$filename)) unlink($discdir.$filename);
|
||||||
|
}
|
||||||
|
closedir($dhandle);
|
||||||
|
|
||||||
|
// Delete the discussion directory.
|
||||||
|
rmdir($discdir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($_POST['data'])) // Create new paste/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,10min,1hour,1day,1month,1year,burn) (default:never)
|
||||||
|
opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
|
||||||
|
nickname (optional) = son 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
header('Content-type: application/json');
|
||||||
|
$error = false;
|
||||||
|
|
||||||
|
// Create storage directory if it does not exist.
|
||||||
|
if (!is_dir('data'))
|
||||||
|
{
|
||||||
|
mkdir('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.
|
||||||
|
if (!trafic_limiter_canPass($_SERVER['REMOTE_ADDR']))
|
||||||
|
{ echo json_encode(array('status'=>1,'message'=>'Please wait 10 seconds between each post.')); exit; }
|
||||||
|
|
||||||
|
// Make sure content is not too big.
|
||||||
|
$data = $_POST['data'];
|
||||||
|
if (strlen($data)>2000000)
|
||||||
|
{ echo json_encode(array('status'=>1,'message'=>'Paste is limited to 2 Mb of encrypted data.')); exit; }
|
||||||
|
|
||||||
|
// Make sure format is correct.
|
||||||
|
if (!validSJCL($data))
|
||||||
|
{ echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
|
||||||
|
// Read additional meta-information.
|
||||||
|
$meta=array();
|
||||||
|
|
||||||
|
// Read expiration date
|
||||||
|
if (!empty($_POST['expire']))
|
||||||
|
{
|
||||||
|
$expire=$_POST['expire'];
|
||||||
|
if ($expire=='10min') $meta['expire_date']=time()+10*60;
|
||||||
|
elseif ($expire=='1hour') $meta['expire_date']=time()+60*60;
|
||||||
|
elseif ($expire=='1day') $meta['expire_date']=time()+24*60*60;
|
||||||
|
elseif ($expire=='1month') $meta['expire_date']=time()+30*24*60*60; // Well this is not *exactly* one month, it's 30 days.
|
||||||
|
elseif ($expire=='1year') $meta['expire_date']=time()+365*24*60*60;
|
||||||
|
elseif ($expire=='burn') $meta['burnafterreading']=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read open discussion flag
|
||||||
|
if (!empty($_POST['opendiscussion']))
|
||||||
|
{
|
||||||
|
$opendiscussion = $_POST['opendiscussion'];
|
||||||
|
if ($opendiscussion!='0' && $opendiscussion!='1') { $error=true; }
|
||||||
|
if ($opendiscussion!='0') { $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']))
|
||||||
|
{
|
||||||
|
$nick = $_POST['nickname'];
|
||||||
|
if (!validSJCL($nick))
|
||||||
|
{
|
||||||
|
$error=true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$meta['nickname']=$nick;
|
||||||
|
|
||||||
|
// 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.)
|
||||||
|
$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)
|
||||||
|
{
|
||||||
|
echo json_encode(array('status'=>1,'message'=>'Invalid data.'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
$is_comment = (!empty($_POST['parentid']) && !empty($_POST['pasteid'])); // Is this post a comment ?
|
||||||
|
$storage = array('data'=>$data);
|
||||||
|
if (count($meta)>0) $storage['meta'] = $meta; // Add meta-information only if necessary.
|
||||||
|
|
||||||
|
if ($is_comment) // The user posts a comment.
|
||||||
|
{
|
||||||
|
$pasteid = $_POST['pasteid'];
|
||||||
|
$parentid = $_POST['parentid'];
|
||||||
|
if (!preg_match('/[a-f\d]{16}/',$pasteid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
if (!preg_match('/[a-f\d]{16}/',$parentid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
|
||||||
|
unset($storage['expire_date']); // Comment do not expire (it's the paste that expires)
|
||||||
|
unset($storage['opendiscussion']);
|
||||||
|
|
||||||
|
// Make sure paste exists.
|
||||||
|
$storagedir = dataid2path($pasteid);
|
||||||
|
if (!is_file($storagedir.$pasteid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
|
||||||
|
// Make sure the discussion is opened in this paste.
|
||||||
|
$paste=json_decode(file_get_contents($storagedir.$pasteid));
|
||||||
|
if (!$paste->meta->opendiscussion) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
|
||||||
|
$discdir = dataid2discussionpath($pasteid);
|
||||||
|
$filename = $pasteid.'.'.$dataid.'.'.$parentid;
|
||||||
|
if (!is_dir($discdir)) mkdir($discdir,$mode=0705,$recursive=true);
|
||||||
|
if (is_file($discdir.$filename)) // Oups... improbable collision.
|
||||||
|
{
|
||||||
|
echo json_encode(array('status'=>1,'message'=>'You are unlucky. Try again.'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($discdir.$filename,json_encode($storage));
|
||||||
|
echo json_encode(array('status'=>0,'id'=>$dataid)); // 0 = no error
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
else // a standard paste.
|
||||||
|
{
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
// New paste
|
||||||
|
file_put_contents($storagedir.$dataid,json_encode($storage));
|
||||||
|
echo json_encode(array('status'=>0,'id'=>$dataid)); // 0 = no error
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(array('status'=>1,'message'=>'Server error.'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$CIPHERDATA='';
|
||||||
|
$ERRORMESSAGE='';
|
||||||
|
if (!empty($_SERVER['QUERY_STRING'])) // Display an existing paste.
|
||||||
|
{
|
||||||
|
$dataid = $_SERVER['QUERY_STRING'];
|
||||||
|
if (preg_match('/[a-f\d]{16}/',$dataid)) // Is this a valid paste identifier ?
|
||||||
|
{
|
||||||
|
$filename = dataid2path($dataid).$dataid;
|
||||||
|
if (is_file($filename)) // Check that paste exists.
|
||||||
|
{
|
||||||
|
// Get the paste itself.
|
||||||
|
$paste=json_decode(file_get_contents($filename));
|
||||||
|
|
||||||
|
// See if paste has expired.
|
||||||
|
if (isset($paste->meta->expire_date) && $paste->meta->expire_date<time())
|
||||||
|
{
|
||||||
|
deletePaste($dataid); // Delete the paste
|
||||||
|
$ERRORMESSAGE='Paste does not exist or has expired.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ERRORMESSAGE=='') // If no error, return the paste.
|
||||||
|
{
|
||||||
|
// We kindly provide the remaining time before expiration (in seconds)
|
||||||
|
if ($paste->meta->expire_date) $paste->meta->remaining_time = $paste->meta->expire_date - time();
|
||||||
|
|
||||||
|
$messages = array($paste); // The paste itself is the first in the list of encrypted messages.
|
||||||
|
// If it's a discussion, get all comments.
|
||||||
|
if ($paste->meta->opendiscussion)
|
||||||
|
{
|
||||||
|
$comments=array();
|
||||||
|
$datadir = dataid2discussionpath($dataid);
|
||||||
|
if (!is_dir($datadir)) mkdir($datadir,$mode=0705,$recursive=true);
|
||||||
|
$dhandle = opendir($datadir);
|
||||||
|
while (false !== ($filename = readdir($dhandle)))
|
||||||
|
{
|
||||||
|
if (is_file($datadir.$filename))
|
||||||
|
{
|
||||||
|
$comment=json_decode(file_get_contents($datadir.$filename));
|
||||||
|
// Filename is in the form pasteid.commentid.parentid:
|
||||||
|
// - pasteid is the paste this reply belongs to.
|
||||||
|
// - commentid is the comment identifier itself.
|
||||||
|
// - parentid is the comment this comment replies to (It can be pasteid)
|
||||||
|
$items=explode('.',$filename);
|
||||||
|
$comment->meta->commentid=$items[1]; // Add some meta information not contained in file.
|
||||||
|
$comment->meta->parentid=$items[2];
|
||||||
|
$comments[$comment->meta->postdate]=$comment; // Store in table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($dhandle);
|
||||||
|
ksort($comments); // Sort comments by date, oldest first.
|
||||||
|
$messages = array_merge($messages, $comments);
|
||||||
|
}
|
||||||
|
$CIPHERDATA = json_encode($messages);
|
||||||
|
|
||||||
|
// If the paste was meant to be read only once, delete it.
|
||||||
|
if ($paste->meta->burnafterreading) deletePaste($dataid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$ERRORMESSAGE='Paste does not exist or has expired.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
require_once "lib/rain.tpl.class.php";
|
||||||
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
|
$page = new RainTPL;
|
||||||
|
$page->assign('CIPHERDATA',htmlspecialchars($CIPHERDATA,ENT_NOQUOTES)); // We escape it here because ENT_NOQUOTES can't be used in RainTPL templates.
|
||||||
|
$page->assign('VERSION',$VERSION);
|
||||||
|
$page->assign('ERRORMESSAGE',$ERRORMESSAGE);
|
||||||
|
$page->draw('page');
|
||||||
|
?>
|
128
lib/base64.js
Normal file
128
lib/base64.js
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* $Id: base64.js,v 1.2 2011/12/27 14:34:49 dankogai Exp dankogai $
|
||||||
|
*
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(global){
|
||||||
|
|
||||||
|
if (global.Base64) return;
|
||||||
|
|
||||||
|
var b64chars
|
||||||
|
= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||||
|
var b64tab = function(bin){
|
||||||
|
var t = {};
|
||||||
|
for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i;
|
||||||
|
return t;
|
||||||
|
}(b64chars);
|
||||||
|
|
||||||
|
var sub_toBase64 = function(m){
|
||||||
|
var n = (m.charCodeAt(0) << 16)
|
||||||
|
| (m.charCodeAt(1) << 8)
|
||||||
|
| (m.charCodeAt(2) );
|
||||||
|
return b64chars.charAt( n >>> 18)
|
||||||
|
+ b64chars.charAt((n >>> 12) & 63)
|
||||||
|
+ b64chars.charAt((n >>> 6) & 63)
|
||||||
|
+ b64chars.charAt( n & 63);
|
||||||
|
};
|
||||||
|
|
||||||
|
var toBase64 = function(bin){
|
||||||
|
if (bin.match(/[^\x00-\xFF]/)) throw 'unsupported character found' ;
|
||||||
|
var padlen = 0;
|
||||||
|
while(bin.length % 3) {
|
||||||
|
bin += '\0';
|
||||||
|
padlen++;
|
||||||
|
};
|
||||||
|
var b64 = bin.replace(/[\x00-\xFF]{3}/g, sub_toBase64);
|
||||||
|
if (!padlen) return b64;
|
||||||
|
b64 = b64.substr(0, b64.length - padlen);
|
||||||
|
while(padlen--) b64 += '=';
|
||||||
|
return b64;
|
||||||
|
};
|
||||||
|
|
||||||
|
var btoa = global.btoa || toBase64;
|
||||||
|
|
||||||
|
var sub_fromBase64 = function(m){
|
||||||
|
var n = (b64tab[ m.charAt(0) ] << 18)
|
||||||
|
| (b64tab[ m.charAt(1) ] << 12)
|
||||||
|
| (b64tab[ m.charAt(2) ] << 6)
|
||||||
|
| (b64tab[ m.charAt(3) ]);
|
||||||
|
return String.fromCharCode( n >> 16 )
|
||||||
|
+ String.fromCharCode( (n >> 8) & 0xff )
|
||||||
|
+ String.fromCharCode( n & 0xff );
|
||||||
|
};
|
||||||
|
|
||||||
|
var fromBase64 = function(b64){
|
||||||
|
b64 = b64.replace(/[^A-Za-z0-9\+\/]/g, '');
|
||||||
|
var padlen = 0;
|
||||||
|
while(b64.length % 4){
|
||||||
|
b64 += 'A';
|
||||||
|
padlen++;
|
||||||
|
}
|
||||||
|
var bin = b64.replace(/[A-Za-z0-9\+\/]{4}/g, sub_fromBase64);
|
||||||
|
if (padlen >= 2)
|
||||||
|
bin = bin.substring(0, bin.length - [0,0,2,1][padlen]);
|
||||||
|
return bin;
|
||||||
|
};
|
||||||
|
|
||||||
|
var atob = global.atob || fromBase64;
|
||||||
|
|
||||||
|
var re_char_nonascii = /[^\x00-\x7F]/g;
|
||||||
|
|
||||||
|
var sub_char_nonascii = function(m){
|
||||||
|
var n = m.charCodeAt(0);
|
||||||
|
return n < 0x800 ? String.fromCharCode(0xc0 | (n >>> 6))
|
||||||
|
+ String.fromCharCode(0x80 | (n & 0x3f))
|
||||||
|
: String.fromCharCode(0xe0 | ((n >>> 12) & 0x0f))
|
||||||
|
+ String.fromCharCode(0x80 | ((n >>> 6) & 0x3f))
|
||||||
|
+ String.fromCharCode(0x80 | (n & 0x3f))
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
var utob = function(uni){
|
||||||
|
return uni.replace(re_char_nonascii, sub_char_nonascii);
|
||||||
|
};
|
||||||
|
|
||||||
|
var re_bytes_nonascii
|
||||||
|
= /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
|
||||||
|
|
||||||
|
var sub_bytes_nonascii = function(m){
|
||||||
|
var c0 = m.charCodeAt(0);
|
||||||
|
var c1 = m.charCodeAt(1);
|
||||||
|
if(c0 < 0xe0){
|
||||||
|
return String.fromCharCode(((c0 & 0x1f) << 6) | (c1 & 0x3f));
|
||||||
|
}else{
|
||||||
|
var c2 = m.charCodeAt(2);
|
||||||
|
return String.fromCharCode(
|
||||||
|
((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var btou = function(bin){
|
||||||
|
return bin.replace(re_bytes_nonascii, sub_bytes_nonascii);
|
||||||
|
};
|
||||||
|
|
||||||
|
global.Base64 = {
|
||||||
|
fromBase64:fromBase64,
|
||||||
|
toBase64:toBase64,
|
||||||
|
atob:atob,
|
||||||
|
btoa:btoa,
|
||||||
|
utob:utob,
|
||||||
|
btou:btou,
|
||||||
|
encode:function(u){ return btoa(utob(u)) },
|
||||||
|
encodeURI:function(u){
|
||||||
|
return btoa(utob(u)).replace(/[+\/]/g, function(m0){
|
||||||
|
return m0 == '+' ? '-' : '_';
|
||||||
|
}).replace(/=+$/, '');
|
||||||
|
},
|
||||||
|
decode:function(a){
|
||||||
|
return btou(atob(a.replace(/[-_]/g, function(m0){
|
||||||
|
return m0 == '-' ? '+' : '/';
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})(this);
|
BIN
lib/busy.gif
Normal file
BIN
lib/busy.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 671 B |
BIN
lib/icon_clone.png
Normal file
BIN
lib/icon_clone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 391 B |
BIN
lib/icon_new.png
Normal file
BIN
lib/icon_new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 209 B |
BIN
lib/icon_send.png
Normal file
BIN
lib/icon_send.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 261 B |
BIN
lib/icon_shorten.png
Normal file
BIN
lib/icon_shorten.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 262 B |
4
lib/jquery.js
vendored
Normal file
4
lib/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1043
lib/rain.tpl.class.php
Normal file
1043
lib/rain.tpl.class.php
Normal file
File diff suppressed because it is too large
Load diff
1671
lib/rawdeflate.js
Normal file
1671
lib/rawdeflate.js
Normal file
File diff suppressed because it is too large
Load diff
753
lib/rawinflate.js
Normal file
753
lib/rawinflate.js
Normal file
|
@ -0,0 +1,753 @@
|
||||||
|
/*
|
||||||
|
* $Id: rawinflate.js,v 0.2 2009/03/01 18:32:24 dankogai Exp $
|
||||||
|
*
|
||||||
|
* original:
|
||||||
|
* http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
|
||||||
|
* Version: 1.0.0.1
|
||||||
|
* LastModified: Dec 25 1999
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Interface:
|
||||||
|
* data = zip_inflate(src);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* constant parameters */
|
||||||
|
var zip_WSIZE = 32768; // Sliding Window size
|
||||||
|
var zip_STORED_BLOCK = 0;
|
||||||
|
var zip_STATIC_TREES = 1;
|
||||||
|
var zip_DYN_TREES = 2;
|
||||||
|
|
||||||
|
/* for inflate */
|
||||||
|
var zip_lbits = 9; // bits in base literal/length lookup table
|
||||||
|
var zip_dbits = 6; // bits in base distance lookup table
|
||||||
|
var zip_INBUFSIZ = 32768; // Input buffer size
|
||||||
|
var zip_INBUF_EXTRA = 64; // Extra buffer
|
||||||
|
|
||||||
|
/* variables (inflate) */
|
||||||
|
var zip_slide;
|
||||||
|
var zip_wp; // current position in slide
|
||||||
|
var zip_fixed_tl = null; // inflate static
|
||||||
|
var zip_fixed_td; // inflate static
|
||||||
|
var zip_fixed_bl, fixed_bd; // inflate static
|
||||||
|
var zip_bit_buf; // bit buffer
|
||||||
|
var zip_bit_len; // bits in bit buffer
|
||||||
|
var zip_method;
|
||||||
|
var zip_eof;
|
||||||
|
var zip_copy_leng;
|
||||||
|
var zip_copy_dist;
|
||||||
|
var zip_tl, zip_td; // literal/length and distance decoder tables
|
||||||
|
var zip_bl, zip_bd; // number of bits decoded by tl and td
|
||||||
|
|
||||||
|
var zip_inflate_data;
|
||||||
|
var zip_inflate_pos;
|
||||||
|
|
||||||
|
|
||||||
|
/* constant tables (inflate) */
|
||||||
|
var zip_MASK_BITS = new Array(
|
||||||
|
0x0000,
|
||||||
|
0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
|
||||||
|
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff);
|
||||||
|
// Tables for deflate from PKZIP's appnote.txt.
|
||||||
|
var zip_cplens = new Array( // Copy lengths for literal codes 257..285
|
||||||
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||||||
|
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0);
|
||||||
|
/* note: see note #13 above about the 258 in this list. */
|
||||||
|
var zip_cplext = new Array( // Extra bits for literal codes 257..285
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||||
|
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99); // 99==invalid
|
||||||
|
var zip_cpdist = new Array( // Copy offsets for distance codes 0..29
|
||||||
|
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||||
|
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||||
|
8193, 12289, 16385, 24577);
|
||||||
|
var zip_cpdext = new Array( // Extra bits for distance codes
|
||||||
|
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
||||||
|
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
||||||
|
12, 12, 13, 13);
|
||||||
|
var zip_border = new Array( // Order of the bit length code lengths
|
||||||
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
|
||||||
|
/* objects (inflate) */
|
||||||
|
|
||||||
|
var zip_HuftList = function() {
|
||||||
|
this.next = null;
|
||||||
|
this.list = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_HuftNode = function() {
|
||||||
|
this.e = 0; // number of extra bits or operation
|
||||||
|
this.b = 0; // number of bits in this code or subcode
|
||||||
|
|
||||||
|
// union
|
||||||
|
this.n = 0; // literal, length base, or distance base
|
||||||
|
this.t = null; // (zip_HuftNode) pointer to next level of table
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_HuftBuild = function(b, // code lengths in bits (all assumed <= BMAX)
|
||||||
|
n, // number of codes (assumed <= N_MAX)
|
||||||
|
s, // number of simple-valued codes (0..s-1)
|
||||||
|
d, // list of base values for non-simple codes
|
||||||
|
e, // list of extra bits for non-simple codes
|
||||||
|
mm // maximum lookup bits
|
||||||
|
) {
|
||||||
|
this.BMAX = 16; // maximum bit length of any code
|
||||||
|
this.N_MAX = 288; // maximum number of codes in any set
|
||||||
|
this.status = 0; // 0: success, 1: incomplete table, 2: bad input
|
||||||
|
this.root = null; // (zip_HuftList) starting table
|
||||||
|
this.m = 0; // maximum lookup bits, returns actual
|
||||||
|
|
||||||
|
/* Given a list of code lengths and a maximum table size, make a set of
|
||||||
|
tables to decode that set of codes. Return zero on success, one if
|
||||||
|
the given code set is incomplete (the tables are still built in this
|
||||||
|
case), two if the input is invalid (all zero length codes or an
|
||||||
|
oversubscribed set of lengths), and three if not enough memory.
|
||||||
|
The code with value 256 is special, and the tables are constructed
|
||||||
|
so that no bits beyond that code are fetched when that code is
|
||||||
|
decoded. */
|
||||||
|
{
|
||||||
|
var a; // counter for codes of length k
|
||||||
|
var c = new Array(this.BMAX+1); // bit length count table
|
||||||
|
var el; // length of EOB code (value 256)
|
||||||
|
var f; // i repeats in table every f entries
|
||||||
|
var g; // maximum code length
|
||||||
|
var h; // table level
|
||||||
|
var i; // counter, current code
|
||||||
|
var j; // counter
|
||||||
|
var k; // number of bits in current code
|
||||||
|
var lx = new Array(this.BMAX+1); // stack of bits per table
|
||||||
|
var p; // pointer into c[], b[], or v[]
|
||||||
|
var pidx; // index of p
|
||||||
|
var q; // (zip_HuftNode) points to current table
|
||||||
|
var r = new zip_HuftNode(); // table entry for structure assignment
|
||||||
|
var u = new Array(this.BMAX); // zip_HuftNode[BMAX][] table stack
|
||||||
|
var v = new Array(this.N_MAX); // values in order of bit length
|
||||||
|
var w;
|
||||||
|
var x = new Array(this.BMAX+1);// bit offsets, then code stack
|
||||||
|
var xp; // pointer into x or c
|
||||||
|
var y; // number of dummy codes added
|
||||||
|
var z; // number of entries in current table
|
||||||
|
var o;
|
||||||
|
var tail; // (zip_HuftList)
|
||||||
|
|
||||||
|
tail = this.root = null;
|
||||||
|
for(i = 0; i < c.length; i++)
|
||||||
|
c[i] = 0;
|
||||||
|
for(i = 0; i < lx.length; i++)
|
||||||
|
lx[i] = 0;
|
||||||
|
for(i = 0; i < u.length; i++)
|
||||||
|
u[i] = null;
|
||||||
|
for(i = 0; i < v.length; i++)
|
||||||
|
v[i] = 0;
|
||||||
|
for(i = 0; i < x.length; i++)
|
||||||
|
x[i] = 0;
|
||||||
|
|
||||||
|
// Generate counts for each bit length
|
||||||
|
el = n > 256 ? b[256] : this.BMAX; // set length of EOB code, if any
|
||||||
|
p = b; pidx = 0;
|
||||||
|
i = n;
|
||||||
|
do {
|
||||||
|
c[p[pidx]]++; // assume all entries <= BMAX
|
||||||
|
pidx++;
|
||||||
|
} while(--i > 0);
|
||||||
|
if(c[0] == n) { // null input--all zero length codes
|
||||||
|
this.root = null;
|
||||||
|
this.m = 0;
|
||||||
|
this.status = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find minimum and maximum length, bound *m by those
|
||||||
|
for(j = 1; j <= this.BMAX; j++)
|
||||||
|
if(c[j] != 0)
|
||||||
|
break;
|
||||||
|
k = j; // minimum code length
|
||||||
|
if(mm < j)
|
||||||
|
mm = j;
|
||||||
|
for(i = this.BMAX; i != 0; i--)
|
||||||
|
if(c[i] != 0)
|
||||||
|
break;
|
||||||
|
g = i; // maximum code length
|
||||||
|
if(mm > i)
|
||||||
|
mm = i;
|
||||||
|
|
||||||
|
// Adjust last length count to fill out codes, if needed
|
||||||
|
for(y = 1 << j; j < i; j++, y <<= 1)
|
||||||
|
if((y -= c[j]) < 0) {
|
||||||
|
this.status = 2; // bad input: more codes than bits
|
||||||
|
this.m = mm;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if((y -= c[i]) < 0) {
|
||||||
|
this.status = 2;
|
||||||
|
this.m = mm;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
c[i] += y;
|
||||||
|
|
||||||
|
// Generate starting offsets into the value table for each length
|
||||||
|
x[1] = j = 0;
|
||||||
|
p = c;
|
||||||
|
pidx = 1;
|
||||||
|
xp = 2;
|
||||||
|
while(--i > 0) // note that i == g from above
|
||||||
|
x[xp++] = (j += p[pidx++]);
|
||||||
|
|
||||||
|
// Make a table of values in order of bit lengths
|
||||||
|
p = b; pidx = 0;
|
||||||
|
i = 0;
|
||||||
|
do {
|
||||||
|
if((j = p[pidx++]) != 0)
|
||||||
|
v[x[j]++] = i;
|
||||||
|
} while(++i < n);
|
||||||
|
n = x[g]; // set n to length of v
|
||||||
|
|
||||||
|
// Generate the Huffman codes and for each, make the table entries
|
||||||
|
x[0] = i = 0; // first Huffman code is zero
|
||||||
|
p = v; pidx = 0; // grab values in bit order
|
||||||
|
h = -1; // no tables yet--level -1
|
||||||
|
w = lx[0] = 0; // no bits decoded yet
|
||||||
|
q = null; // ditto
|
||||||
|
z = 0; // ditto
|
||||||
|
|
||||||
|
// go through the bit lengths (k already is bits in shortest code)
|
||||||
|
for(; k <= g; k++) {
|
||||||
|
a = c[k];
|
||||||
|
while(a-- > 0) {
|
||||||
|
// here i is the Huffman code of length k bits for value p[pidx]
|
||||||
|
// make tables up to required level
|
||||||
|
while(k > w + lx[1 + h]) {
|
||||||
|
w += lx[1 + h]; // add bits already decoded
|
||||||
|
h++;
|
||||||
|
|
||||||
|
// compute minimum size table less than or equal to *m bits
|
||||||
|
z = (z = g - w) > mm ? mm : z; // upper limit
|
||||||
|
if((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table
|
||||||
|
// too few codes for k-w bit table
|
||||||
|
f -= a + 1; // deduct codes from patterns left
|
||||||
|
xp = k;
|
||||||
|
while(++j < z) { // try smaller tables up to z bits
|
||||||
|
if((f <<= 1) <= c[++xp])
|
||||||
|
break; // enough codes to use up j bits
|
||||||
|
f -= c[xp]; // else deduct codes from patterns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(w + j > el && w < el)
|
||||||
|
j = el - w; // make EOB code end at table
|
||||||
|
z = 1 << j; // table entries for j-bit table
|
||||||
|
lx[1 + h] = j; // set table size in stack
|
||||||
|
|
||||||
|
// allocate and link in new table
|
||||||
|
q = new Array(z);
|
||||||
|
for(o = 0; o < z; o++) {
|
||||||
|
q[o] = new zip_HuftNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tail == null)
|
||||||
|
tail = this.root = new zip_HuftList();
|
||||||
|
else
|
||||||
|
tail = tail.next = new zip_HuftList();
|
||||||
|
tail.next = null;
|
||||||
|
tail.list = q;
|
||||||
|
u[h] = q; // table starts after link
|
||||||
|
|
||||||
|
/* connect to last table, if there is one */
|
||||||
|
if(h > 0) {
|
||||||
|
x[h] = i; // save pattern for backing up
|
||||||
|
r.b = lx[h]; // bits to dump before this table
|
||||||
|
r.e = 16 + j; // bits in this table
|
||||||
|
r.t = q; // pointer to this table
|
||||||
|
j = (i & ((1 << w) - 1)) >> (w - lx[h]);
|
||||||
|
u[h-1][j].e = r.e;
|
||||||
|
u[h-1][j].b = r.b;
|
||||||
|
u[h-1][j].n = r.n;
|
||||||
|
u[h-1][j].t = r.t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up table entry in r
|
||||||
|
r.b = k - w;
|
||||||
|
if(pidx >= n)
|
||||||
|
r.e = 99; // out of values--invalid code
|
||||||
|
else if(p[pidx] < s) {
|
||||||
|
r.e = (p[pidx] < 256 ? 16 : 15); // 256 is end-of-block code
|
||||||
|
r.n = p[pidx++]; // simple code is just the value
|
||||||
|
} else {
|
||||||
|
r.e = e[p[pidx] - s]; // non-simple--look up in lists
|
||||||
|
r.n = d[p[pidx++] - s];
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill code-like entries with r //
|
||||||
|
f = 1 << (k - w);
|
||||||
|
for(j = i >> w; j < z; j += f) {
|
||||||
|
q[j].e = r.e;
|
||||||
|
q[j].b = r.b;
|
||||||
|
q[j].n = r.n;
|
||||||
|
q[j].t = r.t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// backwards increment the k-bit code i
|
||||||
|
for(j = 1 << (k - 1); (i & j) != 0; j >>= 1)
|
||||||
|
i ^= j;
|
||||||
|
i ^= j;
|
||||||
|
|
||||||
|
// backup over finished tables
|
||||||
|
while((i & ((1 << w) - 1)) != x[h]) {
|
||||||
|
w -= lx[h]; // don't need to update q
|
||||||
|
h--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return actual size of base table */
|
||||||
|
this.m = lx[1];
|
||||||
|
|
||||||
|
/* Return true (1) if we were given an incomplete table */
|
||||||
|
this.status = ((y != 0 && g != 1) ? 1 : 0);
|
||||||
|
} /* end of constructor */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* routines (inflate) */
|
||||||
|
|
||||||
|
var zip_GET_BYTE = function() {
|
||||||
|
if(zip_inflate_data.length == zip_inflate_pos)
|
||||||
|
return -1;
|
||||||
|
return zip_inflate_data.charCodeAt(zip_inflate_pos++) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_NEEDBITS = function(n) {
|
||||||
|
while(zip_bit_len < n) {
|
||||||
|
zip_bit_buf |= zip_GET_BYTE() << zip_bit_len;
|
||||||
|
zip_bit_len += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_GETBITS = function(n) {
|
||||||
|
return zip_bit_buf & zip_MASK_BITS[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_DUMPBITS = function(n) {
|
||||||
|
zip_bit_buf >>= n;
|
||||||
|
zip_bit_len -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_codes = function(buff, off, size) {
|
||||||
|
/* inflate (decompress) the codes in a deflated (compressed) block.
|
||||||
|
Return an error code or zero if it all goes ok. */
|
||||||
|
var e; // table entry flag/number of extra bits
|
||||||
|
var t; // (zip_HuftNode) pointer to table entry
|
||||||
|
var n;
|
||||||
|
|
||||||
|
if(size == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// inflate the coded data
|
||||||
|
n = 0;
|
||||||
|
for(;;) { // do until end of block
|
||||||
|
zip_NEEDBITS(zip_bl);
|
||||||
|
t = zip_tl.list[zip_GETBITS(zip_bl)];
|
||||||
|
e = t.e;
|
||||||
|
while(e > 16) {
|
||||||
|
if(e == 99)
|
||||||
|
return -1;
|
||||||
|
zip_DUMPBITS(t.b);
|
||||||
|
e -= 16;
|
||||||
|
zip_NEEDBITS(e);
|
||||||
|
t = t.t[zip_GETBITS(e)];
|
||||||
|
e = t.e;
|
||||||
|
}
|
||||||
|
zip_DUMPBITS(t.b);
|
||||||
|
|
||||||
|
if(e == 16) { // then it's a literal
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++] = t.n;
|
||||||
|
if(n == size)
|
||||||
|
return size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit if end of block
|
||||||
|
if(e == 15)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// it's an EOB or a length
|
||||||
|
|
||||||
|
// get length of block to copy
|
||||||
|
zip_NEEDBITS(e);
|
||||||
|
zip_copy_leng = t.n + zip_GETBITS(e);
|
||||||
|
zip_DUMPBITS(e);
|
||||||
|
|
||||||
|
// decode distance of block to copy
|
||||||
|
zip_NEEDBITS(zip_bd);
|
||||||
|
t = zip_td.list[zip_GETBITS(zip_bd)];
|
||||||
|
e = t.e;
|
||||||
|
|
||||||
|
while(e > 16) {
|
||||||
|
if(e == 99)
|
||||||
|
return -1;
|
||||||
|
zip_DUMPBITS(t.b);
|
||||||
|
e -= 16;
|
||||||
|
zip_NEEDBITS(e);
|
||||||
|
t = t.t[zip_GETBITS(e)];
|
||||||
|
e = t.e;
|
||||||
|
}
|
||||||
|
zip_DUMPBITS(t.b);
|
||||||
|
zip_NEEDBITS(e);
|
||||||
|
zip_copy_dist = zip_wp - t.n - zip_GETBITS(e);
|
||||||
|
zip_DUMPBITS(e);
|
||||||
|
|
||||||
|
// do the copy
|
||||||
|
while(zip_copy_leng > 0 && n < size) {
|
||||||
|
zip_copy_leng--;
|
||||||
|
zip_copy_dist &= zip_WSIZE - 1;
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++]
|
||||||
|
= zip_slide[zip_copy_dist++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n == size)
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
zip_method = -1; // done
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_stored = function(buff, off, size) {
|
||||||
|
/* "decompress" an inflated type 0 (stored) block. */
|
||||||
|
var n;
|
||||||
|
|
||||||
|
// go to byte boundary
|
||||||
|
n = zip_bit_len & 7;
|
||||||
|
zip_DUMPBITS(n);
|
||||||
|
|
||||||
|
// get the length and its complement
|
||||||
|
zip_NEEDBITS(16);
|
||||||
|
n = zip_GETBITS(16);
|
||||||
|
zip_DUMPBITS(16);
|
||||||
|
zip_NEEDBITS(16);
|
||||||
|
if(n != ((~zip_bit_buf) & 0xffff))
|
||||||
|
return -1; // error in compressed data
|
||||||
|
zip_DUMPBITS(16);
|
||||||
|
|
||||||
|
// read and output the compressed data
|
||||||
|
zip_copy_leng = n;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
while(zip_copy_leng > 0 && n < size) {
|
||||||
|
zip_copy_leng--;
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
zip_NEEDBITS(8);
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++] =
|
||||||
|
zip_GETBITS(8);
|
||||||
|
zip_DUMPBITS(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(zip_copy_leng == 0)
|
||||||
|
zip_method = -1; // done
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_fixed = function(buff, off, size) {
|
||||||
|
/* decompress an inflated type 1 (fixed Huffman codes) block. We should
|
||||||
|
either replace this with a custom decoder, or at least precompute the
|
||||||
|
Huffman tables. */
|
||||||
|
|
||||||
|
// if first time, set up tables for fixed blocks
|
||||||
|
if(zip_fixed_tl == null) {
|
||||||
|
var i; // temporary variable
|
||||||
|
var l = new Array(288); // length list for huft_build
|
||||||
|
var h; // zip_HuftBuild
|
||||||
|
|
||||||
|
// literal table
|
||||||
|
for(i = 0; i < 144; i++)
|
||||||
|
l[i] = 8;
|
||||||
|
for(; i < 256; i++)
|
||||||
|
l[i] = 9;
|
||||||
|
for(; i < 280; i++)
|
||||||
|
l[i] = 7;
|
||||||
|
for(; i < 288; i++) // make a complete, but wrong code set
|
||||||
|
l[i] = 8;
|
||||||
|
zip_fixed_bl = 7;
|
||||||
|
|
||||||
|
h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext,
|
||||||
|
zip_fixed_bl);
|
||||||
|
if(h.status != 0) {
|
||||||
|
alert("HufBuild error: "+h.status);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
zip_fixed_tl = h.root;
|
||||||
|
zip_fixed_bl = h.m;
|
||||||
|
|
||||||
|
// distance table
|
||||||
|
for(i = 0; i < 30; i++) // make an incomplete code set
|
||||||
|
l[i] = 5;
|
||||||
|
zip_fixed_bd = 5;
|
||||||
|
|
||||||
|
h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd);
|
||||||
|
if(h.status > 1) {
|
||||||
|
zip_fixed_tl = null;
|
||||||
|
alert("HufBuild error: "+h.status);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
zip_fixed_td = h.root;
|
||||||
|
zip_fixed_bd = h.m;
|
||||||
|
}
|
||||||
|
|
||||||
|
zip_tl = zip_fixed_tl;
|
||||||
|
zip_td = zip_fixed_td;
|
||||||
|
zip_bl = zip_fixed_bl;
|
||||||
|
zip_bd = zip_fixed_bd;
|
||||||
|
return zip_inflate_codes(buff, off, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_dynamic = function(buff, off, size) {
|
||||||
|
// decompress an inflated type 2 (dynamic Huffman codes) block.
|
||||||
|
var i; // temporary variables
|
||||||
|
var j;
|
||||||
|
var l; // last length
|
||||||
|
var n; // number of lengths to get
|
||||||
|
var t; // (zip_HuftNode) literal/length code table
|
||||||
|
var nb; // number of bit length codes
|
||||||
|
var nl; // number of literal/length codes
|
||||||
|
var nd; // number of distance codes
|
||||||
|
var ll = new Array(286+30); // literal/length and distance code lengths
|
||||||
|
var h; // (zip_HuftBuild)
|
||||||
|
|
||||||
|
for(i = 0; i < ll.length; i++)
|
||||||
|
ll[i] = 0;
|
||||||
|
|
||||||
|
// read in table lengths
|
||||||
|
zip_NEEDBITS(5);
|
||||||
|
nl = 257 + zip_GETBITS(5); // number of literal/length codes
|
||||||
|
zip_DUMPBITS(5);
|
||||||
|
zip_NEEDBITS(5);
|
||||||
|
nd = 1 + zip_GETBITS(5); // number of distance codes
|
||||||
|
zip_DUMPBITS(5);
|
||||||
|
zip_NEEDBITS(4);
|
||||||
|
nb = 4 + zip_GETBITS(4); // number of bit length codes
|
||||||
|
zip_DUMPBITS(4);
|
||||||
|
if(nl > 286 || nd > 30)
|
||||||
|
return -1; // bad lengths
|
||||||
|
|
||||||
|
// read in bit-length-code lengths
|
||||||
|
for(j = 0; j < nb; j++)
|
||||||
|
{
|
||||||
|
zip_NEEDBITS(3);
|
||||||
|
ll[zip_border[j]] = zip_GETBITS(3);
|
||||||
|
zip_DUMPBITS(3);
|
||||||
|
}
|
||||||
|
for(; j < 19; j++)
|
||||||
|
ll[zip_border[j]] = 0;
|
||||||
|
|
||||||
|
// build decoding table for trees--single level, 7 bit lookup
|
||||||
|
zip_bl = 7;
|
||||||
|
h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl);
|
||||||
|
if(h.status != 0)
|
||||||
|
return -1; // incomplete code set
|
||||||
|
|
||||||
|
zip_tl = h.root;
|
||||||
|
zip_bl = h.m;
|
||||||
|
|
||||||
|
// read in literal and distance code lengths
|
||||||
|
n = nl + nd;
|
||||||
|
i = l = 0;
|
||||||
|
while(i < n) {
|
||||||
|
zip_NEEDBITS(zip_bl);
|
||||||
|
t = zip_tl.list[zip_GETBITS(zip_bl)];
|
||||||
|
j = t.b;
|
||||||
|
zip_DUMPBITS(j);
|
||||||
|
j = t.n;
|
||||||
|
if(j < 16) // length of code in bits (0..15)
|
||||||
|
ll[i++] = l = j; // save last length in l
|
||||||
|
else if(j == 16) { // repeat last length 3 to 6 times
|
||||||
|
zip_NEEDBITS(2);
|
||||||
|
j = 3 + zip_GETBITS(2);
|
||||||
|
zip_DUMPBITS(2);
|
||||||
|
if(i + j > n)
|
||||||
|
return -1;
|
||||||
|
while(j-- > 0)
|
||||||
|
ll[i++] = l;
|
||||||
|
} else if(j == 17) { // 3 to 10 zero length codes
|
||||||
|
zip_NEEDBITS(3);
|
||||||
|
j = 3 + zip_GETBITS(3);
|
||||||
|
zip_DUMPBITS(3);
|
||||||
|
if(i + j > n)
|
||||||
|
return -1;
|
||||||
|
while(j-- > 0)
|
||||||
|
ll[i++] = 0;
|
||||||
|
l = 0;
|
||||||
|
} else { // j == 18: 11 to 138 zero length codes
|
||||||
|
zip_NEEDBITS(7);
|
||||||
|
j = 11 + zip_GETBITS(7);
|
||||||
|
zip_DUMPBITS(7);
|
||||||
|
if(i + j > n)
|
||||||
|
return -1;
|
||||||
|
while(j-- > 0)
|
||||||
|
ll[i++] = 0;
|
||||||
|
l = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the decoding tables for literal/length and distance codes
|
||||||
|
zip_bl = zip_lbits;
|
||||||
|
h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl);
|
||||||
|
if(zip_bl == 0) // no literals or lengths
|
||||||
|
h.status = 1;
|
||||||
|
if(h.status != 0) {
|
||||||
|
if(h.status == 1)
|
||||||
|
;// **incomplete literal tree**
|
||||||
|
return -1; // incomplete code set
|
||||||
|
}
|
||||||
|
zip_tl = h.root;
|
||||||
|
zip_bl = h.m;
|
||||||
|
|
||||||
|
for(i = 0; i < nd; i++)
|
||||||
|
ll[i] = ll[i + nl];
|
||||||
|
zip_bd = zip_dbits;
|
||||||
|
h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd);
|
||||||
|
zip_td = h.root;
|
||||||
|
zip_bd = h.m;
|
||||||
|
|
||||||
|
if(zip_bd == 0 && nl > 257) { // lengths but no distances
|
||||||
|
// **incomplete distance tree**
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(h.status == 1) {
|
||||||
|
;// **incomplete distance tree**
|
||||||
|
}
|
||||||
|
if(h.status != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// decompress until an end-of-block code
|
||||||
|
return zip_inflate_codes(buff, off, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_start = function() {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
if(zip_slide == null)
|
||||||
|
zip_slide = new Array(2 * zip_WSIZE);
|
||||||
|
zip_wp = 0;
|
||||||
|
zip_bit_buf = 0;
|
||||||
|
zip_bit_len = 0;
|
||||||
|
zip_method = -1;
|
||||||
|
zip_eof = false;
|
||||||
|
zip_copy_leng = zip_copy_dist = 0;
|
||||||
|
zip_tl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_internal = function(buff, off, size) {
|
||||||
|
// decompress an inflated entry
|
||||||
|
var n, i;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
while(n < size) {
|
||||||
|
if(zip_eof && zip_method == -1)
|
||||||
|
return n;
|
||||||
|
|
||||||
|
if(zip_copy_leng > 0) {
|
||||||
|
if(zip_method != zip_STORED_BLOCK) {
|
||||||
|
// STATIC_TREES or DYN_TREES
|
||||||
|
while(zip_copy_leng > 0 && n < size) {
|
||||||
|
zip_copy_leng--;
|
||||||
|
zip_copy_dist &= zip_WSIZE - 1;
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++] =
|
||||||
|
zip_slide[zip_copy_dist++];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while(zip_copy_leng > 0 && n < size) {
|
||||||
|
zip_copy_leng--;
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
zip_NEEDBITS(8);
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8);
|
||||||
|
zip_DUMPBITS(8);
|
||||||
|
}
|
||||||
|
if(zip_copy_leng == 0)
|
||||||
|
zip_method = -1; // done
|
||||||
|
}
|
||||||
|
if(n == size)
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(zip_method == -1) {
|
||||||
|
if(zip_eof)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// read in last block bit
|
||||||
|
zip_NEEDBITS(1);
|
||||||
|
if(zip_GETBITS(1) != 0)
|
||||||
|
zip_eof = true;
|
||||||
|
zip_DUMPBITS(1);
|
||||||
|
|
||||||
|
// read in block type
|
||||||
|
zip_NEEDBITS(2);
|
||||||
|
zip_method = zip_GETBITS(2);
|
||||||
|
zip_DUMPBITS(2);
|
||||||
|
zip_tl = null;
|
||||||
|
zip_copy_leng = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(zip_method) {
|
||||||
|
case 0: // zip_STORED_BLOCK
|
||||||
|
i = zip_inflate_stored(buff, off + n, size - n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // zip_STATIC_TREES
|
||||||
|
if(zip_tl != null)
|
||||||
|
i = zip_inflate_codes(buff, off + n, size - n);
|
||||||
|
else
|
||||||
|
i = zip_inflate_fixed(buff, off + n, size - n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // zip_DYN_TREES
|
||||||
|
if(zip_tl != null)
|
||||||
|
i = zip_inflate_codes(buff, off + n, size - n);
|
||||||
|
else
|
||||||
|
i = zip_inflate_dynamic(buff, off + n, size - n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // error
|
||||||
|
i = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i == -1) {
|
||||||
|
if(zip_eof)
|
||||||
|
return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n += i;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate = function(str) {
|
||||||
|
var i, j;
|
||||||
|
|
||||||
|
zip_inflate_start();
|
||||||
|
zip_inflate_data = str;
|
||||||
|
zip_inflate_pos = 0;
|
||||||
|
|
||||||
|
var buff = new Array(1024);
|
||||||
|
var aout = [];
|
||||||
|
while((i = zip_inflate_internal(buff, 0, buff.length)) > 0) {
|
||||||
|
var cbuf = new Array(i);
|
||||||
|
for(j = 0; j < i; j++){
|
||||||
|
cbuf[j] = String.fromCharCode(buff[j]);
|
||||||
|
}
|
||||||
|
aout[aout.length] = cbuf.join("");
|
||||||
|
}
|
||||||
|
zip_inflate_data = null; // G.C.
|
||||||
|
return aout.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! window.RawDeflate) RawDeflate = {};
|
||||||
|
RawDeflate.inflate = zip_inflate;
|
||||||
|
|
||||||
|
})();
|
41
lib/sjcl.js
Normal file
41
lib/sjcl.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
|
||||||
|
if(typeof module!="undefined"&&module.exports)module.exports=sjcl;
|
||||||
|
sjcl.cipher.aes=function(a){this.h[0][0][0]||this.w();var b,c,d,e,f=this.h[0][4],g=this.h[1];b=a.length;var h=1;if(b!==4&&b!==6&&b!==8)throw new sjcl.exception.invalid("invalid aes key size");this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(a%b===0||b===8&&a%b===4){c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255];if(a%b===0){c=c<<8^c>>>24^h<<24;h=h<<1^(h>>7)*283}}d[a]=d[a-b]^c}for(b=0;a;b++,a--){c=d[b&3?a:a-4];e[b]=a<=4||b<4?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^
|
||||||
|
g[3][f[c&255]]}};
|
||||||
|
sjcl.cipher.aes.prototype={encrypt:function(a){return this.H(a,0)},decrypt:function(a){return this.H(a,1)},h:[[[],[],[],[],[]],[[],[],[],[],[]]],w:function(){var a=this.h[0],b=this.h[1],c=a[4],d=b[4],e,f,g,h=[],i=[],k,j,l,m;for(e=0;e<0x100;e++)i[(h[e]=e<<1^(e>>7)*283)^e]=e;for(f=g=0;!c[f];f^=k||1,g=i[g]||1){l=g^g<<1^g<<2^g<<3^g<<4;l=l>>8^l&255^99;c[f]=l;d[l]=f;j=h[e=h[k=h[f]]];m=j*0x1010101^e*0x10001^k*0x101^f*0x1010100;j=h[l]*0x101^l*0x1010100;for(e=0;e<4;e++){a[e][f]=j=j<<24^j>>>8;b[e][l]=m=m<<24^m>>>8}}for(e=
|
||||||
|
0;e<5;e++){a[e]=a[e].slice(0);b[e]=b[e].slice(0)}},H:function(a,b){if(a.length!==4)throw new sjcl.exception.invalid("invalid aes block size");var c=this.a[b],d=a[0]^c[0],e=a[b?3:1]^c[1],f=a[2]^c[2];a=a[b?1:3]^c[3];var g,h,i,k=c.length/4-2,j,l=4,m=[0,0,0,0];g=this.h[b];var n=g[0],o=g[1],p=g[2],q=g[3],r=g[4];for(j=0;j<k;j++){g=n[d>>>24]^o[e>>16&255]^p[f>>8&255]^q[a&255]^c[l];h=n[e>>>24]^o[f>>16&255]^p[a>>8&255]^q[d&255]^c[l+1];i=n[f>>>24]^o[a>>16&255]^p[d>>8&255]^q[e&255]^c[l+2];a=n[a>>>24]^o[d>>16&
|
||||||
|
255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(j=0;j<4;j++){m[b?3&-j:j]=r[d>>>24]<<24^r[e>>16&255]<<16^r[f>>8&255]<<8^r[a&255]^c[l++];g=d;d=e;e=f;f=a;a=g}return m}};
|
||||||
|
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===undefined?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(a.length===0||b.length===0)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return d===32?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;
|
||||||
|
if(b===0)return 0;return(b-1)*32+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(a.length*32<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;if(c>0&&b)a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1);return a},partial:function(a,b,c){if(a===32)return b;return(c?b|0:b<<32-a)+a*0x10000000000},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return false;var c=0,d;for(d=0;d<a.length;d++)c|=
|
||||||
|
a[d]^b[d];return c===0},P:function(a,b,c,d){var e;e=0;if(d===undefined)d=[];for(;b>=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a);for(e=0;e<a.length;e++){d.push(c|a[e]>>>b);c=a[e]<<32-b}e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,b+a>32?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
|
||||||
|
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++){if((d&3)===0)e=a[d/4];b+=String.fromCharCode(e>>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++){d=d<<8|a.charCodeAt(c);if((c&3)===3){b.push(d);d=0}}c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
|
||||||
|
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,d*4)}};
|
||||||
|
sjcl.codec.base64={D:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.D,g=0,h=sjcl.bitArray.bitLength(a);if(c)f=f.substr(0,62)+"-_";for(c=0;d.length*6<h;){d+=f.charAt((g^a[c]>>>e)>>>26);if(e<6){g=a[c]<<6-e;e+=26;c++}else{g<<=6;e-=6}}for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d=0,e=sjcl.codec.base64.D,f=0,g;if(b)e=e.substr(0,62)+"-_";for(b=0;b<a.length;b++){g=e.indexOf(a.charAt(b));
|
||||||
|
if(g<0)throw new sjcl.exception.invalid("this isn't base64!");if(d>26){d-=26;c.push(f^g>>>d);f=g<<32-d}else{d+=6;f^=g<<32-d}}d&56&&c.push(sjcl.bitArray.partial(d&56,f,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.w();if(a){this.n=a.n.slice(0);this.i=a.i.slice(0);this.e=a.e}else this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
|
||||||
|
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.n=this.N.slice(0);this.i=[];this.e=0;return this},update:function(a){if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);var b,c=this.i=sjcl.bitArray.concat(this.i,a);b=this.e;a=this.e=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.C(c.splice(0,16));return this},finalize:function(){var a,b=this.i,c=this.n;b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.e/
|
||||||
|
4294967296));for(b.push(this.e|0);b.length;)this.C(b.splice(0,16));this.reset();return c},N:[],a:[],w:function(){function a(e){return(e-Math.floor(e))*0x100000000|0}var b=0,c=2,d;a:for(;b<64;c++){for(d=2;d*d<=c;d++)if(c%d===0)continue a;if(b<8)this.N[b]=a(Math.pow(c,0.5));this.a[b]=a(Math.pow(c,1/3));b++}},C:function(a){var b,c,d=a.slice(0),e=this.n,f=this.a,g=e[0],h=e[1],i=e[2],k=e[3],j=e[4],l=e[5],m=e[6],n=e[7];for(a=0;a<64;a++){if(a<16)b=d[a];else{b=d[a+1&15];c=d[a+14&15];b=d[a&15]=(b>>>7^b>>>18^
|
||||||
|
b>>>3^b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+d[a&15]+d[a+9&15]|0}b=b+n+(j>>>6^j>>>11^j>>>25^j<<26^j<<21^j<<7)+(m^j&(l^m))+f[a];n=m;m=l;l=j;j=k+b|0;k=i;i=h;h=g;g=b+(h&i^k&(h^i))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0}e[0]=e[0]+g|0;e[1]=e[1]+h|0;e[2]=e[2]+i|0;e[3]=e[3]+k|0;e[4]=e[4]+j|0;e[5]=e[5]+l|0;e[6]=e[6]+m|0;e[7]=e[7]+n|0}};
|
||||||
|
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,i=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];if(i<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;f<4&&k>>>8*f;f++);if(f<15-i)f=15-i;c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.G(a,b,c,d,e,f);g=sjcl.mode.ccm.I(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),i=f.clamp(b,h-e),k=f.bitSlice(b,
|
||||||
|
h-e);h=(h-e)/8;if(g<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;b<4&&h>>>8*b;b++);if(b<15-g)b=15-g;c=f.clamp(c,8*(15-b));i=sjcl.mode.ccm.I(a,i,c,k,e,b);a=sjcl.mode.ccm.G(a,i.data,c,d,e,b);if(!f.equal(i.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");return i.data},G:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,i=h.k;e/=8;if(e%2||e<4||e>16)throw new sjcl.exception.invalid("ccm: invalid tag length");if(d.length>0xffffffff||b.length>0xffffffff)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
|
||||||
|
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;if(c<=65279)g=[h.partial(16,c)];else if(c<=0xffffffff)g=h.concat([h.partial(16,65534)],[c]);g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(i(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(i(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,e*8)},I:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var i=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
|
||||||
|
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!i)return{tag:d,data:[]};for(g=0;g<i;g+=4){c[3]++;e=a.encrypt(c);b[g]^=e[0];b[g+1]^=e[1];b[g+2]^=e[2];b[g+3]^=e[3]}return{tag:d,data:h.clamp(b,k)}}};
|
||||||
|
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.A,i=sjcl.bitArray,k=i.k,j=[0,0,0,0];c=h(a.encrypt(c));var l,m=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4){l=b.slice(g,g+4);j=k(j,l);m=m.concat(k(c,a.encrypt(k(c,l))));c=h(c)}l=b.slice(g);b=i.bitLength(l);g=a.encrypt(k(c,[0,0,0,b]));l=i.clamp(k(l.concat([0,0,0]),g),b);j=k(j,k(l.concat([0,0,0]),g));j=a.encrypt(k(j,k(c,h(c))));
|
||||||
|
if(d.length)j=k(j,f?d:sjcl.mode.ocb2.pmac(a,d));return m.concat(i.concat(l,i.clamp(j,e)))},decrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");e=e||64;var g=sjcl.mode.ocb2.A,h=sjcl.bitArray,i=h.k,k=[0,0,0,0],j=g(a.encrypt(c)),l,m,n=sjcl.bitArray.bitLength(b)-e,o=[];d=d||[];for(c=0;c+4<n/32;c+=4){l=i(j,a.decrypt(i(j,b.slice(c,c+4))));k=i(k,l);o=o.concat(l);j=g(j)}m=n-c*32;l=a.encrypt(i(j,[0,0,0,m]));l=i(l,h.clamp(b.slice(c),
|
||||||
|
m).concat([0,0,0]));k=i(k,l);k=a.encrypt(i(k,i(j,g(j))));if(d.length)k=i(k,f?d:sjcl.mode.ocb2.pmac(a,d));if(!h.equal(h.clamp(k,e),h.bitSlice(b,n)))throw new sjcl.exception.corrupt("ocb: tag doesn't match");return o.concat(h.clamp(l,m))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.A,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]);h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4){h=d(h);g=f(g,a.encrypt(f(h,b.slice(c,c+4))))}b=b.slice(c);if(e.bitLength(b)<128){h=f(h,d(h));b=e.concat(b,[2147483648|0,0,
|
||||||
|
0,0])}g=f(g,b);return a.encrypt(f(d(f(h,d(h))),g))},A:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d=b.prototype.blockSize/32;this.l=[new b,new b];if(a.length>d)a=b.hash(a);for(b=0;b<d;b++){c[0][b]=a[b]^909522486;c[1][b]=a[b]^1549556828}this.l[0].update(c[0]);this.l[1].update(c[1])};
|
||||||
|
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a,b){a=(new this.M(this.l[0])).update(a,b).finalize();return(new this.M(this.l[1])).update(a).finalize()};
|
||||||
|
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;if(d<0||c<0)throw sjcl.exception.invalid("invalid params to pbkdf2");if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,i,k=[],j=sjcl.bitArray;for(i=1;32*k.length<(d||1);i++){e=f=a.encrypt(j.concat(b,[i]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}if(d)k=j.clamp(k,d);return k};
|
||||||
|
sjcl.random={randomWords:function(a,b){var c=[];b=this.isReady(b);var d;if(b===0)throw new sjcl.exception.notReady("generator isn't seeded");else b&2&&this.U(!(b&1));for(b=0;b<a;b+=4){(b+1)%0x10000===0&&this.L();d=this.u();c.push(d[0],d[1],d[2],d[3])}this.L();return c.slice(0,a)},setDefaultParanoia:function(a){this.t=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.q[c],h=this.isReady();d=this.F[c];if(d===undefined)d=this.F[c]=this.R++;if(g===undefined)g=this.q[c]=0;this.q[c]=
|
||||||
|
(this.q[c]+1)%this.b.length;switch(typeof a){case "number":break;case "object":if(b===undefined)for(c=b=0;c<a.length;c++)for(e=a[c];e>0;){b++;e>>>=1}this.b[g].update([d,this.J++,2,b,f,a.length].concat(a));break;case "string":if(b===undefined)b=a.length;this.b[g].update([d,this.J++,3,b,f,a.length]);this.b[g].update(a);break;default:throw new sjcl.exception.bug("random: addEntropy only supports number, array or string");}this.j[g]+=b;this.f+=b;if(h===0){this.isReady()!==0&&this.K("seeded",Math.max(this.g,
|
||||||
|
this.f));this.K("progress",this.getProgress())}},isReady:function(a){a=this.B[a!==undefined?a:this.t];return this.g&&this.g>=a?this.j[0]>80&&(new Date).valueOf()>this.O?3:1:this.f>=a?2:0},getProgress:function(a){a=this.B[a?a:this.t];return this.g>=a?1["0"]:this.f>a?1["0"]:this.f/a},startCollectors:function(){if(!this.m){if(window.addEventListener){window.addEventListener("load",this.o,false);window.addEventListener("mousemove",this.p,false)}else if(document.attachEvent){document.attachEvent("onload",
|
||||||
|
this.o);document.attachEvent("onmousemove",this.p)}else throw new sjcl.exception.bug("can't attach event");this.m=true}},stopCollectors:function(){if(this.m){if(window.removeEventListener){window.removeEventListener("load",this.o,false);window.removeEventListener("mousemove",this.p,false)}else if(window.detachEvent){window.detachEvent("onload",this.o);window.detachEvent("onmousemove",this.p)}this.m=false}},addEventListener:function(a,b){this.r[a][this.Q++]=b},removeEventListener:function(a,b){var c;
|
||||||
|
a=this.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&a[c]===b&&d.push(c);for(b=0;b<d.length;b++){c=d[b];delete a[c]}},b:[new sjcl.hash.sha256],j:[0],z:0,q:{},J:0,F:{},R:0,g:0,f:0,O:0,a:[0,0,0,0,0,0,0,0],d:[0,0,0,0],s:undefined,t:6,m:false,r:{progress:{},seeded:{}},Q:0,B:[0,48,64,96,128,192,0x100,384,512,768,1024],u:function(){for(var a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}return this.s.encrypt(this.d)},L:function(){this.a=this.u().concat(this.u());this.s=new sjcl.cipher.aes(this.a)},
|
||||||
|
T:function(a){this.a=sjcl.hash.sha256.hash(this.a.concat(a));this.s=new sjcl.cipher.aes(this.a);for(a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}},U:function(a){var b=[],c=0,d;this.O=b[0]=(new Date).valueOf()+3E4;for(d=0;d<16;d++)b.push(Math.random()*0x100000000|0);for(d=0;d<this.b.length;d++){b=b.concat(this.b[d].finalize());c+=this.j[d];this.j[d]=0;if(!a&&this.z&1<<d)break}if(this.z>=1<<this.b.length){this.b.push(new sjcl.hash.sha256);this.j.push(0)}this.f-=c;if(c>this.g)this.g=c;this.z++;
|
||||||
|
this.T(b)},p:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX,a.y||a.clientY||a.offsetY],2,"mouse")},o:function(){sjcl.random.addEntropy(new Date,2,"loadtime")},K:function(a,b){var c;a=sjcl.random.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c<d.length;c++)d[c](b)}};try{var s=new Uint32Array(32);crypto.getRandomValues(s);sjcl.random.addEntropy(s,1024,"crypto['getRandomValues']")}catch(t){}
|
||||||
|
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.c({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.c(f,c);c=f.adata;if(typeof f.salt==="string")f.salt=sjcl.codec.base64.toBits(f.salt);if(typeof f.iv==="string")f.iv=sjcl.codec.base64.toBits(f.iv);if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||typeof a==="string"&&f.iter<=100||f.ts!==64&&f.ts!==96&&f.ts!==128||f.ks!==128&&f.ks!==192&&f.ks!==0x100||f.iv.length<
|
||||||
|
2||f.iv.length>4)throw new sjcl.exception.invalid("json encrypt: invalid parameters");if(typeof a==="string"){g=sjcl.misc.cachedPbkdf2(a,f);a=g.key.slice(0,f.ks/32);f.salt=g.salt}if(typeof b==="string")b=sjcl.codec.utf8String.toBits(b);if(typeof c==="string")c=sjcl.codec.utf8String.toBits(c);g=new sjcl.cipher[f.cipher](a);e.c(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(e.V(f,e.defaults))},decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.c(e.c(e.c({},e.defaults),
|
||||||
|
e.decode(b)),c,true);var f;c=b.adata;if(typeof b.salt==="string")b.salt=sjcl.codec.base64.toBits(b.salt);if(typeof b.iv==="string")b.iv=sjcl.codec.base64.toBits(b.iv);if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||typeof a==="string"&&b.iter<=100||b.ts!==64&&b.ts!==96&&b.ts!==128||b.ks!==128&&b.ks!==192&&b.ks!==0x100||!b.iv||b.iv.length<2||b.iv.length>4)throw new sjcl.exception.invalid("json decrypt: invalid parameters");if(typeof a==="string"){f=sjcl.misc.cachedPbkdf2(a,b);a=f.key.slice(0,b.ks/32);
|
||||||
|
b.salt=f.salt}if(typeof c==="string")c=sjcl.codec.utf8String.toBits(c);f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.c(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';
|
||||||
|
break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],1)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type");}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++){if(!(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");
|
||||||
|
b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4])}return b},c:function(a,b,c){if(a===undefined)a={};if(b===undefined)return a;var d;for(d in b)if(b.hasOwnProperty(d)){if(c&&a[d]!==undefined&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},V:function(a,b){var c={},d;for(d in a)if(a.hasOwnProperty(d)&&a[d]!==b[d])c[d]=a[d];return c},W:function(a,b){var c={},d;for(d=0;d<b.length;d++)if(a[b[d]]!==
|
||||||
|
undefined)c[b[d]]=a[b[d]];return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.S={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.S,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===undefined?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
|
158
lib/vizhash_gd_zero.php
Normal file
158
lib/vizhash_gd_zero.php
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
<?php
|
||||||
|
// VizHash_GD 0.0.4 beta ZeroBin 0.15
|
||||||
|
// Visual Hash implementation in php4+GD, stripped down and modified version for ZeroBin
|
||||||
|
// See: http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd
|
||||||
|
// This is free software under the zlib/libpng licence
|
||||||
|
// http://www.opensource.org/licenses/zlib-license.php
|
||||||
|
/* Example:
|
||||||
|
$vz = new vizhash16x16();
|
||||||
|
$data = $vz->generate('hello');
|
||||||
|
header('Content-type: image/png');
|
||||||
|
echo $data;
|
||||||
|
exit;
|
||||||
|
*/
|
||||||
|
class vizhash16x16
|
||||||
|
{
|
||||||
|
private $VALUES;
|
||||||
|
private $VALUES_INDEX;
|
||||||
|
private $width;
|
||||||
|
private $height;
|
||||||
|
private $salt;
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->width=16;
|
||||||
|
$this->height=16;
|
||||||
|
|
||||||
|
// Read salt from file (and create it if does not exist).
|
||||||
|
// The salt will make vizhash avatar unique on each ZeroBin installation
|
||||||
|
// to prevent IP checking.
|
||||||
|
$saltfile = 'data/salt.php';
|
||||||
|
if (!is_file($saltfile))
|
||||||
|
file_put_contents($saltfile,'<?php /* |'.$this->randomSalt().'| */ ?>');
|
||||||
|
$items=explode('|',file_get_contents($saltfile));
|
||||||
|
$this->salt = $items[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a 16x16 png corresponding to $text.
|
||||||
|
// Input: $text (string)
|
||||||
|
// Output: PNG data. Or empty string if GD is not available.
|
||||||
|
function generate($text)
|
||||||
|
{
|
||||||
|
if (!function_exists('gd_info')) return '';
|
||||||
|
|
||||||
|
// We hash the input string.
|
||||||
|
$hash=hash('sha1',$text.$this->salt).hash('md5',$text.$this->salt);
|
||||||
|
$hash=$hash.strrev($hash); # more data to make graphics
|
||||||
|
|
||||||
|
// We convert the hash into an array of integers.
|
||||||
|
$this->VALUES=array();
|
||||||
|
for($i=0; $i<strlen($hash); $i=$i+2){ array_push($this->VALUES,hexdec(substr($hash,$i,2))); }
|
||||||
|
$this->VALUES_INDEX=0; // to walk the array.
|
||||||
|
|
||||||
|
// Then use these integers to drive the creation of an image.
|
||||||
|
$image = imagecreatetruecolor($this->width,$this->height);
|
||||||
|
|
||||||
|
$r0 = $this->getInt();$r=$r0;
|
||||||
|
$g0 = $this->getInt();$g=$g0;
|
||||||
|
$b0 = $this->getInt();$b=$b0;
|
||||||
|
|
||||||
|
// First, create an image with a specific gradient background.
|
||||||
|
$op='v'; if (($this->getInt()%2)==0) { $op='h'; };
|
||||||
|
$image = $this->degrade($image,$op,array($r0,$g0,$b0),array(0,0,0));
|
||||||
|
|
||||||
|
for($i=0; $i<7; $i=$i+1)
|
||||||
|
{
|
||||||
|
$action=$this->getInt();
|
||||||
|
$color = imagecolorallocate($image, $r,$g,$b);
|
||||||
|
$r = ($r0 + $this->getInt()/25)%256;
|
||||||
|
$g = ($g0 + $this->getInt()/25)%256;
|
||||||
|
$b = ($b0 + $this->getInt()/25)%256;
|
||||||
|
$r0=$r; $g0=$g; $b0=$b;
|
||||||
|
$this->drawshape($image,$action,$color);
|
||||||
|
}
|
||||||
|
|
||||||
|
$color = imagecolorallocate($image,$this->getInt(),$this->getInt(),$this->getInt());
|
||||||
|
$this->drawshape($image,$this->getInt(),$color);
|
||||||
|
ob_start();
|
||||||
|
imagepng($image);
|
||||||
|
$imagedata = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
imagedestroy($image);
|
||||||
|
|
||||||
|
return $imagedata;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a large random hexadecimal salt.
|
||||||
|
private function randomSalt()
|
||||||
|
{
|
||||||
|
$randomSalt='';
|
||||||
|
for($i=0;$i<6;$i++) { $randomSalt.=base_convert(mt_rand(),10,16); }
|
||||||
|
return $randomSalt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function getInt() // Returns a single integer from the $VALUES array (0...255)
|
||||||
|
{
|
||||||
|
$v= $this->VALUES[$this->VALUES_INDEX];
|
||||||
|
$this->VALUES_INDEX++;
|
||||||
|
$this->VALUES_INDEX %= count($this->VALUES); // Warp around the array
|
||||||
|
return $v;
|
||||||
|
}
|
||||||
|
private function getX() // Returns a single integer from the array (roughly mapped to image width)
|
||||||
|
{
|
||||||
|
return $this->width*$this->getInt()/256;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getY() // Returns a single integer from the array (roughly mapped to image height)
|
||||||
|
{
|
||||||
|
return $this->height*$this->getInt()/256;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gradient function taken from:
|
||||||
|
# http://www.supportduweb.com/scripts_tutoriaux-code-source-41-gd-faire-un-degrade-en-php-gd-fonction-degrade-imagerie.html
|
||||||
|
private function degrade($img,$direction,$color1,$color2)
|
||||||
|
{
|
||||||
|
if($direction=='h') { $size = imagesx($img); $sizeinv = imagesy($img); }
|
||||||
|
else { $size = imagesy($img); $sizeinv = imagesx($img);}
|
||||||
|
$diffs = array(
|
||||||
|
(($color2[0]-$color1[0])/$size),
|
||||||
|
(($color2[1]-$color1[1])/$size),
|
||||||
|
(($color2[2]-$color1[2])/$size)
|
||||||
|
);
|
||||||
|
for($i=0;$i<$size;$i++)
|
||||||
|
{
|
||||||
|
$r = $color1[0]+($diffs[0]*$i);
|
||||||
|
$g = $color1[1]+($diffs[1]*$i);
|
||||||
|
$b = $color1[2]+($diffs[2]*$i);
|
||||||
|
if($direction=='h') { imageline($img,$i,0,$i,$sizeinv,imagecolorallocate($img,$r,$g,$b)); }
|
||||||
|
else { imageline($img,0,$i,$sizeinv,$i,imagecolorallocate($img,$r,$g,$b)); }
|
||||||
|
}
|
||||||
|
return $img;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawshape($image,$action,$color)
|
||||||
|
{
|
||||||
|
switch($action%7)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
ImageFilledRectangle ($image,$this->getX(),$this->getY(),$this->getX(),$this->getY(),$color);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
ImageFilledEllipse ($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(), $color);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$points = array($this->getX(), $this->getY(), $this->getX(), $this->getY(), $this->getX(), $this->getY(),$this->getX(), $this->getY());
|
||||||
|
ImageFilledPolygon ($image, $points, 4, $color);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
$start=$this->getInt()*360/256; $end=$start+$this->getInt()*180/256;
|
||||||
|
ImageFilledArc ($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(),$start,$end,$color,IMG_ARC_PIE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
357
lib/zerobin.css
Normal file
357
lib/zerobin.css
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
/* ZeroBin 0.15 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
||||||
|
|
||||||
|
/* Hé, t'as vu Idleman, j'ai fait un effort sur les CSS ! ;-) */
|
||||||
|
|
||||||
|
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.
|
||||||
|
Licensed under the BSD License. - http://yuilibrary.com/license/ */
|
||||||
|
html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{*font-size:100%}legend{color:#000}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color:#455463;
|
||||||
|
color:white;
|
||||||
|
min-height:100%;
|
||||||
|
background-image: linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #0F1823), color-stop(1, #455463));
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin-bottom:15px;
|
||||||
|
padding-left:60px; padding-right:60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a { color:#0F388F; }
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size:3.5em;
|
||||||
|
font-weight:700;
|
||||||
|
color:#000;
|
||||||
|
position:relative;
|
||||||
|
display:inline;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:before {
|
||||||
|
content:attr(title);
|
||||||
|
position:absolute;
|
||||||
|
color:rgba(255,255,255,0.15);
|
||||||
|
top:1px;
|
||||||
|
left:1px;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color:#000;
|
||||||
|
font-size:1em;
|
||||||
|
display:inline;
|
||||||
|
font-style:italic;
|
||||||
|
font-weight:bold;
|
||||||
|
position:relative;
|
||||||
|
bottom:8px;}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color:#94a3b4;
|
||||||
|
font-size:0.7em;
|
||||||
|
display:inline;
|
||||||
|
position:relative;
|
||||||
|
bottom:8px;}
|
||||||
|
|
||||||
|
#aboutbox {
|
||||||
|
font-size:0.85em;
|
||||||
|
color: #94a3b4;
|
||||||
|
padding: 4px 8px 4px 16px;
|
||||||
|
position:relative;
|
||||||
|
top:10px;
|
||||||
|
border-left: 2px solid #94a3b4;
|
||||||
|
float:right;
|
||||||
|
width:60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#aboutbox a { color: #94a3b4; }
|
||||||
|
|
||||||
|
textarea#message,div#cleartext,.replymessage {
|
||||||
|
clear:both;
|
||||||
|
color:black;
|
||||||
|
background-color:#fff;
|
||||||
|
white-space:pre-wrap;
|
||||||
|
font-family:Consolas,"Lucida Console","DejaVu Sans Mono",Monaco,monospace;
|
||||||
|
font-size:9pt;
|
||||||
|
border: 1px solid #28343F;
|
||||||
|
padding:5px;
|
||||||
|
box-sizing:border-box;
|
||||||
|
-webkit-box-sizing:border-box;
|
||||||
|
-moz-box-sizing:border-box;
|
||||||
|
-ms-box-sizing:border-box;
|
||||||
|
-o-box-sizing:border-box;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#status {
|
||||||
|
clear:both;
|
||||||
|
padding:5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div#pastelink {
|
||||||
|
background-color:#1F2833;
|
||||||
|
color:white;
|
||||||
|
padding:4px 12px;
|
||||||
|
clear:both;
|
||||||
|
-moz-box-shadow: inset 0px 2px 2px #000;
|
||||||
|
-webkit-box-shadow: inset 0px 2px 2px #000;
|
||||||
|
box-shadow: inset 0px 2px 5px #000;
|
||||||
|
}
|
||||||
|
div#pastelink a { color:white; }
|
||||||
|
div#pastelink button { margin-left:11px }
|
||||||
|
div#toolbar, div#status { margin-bottom:5px; }
|
||||||
|
|
||||||
|
button,.button,div#expiration,div#language {
|
||||||
|
color:#fff;
|
||||||
|
background-color:#323B47;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-position:center left;
|
||||||
|
padding:4px 8px;
|
||||||
|
font-size:1em;
|
||||||
|
margin-right:5px;
|
||||||
|
display:inline;
|
||||||
|
background-image: linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #323B47), color-stop(1, #51606E));
|
||||||
|
border: 1px solid #28343F;
|
||||||
|
-moz-box-shadow: inset 0px 1px 2px #647384;
|
||||||
|
-webkit-box-shadow: inset 0px 1px 2px #647384;
|
||||||
|
box-shadow: inset 0px 1px 2px #647384;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
-moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background-image: linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #424B57), color-stop(1, #61707E));
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
background-image: linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #51606E), color-stop(1, #323B47));
|
||||||
|
position:relative;
|
||||||
|
top:1px;
|
||||||
|
}
|
||||||
|
button:disabled, .buttondisabled {
|
||||||
|
background:#ccc;
|
||||||
|
color:#888;
|
||||||
|
top:0px;
|
||||||
|
}
|
||||||
|
button img {
|
||||||
|
margin-right:8px;
|
||||||
|
position:relative;
|
||||||
|
top:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#expiration, div#language, div#opendisc {
|
||||||
|
background-color:#414D5A;
|
||||||
|
padding:6px 8px;
|
||||||
|
margin:0px 5px 0px 0px;;
|
||||||
|
position: relative;
|
||||||
|
bottom:1px; /* WTF ? Why is this shifted by 1 pixel ? */
|
||||||
|
}
|
||||||
|
div#expiration select, div#language select {
|
||||||
|
color:#eee;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div#expiration select option, div#language select option {
|
||||||
|
color:#eee;
|
||||||
|
background: #414D5A;
|
||||||
|
background-color:#414D5A;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#remainingtime {
|
||||||
|
color: #94a3b4;
|
||||||
|
display:inline;
|
||||||
|
font-size:0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.foryoureyesonly {
|
||||||
|
color: yellow !important;
|
||||||
|
font-size: 1em !important;
|
||||||
|
font-weight:bold !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button#newbutton { float:right; margin-right:0px;margin-bottom:5px; display:inline; }
|
||||||
|
input { color:#777; font-size:1em; padding:6px; border: 1px solid #28343F; }
|
||||||
|
|
||||||
|
.nonworking {
|
||||||
|
background-color:#fff;
|
||||||
|
color:#000;
|
||||||
|
width:100%;
|
||||||
|
text-align:center;
|
||||||
|
font-weight:bold;
|
||||||
|
font-size:10pt;
|
||||||
|
-webkit-border-radius:4px;
|
||||||
|
-moz-border-radius:4px;
|
||||||
|
border-radius:4px;
|
||||||
|
padding:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#ienotice {
|
||||||
|
background-color:#7E98AF;
|
||||||
|
color:#000;
|
||||||
|
font-size:0.85em;
|
||||||
|
padding:3px 5px;
|
||||||
|
text-align:center;
|
||||||
|
-webkit-border-radius:4px;
|
||||||
|
-moz-border-radius:4px;
|
||||||
|
border-radius:4px;
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#ienotice a {
|
||||||
|
color:black;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#oldienotice {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorMessage {
|
||||||
|
background-color:#FF7979 !important;
|
||||||
|
color:#FF0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --- discussion related CSS ------- */
|
||||||
|
|
||||||
|
|
||||||
|
div#discussion { /* Discussion container */
|
||||||
|
margin-top:20px;
|
||||||
|
width:100%;
|
||||||
|
margin-left:-30px;
|
||||||
|
min-width:200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size:1.2em;
|
||||||
|
color: #94A3B4;
|
||||||
|
font-style:italic;
|
||||||
|
font-weight:bold;
|
||||||
|
position:relative;
|
||||||
|
margin-left:30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.comment /* One single reply */
|
||||||
|
{
|
||||||
|
background-color:#CECED6;
|
||||||
|
color:#000;
|
||||||
|
white-space:pre-wrap;
|
||||||
|
font-family:Consolas,"Lucida Console","DejaVu Sans Mono",Monaco,monospace;
|
||||||
|
font-size:9pt;
|
||||||
|
border-left: 1px solid #859AAE;
|
||||||
|
border-top: 1px solid #859AAE;
|
||||||
|
padding:5px 0px 5px 5px;
|
||||||
|
margin-left:30px;
|
||||||
|
-moz-box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
|
||||||
|
-webkit-box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
|
||||||
|
box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
|
||||||
|
min-width:200px;
|
||||||
|
overflow:auto;
|
||||||
|
}
|
||||||
|
/* FIXME: Add min-width */
|
||||||
|
|
||||||
|
div.reply {
|
||||||
|
margin: 5px 0px 0px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#replystatus {
|
||||||
|
display:inline;
|
||||||
|
padding:1px 7px;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.comment button {
|
||||||
|
color:#446;
|
||||||
|
background-color:#aab;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-position:center left;
|
||||||
|
padding:0px 2px;
|
||||||
|
font-size:0.73em;
|
||||||
|
margin: 3px 5px 3px 0px;
|
||||||
|
display:inline;
|
||||||
|
background-image: linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #aab), color-stop(1, #ccc));
|
||||||
|
border: 1px solid #ccd;
|
||||||
|
-moz-box-shadow: inset 0px 1px 2px #ddd;
|
||||||
|
-webkit-box-shadow: inset 0px 1px 2px #fff;
|
||||||
|
box-shadow: inset 0px 1px 2px #eee;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
-moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;
|
||||||
|
}
|
||||||
|
div.comment button:hover {
|
||||||
|
background-image: linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccd), color-stop(1, #fff));
|
||||||
|
}
|
||||||
|
div.comment button:active {
|
||||||
|
background-image: linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(1, #889));
|
||||||
|
position:relative;
|
||||||
|
top:1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.comment input {
|
||||||
|
padding:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea#replymessage {
|
||||||
|
margin-top:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.commentmeta {
|
||||||
|
color: #fff;
|
||||||
|
background-color:#8EA0B2;
|
||||||
|
margin-bottom:3px;
|
||||||
|
padding:0px 0px 0px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.commentdate {
|
||||||
|
color: #BFCEDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.vizhash {
|
||||||
|
width:16px;
|
||||||
|
height:16px;
|
||||||
|
position:relative;
|
||||||
|
top:2px;
|
||||||
|
left:-3px;
|
||||||
|
}
|
364
lib/zerobin.js
Normal file
364
lib/zerobin.js
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
/* ZeroBin 0.15 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
||||||
|
|
||||||
|
// Immediately start random number generator collector.
|
||||||
|
sjcl.random.startCollectors();
|
||||||
|
|
||||||
|
// Converts a duration (in seconds) into human readable format.
|
||||||
|
function secondsToHuman(seconds)
|
||||||
|
{
|
||||||
|
if (seconds<60) { var v=Math.floor(seconds); return v+' second'+((v>1)?'s':''); }
|
||||||
|
if (seconds<60*60) { var v=Math.floor(seconds/60); return v+' minute'+((v>1)?'s':''); }
|
||||||
|
if (seconds<60*60*24) { var v=Math.floor(seconds/(60*60)); return v+' hour'+((v>1)?'s':''); }
|
||||||
|
// If less than 2 months, display in days:
|
||||||
|
if (seconds<60*60*24*60) { var v=Math.floor(seconds/(60*60*24)); return v+' day'+((v>1)?'s':''); }
|
||||||
|
var v=Math.floor(seconds/(60*60*24*30)); return v+' month'+((v>1)?'s':'');
|
||||||
|
}
|
||||||
|
// Compress a message (deflate compression). Returns base64 encoded data.
|
||||||
|
function compress(message) { return Base64.toBase64(RawDeflate.deflate(Base64.utob(message))); }
|
||||||
|
|
||||||
|
// Decompress a message compressed with compress().
|
||||||
|
function decompress(data) { return Base64.btou(RawDeflate.inflate(Base64.fromBase64(data))) }
|
||||||
|
|
||||||
|
// Compress, then encrypt message with key.
|
||||||
|
function zeroCipher(key,message)
|
||||||
|
{
|
||||||
|
return sjcl.encrypt(key,compress(message));
|
||||||
|
}
|
||||||
|
// Decrypt message with key, then decompress.
|
||||||
|
function zeroDecipher(key,data)
|
||||||
|
{
|
||||||
|
return decompress(sjcl.decrypt(key,data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the current script location (without search or hash part of the URL).
|
||||||
|
// eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/
|
||||||
|
function scriptLocation()
|
||||||
|
{
|
||||||
|
return window.location.href.substring(0,window.location.href.length
|
||||||
|
-window.location.search.length -window.location.hash.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the paste unique identifier from the URL
|
||||||
|
// eg. 'c05354954c49a487'
|
||||||
|
function pasteID()
|
||||||
|
{
|
||||||
|
return window.location.search.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set text of a DOM element (required for IE)
|
||||||
|
// This is equivalent to element.text(text)
|
||||||
|
// Input: element : a DOM element.
|
||||||
|
// text : the text to enter.
|
||||||
|
function setElementText(element,text)
|
||||||
|
{
|
||||||
|
if ($('div#oldienotice').is(":visible")) // For IE<10.
|
||||||
|
{
|
||||||
|
// IE<10 do not support white-space:pre-wrap; so we have to do this BIG UGLY STINKING THING.
|
||||||
|
element.text(text.replace(/\n/ig,'{BIG_UGLY_STINKING_THING__OH_GOD_I_HATE_IE}'));
|
||||||
|
element.html(element.text().replace(/{BIG_UGLY_STINKING_THING__OH_GOD_I_HATE_IE}/ig,"\r\n<br>"));
|
||||||
|
}
|
||||||
|
else // for other (sane) browsers:
|
||||||
|
{
|
||||||
|
element.text(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show decrypted text in the display area, including discussion (if open)
|
||||||
|
// Input: messages (Array) : Array of messages to display (items = array with keys ('data','meta')
|
||||||
|
// key (string): decryption key
|
||||||
|
function displayMessages(key,comments)
|
||||||
|
{
|
||||||
|
try { // Try to decrypt the paste.
|
||||||
|
var cleartext = zeroDecipher(key,comments[0].data);
|
||||||
|
} catch(err) {
|
||||||
|
$('div#cleartext').hide();
|
||||||
|
$('button#clonebutton').hide();
|
||||||
|
showError('Could not decrypt data (Wrong key ?)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setElementText($('div#cleartext'),cleartext);
|
||||||
|
urls2links($('div#cleartext')); // Convert URLs to clickable links.
|
||||||
|
|
||||||
|
// Display paste expiration.
|
||||||
|
if (comments[0].meta.expire_date) $('div#remainingtime').removeClass('foryoureyesonly').text('This document will expire in '+secondsToHuman(comments[0].meta.remaining_time)+'.').show();
|
||||||
|
if (comments[0].meta.burnafterreading)
|
||||||
|
{
|
||||||
|
$('div#remainingtime').addClass('foryoureyesonly').text('FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.').show();
|
||||||
|
$('button#clonebutton').hide(); // Discourage cloning (as it can't really be prevented).
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the discussion is opened on this paste, display it.
|
||||||
|
if (comments[0].meta.opendiscussion)
|
||||||
|
{
|
||||||
|
$('div#comments').html('');
|
||||||
|
for (var i = 1; i < comments.length; i++) // For each comment.
|
||||||
|
{
|
||||||
|
var comment=comments[i];
|
||||||
|
var cleartext="[Could not decrypt comment ; Wrong key ?]";
|
||||||
|
try { cleartext = zeroDecipher(key,comment.data); } catch(err) { }
|
||||||
|
var place = $('div#comments');
|
||||||
|
// If parent comment exists, display below (CSS will automatically shift it right.)
|
||||||
|
var cname = 'div#comment_'+comment.meta.parentid
|
||||||
|
if ($(cname).length) place = $(cname); // If the element exists in page
|
||||||
|
var divComment = $('<div class="comment" id="comment_'+comment.meta.commentid+'">'
|
||||||
|
+'<div class="commentmeta"><span class="nickname"></span><span class="commentdate"></span></div><div class="commentdata"></div>'
|
||||||
|
+'<button onclick="open_reply($(this),\''+comment.meta.commentid+'\');return false;">Reply</button>'
|
||||||
|
+'</div>');
|
||||||
|
setElementText(divComment.find('div.commentdata'),cleartext);
|
||||||
|
urls2links(divComment.find('div.commentdata')); // Convert URLs to clickable links in comment.
|
||||||
|
divComment.find('span.nickname').html('<i>(Anonymous)</i>');
|
||||||
|
// Try to get optional nickname:
|
||||||
|
try { divComment.find('span.nickname').text(zeroDecipher(key,comment.meta.nickname));} catch(err) { }
|
||||||
|
divComment.find('span.commentdate').text(' ('+(new Date(comment.meta.postdate*1000).toUTCString())+')').attr('title','CommentID: '+comment.meta.commentid);
|
||||||
|
// If an avatar is available, display it.
|
||||||
|
if (comment.meta.vizhash)
|
||||||
|
divComment.find('span.nickname').before('<img src="'+comment.meta.vizhash+'" class="vizhash" title="Anonymous avatar (Vizhash of the IP address)" />');
|
||||||
|
place.append(divComment);
|
||||||
|
}
|
||||||
|
$('div#comments').append('<div class="comment"><button onclick="open_reply($(this),\''+pasteID()+'\');return false;">Add comment</button></div>');
|
||||||
|
$('div#discussion').show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the comment entry when clicking the "Reply" button of a comment.
|
||||||
|
// source = element which emitted the event.
|
||||||
|
// commentid = identifier of the comment we want to reply to.
|
||||||
|
function open_reply(source,commentid)
|
||||||
|
{
|
||||||
|
$('div.reply').remove(); // Remove any other reply area.
|
||||||
|
source.after('<div class="reply">'
|
||||||
|
+'<input type="text" id="nickname" title="Optional nickname..." value="Optional nickname..." />'
|
||||||
|
+'<textarea id="replymessage" class="replymessage" cols="80" rows="7"></textarea>'
|
||||||
|
+'<br><button id="replybutton" onclick="send_comment(\''+commentid+'\');return false;">Post comment</button>'
|
||||||
|
+'<div id="replystatus"> </div>'
|
||||||
|
+'</div>');
|
||||||
|
$('input#nickname').focus(function() {
|
||||||
|
$(this).css('color', '#000');
|
||||||
|
if($(this).val()==$(this).attr('title')) $(this).val('');
|
||||||
|
});
|
||||||
|
$('textarea#replymessage').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a reply in a discussion.
|
||||||
|
// Input: parentid : the comment identifier we want to send a reply to.
|
||||||
|
function send_comment(parentid)
|
||||||
|
{
|
||||||
|
if ($('textarea#replymessage').val().length==0) return; // Do not send if no data.
|
||||||
|
showStatus('Sending comment...',spin=true);
|
||||||
|
var cipherdata=zeroCipher(pageKey(), $('textarea#replymessage').val());
|
||||||
|
var ciphernickname='';
|
||||||
|
var nick=$('input#nickname').val();
|
||||||
|
if (nick!='' && nick!='Optional nickname...') ciphernickname=ciphernickname=zeroCipher(pageKey(),nick);
|
||||||
|
var data_to_send = { data:cipherdata,
|
||||||
|
parentid: parentid,
|
||||||
|
pasteid: pasteID(),
|
||||||
|
nickname: ciphernickname
|
||||||
|
};
|
||||||
|
$.post(scriptLocation(), data_to_send ,'json' )
|
||||||
|
.error( function() { showError('Comment could not be sent (serveur error or not responding).'); } )
|
||||||
|
.success(function(data)
|
||||||
|
{
|
||||||
|
if (data.status==0)
|
||||||
|
{
|
||||||
|
showStatus('Comment posted.');
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
else if (data.status==1)
|
||||||
|
{
|
||||||
|
showError('Could not post comment: '+data.message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showError('Could not post comment.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a new paste to server
|
||||||
|
function send_data()
|
||||||
|
{
|
||||||
|
if ($('textarea#message').val().length==0) return; // Do not send if no data.
|
||||||
|
showStatus('Sending paste...',spin=true);
|
||||||
|
var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8,0),0);
|
||||||
|
var cipherdata = zeroCipher(randomkey,$('textarea#message').val());
|
||||||
|
var data_to_send = { data:cipherdata,
|
||||||
|
expire:$('select#pasteExpiration').val(),
|
||||||
|
opendiscussion:$('input#opendiscussion').is(':checked')?1:0
|
||||||
|
};
|
||||||
|
$.post(scriptLocation(), data_to_send ,'json' )
|
||||||
|
.error( function() { showError('Data could not be sent (serveur error or not responding).'); } )
|
||||||
|
.success(function(data)
|
||||||
|
{
|
||||||
|
if (data.status==0)
|
||||||
|
{
|
||||||
|
stateExistingPaste();
|
||||||
|
var url=scriptLocation()+"?"+data.id+'#'+randomkey;
|
||||||
|
showStatus('');
|
||||||
|
$('div#pastelink').html('Your paste is <a href="'+url+'">'+url+'</a>');
|
||||||
|
$('div#pastelink').append(' <button id="shortenbutton" onclick="document.location=\''+shortenUrl(url)+'\'"><img src="lib/icon_shorten.png" width="13" height="15" />Shorten URL</button>').show();
|
||||||
|
setElementText($('div#cleartext'),$('textarea#message').val());
|
||||||
|
urls2links($('div#cleartext'));
|
||||||
|
showStatus('');
|
||||||
|
}
|
||||||
|
else if (data.status==1)
|
||||||
|
{
|
||||||
|
showError('Could not create paste: '+data.message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showError('Could not create paste.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the screen in "New paste" mode.
|
||||||
|
function stateNewPaste()
|
||||||
|
{
|
||||||
|
$('button#sendbutton').show();
|
||||||
|
$('button#clonebutton').hide();
|
||||||
|
$('div#expiration').show();
|
||||||
|
$('div#remainingtime').hide();
|
||||||
|
$('div#language').hide(); // $('#language').show();
|
||||||
|
$('input#password').hide(); //$('#password').show();
|
||||||
|
$('div#opendisc').show();
|
||||||
|
$('button#newbutton').show();
|
||||||
|
$('div#pastelink').hide();
|
||||||
|
$('textarea#message').text('');
|
||||||
|
$('textarea#message').show();
|
||||||
|
$('div#cleartext').hide();
|
||||||
|
$('div#message').focus();
|
||||||
|
$('div#discussion').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the screen in "Existing paste" mode.
|
||||||
|
function stateExistingPaste()
|
||||||
|
{
|
||||||
|
$('button#sendbutton').hide();
|
||||||
|
if (!$('div#oldienotice').is(":visible")) $('button#clonebutton').show(); // No "clone" for IE<10.
|
||||||
|
$('button#clonebutton').show();// FIXME
|
||||||
|
$('div#expiration').hide();
|
||||||
|
$('div#language').hide();
|
||||||
|
$('input#password').hide();
|
||||||
|
$('div#opendisc').hide();
|
||||||
|
$('button#newbutton').show();
|
||||||
|
$('div#pastelink').hide();
|
||||||
|
$('textarea#message').hide();
|
||||||
|
$('div#cleartext').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone the current paste.
|
||||||
|
function clonePaste()
|
||||||
|
{
|
||||||
|
stateNewPaste();
|
||||||
|
showStatus('');
|
||||||
|
$('textarea#message').text($('div#cleartext').text());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new paste.
|
||||||
|
function newPaste()
|
||||||
|
{
|
||||||
|
stateNewPaste();
|
||||||
|
showStatus('');
|
||||||
|
$('textarea#message').text('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display an error message
|
||||||
|
// (We use the same function for paste and reply to comments)
|
||||||
|
function showError(message)
|
||||||
|
{
|
||||||
|
$('div#status').addClass('errorMessage').text(message);
|
||||||
|
$('div#replystatus').addClass('errorMessage').text(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display status
|
||||||
|
// (We use the same function for paste and reply to comments)
|
||||||
|
// message (string) = text to display
|
||||||
|
// spin (boolean, optional) = tell if the "spinning" animation should be displayed.
|
||||||
|
function showStatus(message,spin)
|
||||||
|
{
|
||||||
|
$('div#replystatus').removeClass('errorMessage');
|
||||||
|
$('div#replystatus').text(message);
|
||||||
|
if (!message) { $('div#status').html(' '); return; }
|
||||||
|
if (message=='') { $('div#status').html(' '); return; }
|
||||||
|
$('div#status').removeClass('errorMessage');
|
||||||
|
$('div#status').text(message);
|
||||||
|
if (spin)
|
||||||
|
{
|
||||||
|
var img = '<img src="lib/busy.gif" style="width:16px;height:9px;margin:0px 4px 0px 0px;" />';
|
||||||
|
$('div#status').prepend(img);
|
||||||
|
$('div#replystatus').prepend(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate link to URL shortener.
|
||||||
|
function shortenUrl(url)
|
||||||
|
{
|
||||||
|
return 'http://snipurl.com/site/snip?link='+encodeURIComponent(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert URLs to clickable links.
|
||||||
|
// Input: element : a jQuery DOM element.
|
||||||
|
// Example URLs to handle:
|
||||||
|
// magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
|
||||||
|
// http://localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
|
||||||
|
// http://user:password@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
|
||||||
|
// FIXME: add ppa & apt links.
|
||||||
|
function urls2links(element)
|
||||||
|
{
|
||||||
|
var re = /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig;
|
||||||
|
element.html(element.html().replace(re,'<a href="$1" rel="nofollow">$1</a>'));
|
||||||
|
var re = /((magnet):[\w?=&.\/-;#@~%+-]+)/ig;
|
||||||
|
element.html(element.html().replace(re,'<a href="$1">$1</a>'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the deciphering key stored in anchor part of the URL
|
||||||
|
function pageKey()
|
||||||
|
{
|
||||||
|
var key = window.location.hash.substring(1); // Get key
|
||||||
|
|
||||||
|
// Some stupid web 2.0 services and redirectors add data AFTER the anchor
|
||||||
|
// (such as &utm_source=...).
|
||||||
|
// We will strip any additional data.
|
||||||
|
|
||||||
|
// First, strip everything after the equal sign (=) which signals end of base64 string.
|
||||||
|
i = key.indexOf('='); if (i>-1) { key = key.substring(0,i+1); }
|
||||||
|
|
||||||
|
// If the equal sign was not present, some parameters may remain:
|
||||||
|
i = key.indexOf('&'); if (i>-1) { key = key.substring(0,i); }
|
||||||
|
|
||||||
|
// Then add trailing equal sign if it's missing
|
||||||
|
if (key.charAt(key.length-1)!=='=') key+='=';
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
$('select#pasteExpiration').change(function() {
|
||||||
|
if ($(this).val()=='burn') { $('div#opendisc').addClass('buttondisabled'); $('input#opendiscussion').attr('disabled',true); }
|
||||||
|
else { $('div#opendisc').removeClass('buttondisabled'); $('input#opendiscussion').removeAttr('disabled'); }
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if ($('div#cipherdata').text().length>1) // Display an existing paste
|
||||||
|
{
|
||||||
|
if (window.location.hash.length==0) // Missing decryption key in URL ?
|
||||||
|
{
|
||||||
|
showError('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL ?)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var messages = jQuery.parseJSON($('div#cipherdata').text()); // List of messages to display
|
||||||
|
stateExistingPaste(); // Show proper elements on screen.
|
||||||
|
displayMessages(pageKey(),messages);
|
||||||
|
}
|
||||||
|
else if ($('div#errormessage').text().length>1) // Display error message from php code.
|
||||||
|
{
|
||||||
|
showError($('div#errormessage').text());
|
||||||
|
}
|
||||||
|
else // Create a new paste.
|
||||||
|
{
|
||||||
|
newPaste();
|
||||||
|
}
|
||||||
|
});
|
81
tpl/page.html
Normal file
81
tpl/page.html
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>ZeroBin</title>
|
||||||
|
<link type="text/css" rel="stylesheet" href="lib/zerobin.css?{$VERSION|rawurlencode}#" />
|
||||||
|
<script src="lib/jquery.js#"></script>
|
||||||
|
<script src="lib/sjcl.js#"></script>
|
||||||
|
<script src="lib/base64.js#"></script>
|
||||||
|
<script src="lib/rawdeflate.js#"></script>
|
||||||
|
<script src="lib/rawinflate.js#"></script>
|
||||||
|
<script src="lib/zerobin.js?{$VERSION|rawurlencode}#"></script>
|
||||||
|
|
||||||
|
<!--[if lt IE 10]>
|
||||||
|
<style> body {padding-left:60px;padding-right:60px;} div#ienotice {display:block;} </style>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<!--[if lt IE 10]>
|
||||||
|
<style> div#ienotice {display:block; } div#oldienotice {display:block; } </style>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="aboutbox">
|
||||||
|
ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data.
|
||||||
|
Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES.
|
||||||
|
More information on the <a href="http://sebsauvage.net/wiki/doku.php?id=php:zerobin">project page</a>.<br />
|
||||||
|
<span style="text-decoration:blink;font-size:10pt;color:#a4b3c4;">▶</span> Note: This is a test service:
|
||||||
|
Data may be deleted anytime. Kittens will die if you abuse this service.
|
||||||
|
</div>
|
||||||
|
<h1 title="ZeroBin" onclick="window.location.href=scriptLocation();return false;">ZeroBin</h1><br>
|
||||||
|
<h2>Because ignorance is bliss</h2><br>
|
||||||
|
<h3>{$VERSION}</h3>
|
||||||
|
<noscript><div class="nonworking">Javascript is required for ZeroBin to work.<br>Sorry for the inconvenience.</div></noscript>
|
||||||
|
<div id="oldienotice" class="nonworking">ZeroBin requires a modern browser to work.</div>
|
||||||
|
<div id="ienotice">Still using Internet Explorer ? Do yourself a favor, switch to a modern browser:
|
||||||
|
<a href="http://www.mozilla.org/firefox/">Firefox</a>,
|
||||||
|
<a href="http://www.opera.com/">Opera</a>,
|
||||||
|
<a href="http://www.google.com/chrome">Chrome</a>,
|
||||||
|
<a href="http://www.apple.com/safari">Safari</a>...
|
||||||
|
</div>
|
||||||
|
<div id="status"> </div>
|
||||||
|
<div id="errormessage" style="display:none">{$ERRORMESSAGE|htmlspecialchars}</div>
|
||||||
|
<div id="toolbar">
|
||||||
|
<button id="newbutton" onclick="window.location.href=scriptLocation();return false;" style="display:none;"><img src="lib/icon_new.png#" width="11" height="15" />New</button>
|
||||||
|
<button id="sendbutton" onclick="send_data();return false;" style="display:none;"><img src="lib/icon_send.png#" width="18" height="15" />Send</button>
|
||||||
|
<button id="clonebutton" onclick="clonePaste();return false;" style="display:none;"><img src="lib/icon_clone.png#" width="15" height="17" />Clone</button>
|
||||||
|
<div id="expiration" style="display:none;">Expire:
|
||||||
|
<select id="pasteExpiration" name="pasteExpiration">
|
||||||
|
<option value="burn">Burn after reading</option>
|
||||||
|
<option value="10min">10 minutes</option>
|
||||||
|
<option value="1hour">1 hour</option>
|
||||||
|
<option value="1day">1 day</option>
|
||||||
|
<option value="1month" selected="selected">1 month</option>
|
||||||
|
<option value="1year">1 year</option>
|
||||||
|
<option value="never">Never</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="remainingtime" style="display:none;"></div>
|
||||||
|
<div id="language" style="display:none;">
|
||||||
|
<select name="language">
|
||||||
|
<option value="language" selected="selected">Language</option>
|
||||||
|
<option value="C/C++">C/C++</option>
|
||||||
|
<option value="php">php</option>
|
||||||
|
<option value="python">Python</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input id="password" value="Optional password..." style="display:none;" />
|
||||||
|
<div id="opendisc" class="button" style="display:none;">
|
||||||
|
<input type="checkbox" id="opendiscussion" name="opendiscussion" />
|
||||||
|
<label for="opendiscussion">Open discussion</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="pastelink" style="display:none;"></div>
|
||||||
|
<div id="cleartext" style="display:none;"></div>
|
||||||
|
<textarea id="message" name="message" cols="80" rows="25" style="display:none;"></textarea>
|
||||||
|
<div id="discussion" style="display:none;">
|
||||||
|
<h4>Discussion</h4>
|
||||||
|
<div id="comments"></div>
|
||||||
|
</div>
|
||||||
|
<div id="cipherdata" style="display:none;">{$CIPHERDATA}</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue