Initial commit of version 0.15 alpha.

This commit is contained in:
Sebastien SAUVAGE 2012-04-21 21:59:45 +02:00
commit 52630374e5
17 changed files with 4972 additions and 0 deletions

33
README Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

BIN
lib/icon_clone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

BIN
lib/icon_new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

BIN
lib/icon_send.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

BIN
lib/icon_shorten.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

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

File diff suppressed because it is too large Load diff

1671
lib/rawdeflate.js Normal file

File diff suppressed because it is too large Load diff

753
lib/rawinflate.js Normal file
View 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
View 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
View 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
View 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
View 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">&nbsp;</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('&nbsp;&nbsp;<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('&nbsp'); return; }
if (message=='') { $('div#status').html('&nbsp'); 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
View 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;">&#9654;</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 ? &nbsp;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">&nbsp;</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>