├── img ├── busy.gif ├── icon_new.png ├── icon_raw.png ├── icon_send.png └── icon_clone.png ├── .gitignore ├── robots.txt ├── lib ├── serversalt.php ├── vizhash_gd_zero.php └── rain.tpl.class.php ├── README.md ├── js ├── highlight.styles │ └── monokai.css ├── base64.js ├── sjcl.js ├── rawinflate.js ├── zerobin.js └── rawdeflate.js ├── CHANGELOG.md ├── tpl └── page.html ├── css └── zerobin.css └── index.php /img/busy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebsauvage/ZeroBin/HEAD/img/busy.gif -------------------------------------------------------------------------------- /img/icon_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebsauvage/ZeroBin/HEAD/img/icon_new.png -------------------------------------------------------------------------------- /img/icon_raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebsauvage/ZeroBin/HEAD/img/icon_raw.png -------------------------------------------------------------------------------- /img/icon_send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebsauvage/ZeroBin/HEAD/img/icon_send.png -------------------------------------------------------------------------------- /img/icon_clone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebsauvage/ZeroBin/HEAD/img/icon_clone.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore data/ and tmp/ 2 | data/ 3 | tmp/ 4 | # Ignore for safety 5 | .htaccess 6 | .htpasswd -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | # Might as well keep robots away for performance and privacy reasons. 2 | User-agent: * 3 | Allow: /index.php 4 | Disallow: / 5 | -------------------------------------------------------------------------------- /lib/serversalt.php: -------------------------------------------------------------------------------- 1 | ',LOCK_EX); 31 | $items=explode('|',file_get_contents($saltfile)); 32 | return $items[1]; 33 | 34 | } 35 | 36 | ?> -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This repo is unmaintained and outdated! Do not use this code anymore! :warning: 2 | 3 | **If you still use this old code switch to the new maintained repo:** 4 | **:arrow_forward: https://github.com/PrivateBin/PrivateBin** 5 | 6 | You will get more features and up-to-date code. 7 | 8 | ==== 9 | ---- 10 | 11 | ZeroBin 0.19 Alpha 12 | 13 | ==== THIS IS ALPHA SOFTWARE - USE AT YOUR OWN RISKS ==== 14 | 15 | ZeroBin is a minimalist, opensource online pastebin where the server 16 | has zero knowledge of pasted data. Data is encrypted/decrypted in the 17 | browser using 256 bits AES. 18 | 19 | More information on the project page: 20 | http://sebsauvage.net/wiki/doku.php?id=php:zerobin 21 | 22 | ------------------------------------------------------------------------------ 23 | 24 | Copyright (c) 2012 Sébastien SAUVAGE (sebsauvage.net) 25 | 26 | This software is provided 'as-is', without any express or implied warranty. 27 | In no event will the authors be held liable for any damages arising from 28 | the use of this software. 29 | 30 | Permission is granted to anyone to use this software for any purpose, 31 | including commercial applications, and to alter it and redistribute it 32 | freely, subject to the following restrictions: 33 | 34 | 1. The origin of this software must not be misrepresented; you must 35 | not claim that you wrote the original software. If you use this 36 | software in a product, an acknowledgment in the product documentation 37 | would be appreciated but is not required. 38 | 39 | 2. Altered source versions must be plainly marked as such, and must 40 | not be misrepresented as being the original software. 41 | 42 | 3. This notice may not be removed or altered from any source distribution. 43 | 44 | ------------------------------------------------------------------------------ 45 | -------------------------------------------------------------------------------- /js/highlight.styles/monokai.css: -------------------------------------------------------------------------------- 1 | /* 2 | Monokai style - ported by Luigi Maselli - http://grigio.org 3 | */ 4 | 5 | pre code { 6 | display: block; padding: 0.5em; 7 | background: #272822; 8 | } 9 | 10 | pre .tag, 11 | pre .tag .title, 12 | pre .keyword, 13 | pre .literal, 14 | pre .change, 15 | pre .winutils, 16 | pre .flow, 17 | pre .lisp .title, 18 | pre .clojure .built_in, 19 | pre .nginx .title, 20 | pre .tex .special { 21 | color: #F92672; 22 | } 23 | 24 | pre code { 25 | color: #DDD; 26 | } 27 | 28 | pre code .constant { 29 | color: #66D9EF; 30 | } 31 | 32 | pre .class .title { 33 | color: white; 34 | } 35 | 36 | pre .attribute, 37 | pre .symbol, 38 | pre .symbol .string, 39 | pre .value, 40 | pre .regexp { 41 | color: #BF79DB; 42 | } 43 | 44 | pre .tag .value, 45 | pre .string, 46 | pre .subst, 47 | pre .title, 48 | pre .haskell .type, 49 | pre .preprocessor, 50 | pre .ruby .class .parent, 51 | pre .built_in, 52 | pre .sql .aggregate, 53 | pre .django .template_tag, 54 | pre .django .variable, 55 | pre .smalltalk .class, 56 | pre .javadoc, 57 | pre .django .filter .argument, 58 | pre .smalltalk .localvars, 59 | pre .smalltalk .array, 60 | pre .attr_selector, 61 | pre .pseudo, 62 | pre .addition, 63 | pre .stream, 64 | pre .envvar, 65 | pre .apache .tag, 66 | pre .apache .cbracket, 67 | pre .tex .command, 68 | pre .prompt { 69 | color: #A6E22E; 70 | } 71 | 72 | pre .comment, 73 | pre .java .annotation, 74 | pre .python .decorator, 75 | pre .template_comment, 76 | pre .pi, 77 | pre .doctype, 78 | pre .deletion, 79 | pre .shebang, 80 | pre .apache .sqbracket, 81 | pre .tex .formula { 82 | color: #75715E; 83 | } 84 | 85 | pre .keyword, 86 | pre .literal, 87 | pre .css .id, 88 | pre .phpdoc, 89 | pre .title, 90 | pre .haskell .type, 91 | pre .vbscript .built_in, 92 | pre .sql .aggregate, 93 | pre .rsl .built_in, 94 | pre .smalltalk .class, 95 | pre .diff .header, 96 | pre .chunk, 97 | pre .winutils, 98 | pre .bash .variable, 99 | pre .apache .tag, 100 | pre .tex .special, 101 | pre .request, 102 | pre .status { 103 | font-weight: bold; 104 | } 105 | 106 | pre .coffeescript .javascript, 107 | pre .javascript .xml, 108 | pre .tex .formula, 109 | pre .xml .javascript, 110 | pre .xml .vbscript, 111 | pre .xml .css, 112 | pre .xml .cdata { 113 | opacity: 0.5; 114 | } 115 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ZeroBin version history # 2 | 3 | * **Alpha 0.8 (2012-04-11):** 4 | * Source code not published yet. 5 | * Interface completely redesigned. Icons added. 6 | * Now properly supports IE6/7 (ugly display, but it works. "Clone" button is disabled though.) 7 | * Added one level of depth for storage directories (This is better for higher load servers). 8 | * php version is now checked (min: 5.2.6) 9 | * Better checks on posted json data on server. 10 | * Added "1 year" expiration. 11 | * URLs are now converted to clickable links. This include http, https, ftp and magnet links. 12 | * Clickable links include ''rel="nofollow"'' to discourage SEO. 13 | * On my public service (http://sebsauvage.net/paste/) 14 | * All data will be deleted (you were warned - this is a test service) 15 | * Default paste expiration is now 1 month to prevent clogging-up my host. 16 | * **Alpha 0.9 (2012-04-11):** 17 | * Oh bummer... IE 8 is as shitty as IE6/7: Its does not seem to support ''white-space:pre-wrap'' correctly. I had to activate the special handling mode. I still have to test IE 9. 18 | * **Alpha 0.10 (2012-04-12):** 19 | * IE9 does not seem to correctly support ''pre-wrap'' either. Special handling mode activated for all version of IE<10. (Note: **ALL other browsers** correctly support this feature.) 20 | * **Alpha 0.11 (2012-04-12):** 21 | * Automatically ignore parameters (such as &utm_source=...) added //after// the anchor by some stupid Web 2.0 services. 22 | * First public release. 23 | * **Alpha 0.12 (2012-04-18):** 24 | * **DISCUSSIONS !** Now you can enable discussions on your pastes. Of course, posted comments and nickname are also encrypted and the server cannot see them. 25 | * This feature implies a change in storage format. You will have to delete all previous pastes in your ZeroBin. 26 | * Added [[php:vizhash_gd|Vizhash]] as avatars, so you can match posters IP addresses without revealing them. (Same image = same IP). Of course the IP address cannot be deduced from the Vizhash. 27 | * Remaining time before expiration is now displayed. 28 | * Explicit tags were added to CSS and jQuery selectors (eg. div#aaa instead of #aaa) to speed up browser. 29 | * Better cleaning of the URL (to make sure the key is not broken by some stupid redirection service) 30 | * **Alpha 0.13 (2012-04-18):** 31 | * FIXED: ''imageantialias()'' call removed because it's not really usefull and can be a problem on most hosts (if GD is not compiled in php). 32 | * FIXED: $error not properly initialized in index.php 33 | * **Alpha 0.14 (2012-04-20):** 34 | * ADDED: GD presence is checked. 35 | * CHANGED: Traffic limiter data files moved to data/ (→easier rights management) 36 | * ADDED: "Burn after reading" implemented. Opening the URL will display the paste and immediately destroy it on server. 37 | * **Alpha 0.15 (2012-04-20):** 38 | * FIXED: 2 minor corrections to avoid notices in php log. 39 | * FIXED: Sources converted to UTF-8. 40 | * **Alpha 0.15 (2012-04-20):** 41 | * FIXED: 2 minor corrections to avoid notices in php log. 42 | * FIXED: Sources converted to UTF-8. 43 | * **Alpha 0.16**: 44 | * FIXED minor php warnings. 45 | * FIXED: zerobin.js reformated and properly commented. 46 | * FIXED: Directory structure re-organized. 47 | * CHANGED: URL shortening button was removed. (It was bad for privacy.) 48 | * **Alpha 0.17 (2013-02-23)**: 49 | * ADDED: Deletion URL. 50 | * small refactoring. 51 | * improved regex checks. 52 | * larger server alt on installation. 53 | * **Alpha 0.18 (2013-02-24)**: 54 | * ADDED: The resulting URL is automatically selected after pressing "Send". You just have to press CTRL+C. 55 | * ADDED: Automatic syntax highlighting for 53 languages using highlight.js 56 | * ADDED: "5 minutes" and "1 week" expirations. 57 | * ADDED: "Raw text" button. 58 | * jQuery upgraded to 1.9.1 59 | * sjcl upgraded to GitHub master 2013-02-23 60 | * base64.js upgraded to 1.7 61 | * FIXED: Dates in discussion are now proper local dates. 62 | * ADDED: Robot meta tags in HTML to prevent search engines indexing. 63 | * ADDED: Better json checking (including entropy). 64 | * ADDED: Added version to js/css assets URLs in order to prevent some abusive caches to serve an obsolete version of these files when ZeroBin is upgraded. 65 | * "Burn after reading" option has been moved out of Expiration combo to a separate checkbox. Reason is: You can prevent a read-once paste to be available ad vitam eternam on the net. 66 | * **Alpha 0.19 (2013-07-05)**: 67 | * Corrected XSS security flaw which affected IE<10. Other browsers were not affected. 68 | * Corrected spacing display in IE<10. 69 | 70 | -------------------------------------------------------------------------------- /tpl/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ZeroBin 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 22 | 23 | 24 | 25 |
26 | ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. 27 | Data is encrypted/decrypted in the browser using 256 bits AES. 28 | More information on the project page.
29 | Note: This is a test service: 30 | Data may be deleted anytime. Kittens will die if you abuse this service. 31 |
32 |

ZeroBin


33 |

Because ignorance is bliss


34 |

{$VERSION}

35 | 36 |
ZeroBin requires a modern browser to work.
37 |
Still using Internet Explorer ?  Do yourself a favor, switch to a modern browser: 38 | Firefox, 39 | Opera, 40 | Chrome, 41 | Safari... 42 |
43 |
{$STATUS}
44 | 45 |
46 | 47 | 48 | 49 | 50 | 62 | 63 | 67 | 71 | 75 |
76 | 80 | 81 | 82 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /lib/vizhash_gd_zero.php: -------------------------------------------------------------------------------- 1 | generate('hello'); 10 | header('Content-type: image/png'); 11 | echo $data; 12 | exit; 13 | */ 14 | require_once "serversalt.php"; 15 | class vizhash16x16 16 | { 17 | private $VALUES; 18 | private $VALUES_INDEX; 19 | private $width; 20 | private $height; 21 | private $salt; 22 | function __construct() 23 | { 24 | $this->width=16; 25 | $this->height=16; 26 | $this->salt = getServerSalt(); 27 | } 28 | 29 | // Generate a 16x16 png corresponding to $text. 30 | // Input: $text (string) 31 | // Output: PNG data. Or empty string if GD is not available. 32 | function generate($text) 33 | { 34 | if (!function_exists('gd_info')) return ''; 35 | 36 | // We hash the input string. 37 | $hash=hash('sha1',$text.$this->salt).hash('md5',$text.$this->salt); 38 | $hash=$hash.strrev($hash); # more data to make graphics 39 | 40 | // We convert the hash into an array of integers. 41 | $this->VALUES=array(); 42 | for($i=0; $iVALUES,hexdec(substr($hash,$i,2))); } 43 | $this->VALUES_INDEX=0; // to walk the array. 44 | 45 | // Then use these integers to drive the creation of an image. 46 | $image = imagecreatetruecolor($this->width,$this->height); 47 | 48 | $r0 = $this->getInt();$r=$r0; 49 | $g0 = $this->getInt();$g=$g0; 50 | $b0 = $this->getInt();$b=$b0; 51 | 52 | // First, create an image with a specific gradient background. 53 | $op='v'; if (($this->getInt()%2)==0) { $op='h'; }; 54 | $image = $this->degrade($image,$op,array($r0,$g0,$b0),array(0,0,0)); 55 | 56 | for($i=0; $i<7; $i=$i+1) 57 | { 58 | $action=$this->getInt(); 59 | $color = imagecolorallocate($image, $r,$g,$b); 60 | $r = ($r0 + $this->getInt()/25)%256; 61 | $g = ($g0 + $this->getInt()/25)%256; 62 | $b = ($b0 + $this->getInt()/25)%256; 63 | $r0=$r; $g0=$g; $b0=$b; 64 | $this->drawshape($image,$action,$color); 65 | } 66 | 67 | $color = imagecolorallocate($image,$this->getInt(),$this->getInt(),$this->getInt()); 68 | $this->drawshape($image,$this->getInt(),$color); 69 | ob_start(); 70 | imagepng($image); 71 | $imagedata = ob_get_contents(); 72 | ob_end_clean(); 73 | imagedestroy($image); 74 | 75 | return $imagedata; 76 | } 77 | 78 | private function getInt() // Returns a single integer from the $VALUES array (0...255) 79 | { 80 | $v= $this->VALUES[$this->VALUES_INDEX]; 81 | $this->VALUES_INDEX++; 82 | $this->VALUES_INDEX %= count($this->VALUES); // Warp around the array 83 | return $v; 84 | } 85 | private function getX() // Returns a single integer from the array (roughly mapped to image width) 86 | { 87 | return $this->width*$this->getInt()/256; 88 | } 89 | 90 | private function getY() // Returns a single integer from the array (roughly mapped to image height) 91 | { 92 | return $this->height*$this->getInt()/256; 93 | } 94 | 95 | # Gradient function taken from: 96 | # http://www.supportduweb.com/scripts_tutoriaux-code-source-41-gd-faire-un-degrade-en-php-gd-fonction-degrade-imagerie.html 97 | private function degrade($img,$direction,$color1,$color2) 98 | { 99 | if($direction=='h') { $size = imagesx($img); $sizeinv = imagesy($img); } 100 | else { $size = imagesy($img); $sizeinv = imagesx($img);} 101 | $diffs = array( 102 | (($color2[0]-$color1[0])/$size), 103 | (($color2[1]-$color1[1])/$size), 104 | (($color2[2]-$color1[2])/$size) 105 | ); 106 | for($i=0;$i<$size;$i++) 107 | { 108 | $r = $color1[0]+($diffs[0]*$i); 109 | $g = $color1[1]+($diffs[1]*$i); 110 | $b = $color1[2]+($diffs[2]*$i); 111 | if($direction=='h') { imageline($img,$i,0,$i,$sizeinv,imagecolorallocate($img,$r,$g,$b)); } 112 | else { imageline($img,0,$i,$sizeinv,$i,imagecolorallocate($img,$r,$g,$b)); } 113 | } 114 | return $img; 115 | } 116 | 117 | private function drawshape($image,$action,$color) 118 | { 119 | switch($action%7) 120 | { 121 | case 0: 122 | ImageFilledRectangle ($image,$this->getX(),$this->getY(),$this->getX(),$this->getY(),$color); 123 | break; 124 | case 1: 125 | case 2: 126 | ImageFilledEllipse ($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(), $color); 127 | break; 128 | case 3: 129 | $points = array($this->getX(), $this->getY(), $this->getX(), $this->getY(), $this->getX(), $this->getY(),$this->getX(), $this->getY()); 130 | ImageFilledPolygon ($image, $points, 4, $color); 131 | break; 132 | case 4: 133 | case 5: 134 | case 6: 135 | $start=$this->getInt()*360/256; $end=$start+$this->getInt()*180/256; 136 | ImageFilledArc ($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(),$start,$end,$color,IMG_ARC_PIE); 137 | break; 138 | } 139 | } 140 | } 141 | 142 | ?> -------------------------------------------------------------------------------- /js/base64.js: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: base64.js,v 1.7 2012/08/23 10:30:18 dankogai Exp dankogai $ 3 | * 4 | * Licensed under the MIT license. 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * References: 8 | * http://en.wikipedia.org/wiki/Base64 9 | */ 10 | 11 | (function(global){ 12 | 13 | var b64chars 14 | = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 15 | 16 | var b64charcodes = function(){ 17 | var a = []; 18 | var codeA = 'A'.charCodeAt(0); 19 | var codea = 'a'.charCodeAt(0); 20 | var code0 = '0'.charCodeAt(0); 21 | for (var i = 0; i < 26; i ++) a.push(codeA + i); 22 | for (var i = 0; i < 26; i ++) a.push(codea + i); 23 | for (var i = 0; i < 10; i ++) a.push(code0 + i); 24 | a.push('+'.charCodeAt(0)); 25 | a.push('/'.charCodeAt(0)); 26 | return a; 27 | }(); 28 | 29 | var b64tab = function(bin){ 30 | var t = {}; 31 | for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i; 32 | return t; 33 | }(b64chars); 34 | 35 | var stringToArray = function(s){ 36 | var a = []; 37 | for (var i = 0, l = s.length; i < l; i ++) a[i] = s.charCodeAt(i); 38 | return a; 39 | }; 40 | 41 | var convertUTF8ArrayToBase64 = function(bin){ 42 | var padlen = 0; 43 | while (bin.length % 3){ 44 | bin.push(0); 45 | padlen++; 46 | }; 47 | var b64 = []; 48 | for (var i = 0, l = bin.length; i < l; i += 3){ 49 | var c0 = bin[i], c1 = bin[i+1], c2 = bin[i+2]; 50 | if (c0 >= 256 || c1 >= 256 || c2 >= 256) 51 | throw 'unsupported character found'; 52 | var n = (c0 << 16) | (c1 << 8) | c2; 53 | b64.push( 54 | b64charcodes[ n >>> 18], 55 | b64charcodes[(n >>> 12) & 63], 56 | b64charcodes[(n >>> 6) & 63], 57 | b64charcodes[ n & 63] 58 | ); 59 | } 60 | while (padlen--) b64[b64.length - padlen - 1] = '='.charCodeAt(0); 61 | return chunkStringFromCharCodeApply(b64); 62 | }; 63 | 64 | var convertBase64ToUTF8Array = function(b64){ 65 | b64 = b64.replace(/[^A-Za-z0-9+\/]+/g, ''); 66 | var bin = []; 67 | var padlen = b64.length % 4; 68 | for (var i = 0, l = b64.length; i < l; i += 4){ 69 | var n = ((b64tab[b64.charAt(i )] || 0) << 18) 70 | | ((b64tab[b64.charAt(i+1)] || 0) << 12) 71 | | ((b64tab[b64.charAt(i+2)] || 0) << 6) 72 | | ((b64tab[b64.charAt(i+3)] || 0)); 73 | bin.push( 74 | ( n >> 16 ), 75 | ( (n >> 8) & 0xff ), 76 | ( n & 0xff ) 77 | ); 78 | } 79 | bin.length -= [0,0,2,1][padlen]; 80 | return bin; 81 | }; 82 | 83 | var convertUTF16ArrayToUTF8Array = function(uni){ 84 | var bin = []; 85 | for (var i = 0, l = uni.length; i < l; i++){ 86 | var n = uni[i]; 87 | if (n < 0x80) 88 | bin.push(n); 89 | else if (n < 0x800) 90 | bin.push( 91 | 0xc0 | (n >>> 6), 92 | 0x80 | (n & 0x3f)); 93 | else 94 | bin.push( 95 | 0xe0 | ((n >>> 12) & 0x0f), 96 | 0x80 | ((n >>> 6) & 0x3f), 97 | 0x80 | (n & 0x3f)); 98 | } 99 | return bin; 100 | }; 101 | 102 | var convertUTF8ArrayToUTF16Array = function(bin){ 103 | var uni = []; 104 | for (var i = 0, l = bin.length; i < l; i++){ 105 | var c0 = bin[i]; 106 | if (c0 < 0x80){ 107 | uni.push(c0); 108 | }else{ 109 | var c1 = bin[++i]; 110 | if (c0 < 0xe0){ 111 | uni.push(((c0 & 0x1f) << 6) | (c1 & 0x3f)); 112 | }else{ 113 | var c2 = bin[++i]; 114 | uni.push( 115 | ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f) 116 | ); 117 | } 118 | } 119 | } 120 | return uni; 121 | }; 122 | 123 | var convertUTF8StringToBase64 = function(bin){ 124 | return convertUTF8ArrayToBase64(stringToArray(bin)); 125 | }; 126 | 127 | var convertBase64ToUTF8String = function(b64){ 128 | return chunkStringFromCharCodeApply(convertBase64ToUTF8Array(b64)); 129 | }; 130 | 131 | var convertUTF8StringToUTF16Array = function(bin){ 132 | return convertUTF8ArrayToUTF16Array(stringToArray(bin)); 133 | }; 134 | 135 | var convertUTF8ArrayToUTF16String = function(bin){ 136 | return chunkStringFromCharCodeApply(convertUTF8ArrayToUTF16Array(bin)); 137 | }; 138 | 139 | var convertUTF8StringToUTF16String = function(bin){ 140 | return chunkStringFromCharCodeApply( 141 | convertUTF8ArrayToUTF16Array(stringToArray(bin)) 142 | ); 143 | }; 144 | 145 | var convertUTF16StringToUTF8Array = function(uni){ 146 | return convertUTF16ArrayToUTF8Array(stringToArray(uni)); 147 | }; 148 | 149 | var convertUTF16ArrayToUTF8String = function(uni){ 150 | return chunkStringFromCharCodeApply(convertUTF16ArrayToUTF8Array(uni)); 151 | }; 152 | 153 | var convertUTF16StringToUTF8String = function(uni){ 154 | return chunkStringFromCharCodeApply( 155 | convertUTF16ArrayToUTF8Array(stringToArray(uni)) 156 | ); 157 | }; 158 | 159 | /* 160 | * String.fromCharCode.apply will only handle arrays as big as 65536, 161 | * after that it'll return a truncated string with no warning. 162 | */ 163 | var chunkStringFromCharCodeApply = function(arr){ 164 | var strs = [], i; 165 | for (i = 0; i < arr.length; i += 65536){ 166 | strs.push(String.fromCharCode.apply(String, arr.slice(i, i+65536))); 167 | } 168 | return strs.join(''); 169 | }; 170 | 171 | if (global.btoa){ 172 | var btoa = global.btoa; 173 | var convertUTF16StringToBase64 = function (uni){ 174 | return btoa(convertUTF16StringToUTF8String(uni)); 175 | }; 176 | } 177 | else { 178 | var btoa = convertUTF8StringToBase64; 179 | var convertUTF16StringToBase64 = function (uni){ 180 | return convertUTF8ArrayToBase64(convertUTF16StringToUTF8Array(uni)); 181 | }; 182 | } 183 | 184 | if (global.atob){ 185 | var atob = global.atob; 186 | var convertBase64ToUTF16String = function (b64){ 187 | return convertUTF8StringToUTF16String(atob(b64)); 188 | }; 189 | } 190 | else { 191 | var atob = convertBase64ToUTF8String; 192 | var convertBase64ToUTF16String = function (b64){ 193 | return convertUTF8ArrayToUTF16String(convertBase64ToUTF8Array(b64)); 194 | }; 195 | } 196 | 197 | global.Base64 = { 198 | convertUTF8ArrayToBase64:convertUTF8ArrayToBase64, 199 | convertByteArrayToBase64:convertUTF8ArrayToBase64, 200 | convertBase64ToUTF8Array:convertBase64ToUTF8Array, 201 | convertBase64ToByteArray:convertBase64ToUTF8Array, 202 | convertUTF16ArrayToUTF8Array:convertUTF16ArrayToUTF8Array, 203 | convertUTF16ArrayToByteArray:convertUTF16ArrayToUTF8Array, 204 | convertUTF8ArrayToUTF16Array:convertUTF8ArrayToUTF16Array, 205 | convertByteArrayToUTF16Array:convertUTF8ArrayToUTF16Array, 206 | convertUTF8StringToBase64:convertUTF8StringToBase64, 207 | convertBase64ToUTF8String:convertBase64ToUTF8String, 208 | convertUTF8StringToUTF16Array:convertUTF8StringToUTF16Array, 209 | convertUTF8ArrayToUTF16String:convertUTF8ArrayToUTF16String, 210 | convertByteArrayToUTF16String:convertUTF8ArrayToUTF16String, 211 | convertUTF8StringToUTF16String:convertUTF8StringToUTF16String, 212 | convertUTF16StringToUTF8Array:convertUTF16StringToUTF8Array, 213 | convertUTF16StringToByteArray:convertUTF16StringToUTF8Array, 214 | convertUTF16ArrayToUTF8String:convertUTF16ArrayToUTF8String, 215 | convertUTF16StringToUTF8String:convertUTF16StringToUTF8String, 216 | convertUTF16StringToBase64:convertUTF16StringToBase64, 217 | convertBase64ToUTF16String:convertBase64ToUTF16String, 218 | fromBase64:convertBase64ToUTF8String, 219 | toBase64:convertUTF8StringToBase64, 220 | atob:atob, 221 | btoa:btoa, 222 | utob:convertUTF16StringToUTF8String, 223 | btou:convertUTF8StringToUTF16String, 224 | encode:convertUTF16StringToBase64, 225 | encodeURI:function(u){ 226 | return convertUTF16StringToBase64(u).replace(/[+\/]/g, function(m0){ 227 | return m0 == '+' ? '-' : '_'; 228 | }).replace(/=+$/, ''); 229 | }, 230 | decode:function(a){ 231 | return convertBase64ToUTF16String(a.replace(/[-_]/g, function(m0){ 232 | return m0 == '-' ? '+' : '/'; 233 | })); 234 | } 235 | }; 236 | 237 | })(this); 238 | -------------------------------------------------------------------------------- /css/zerobin.css: -------------------------------------------------------------------------------- 1 | /* ZeroBin 0.19 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */ 2 | 3 | 4 | /* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved. 5 | Licensed under the BSD License. - http://yuilibrary.com/license/ */ 6 | 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} 7 | 8 | html { 9 | background-color:#455463; 10 | color:white; 11 | min-height:100%; 12 | background-image: linear-gradient(bottom, #0F1823 0%, #455463 100%); 13 | background-image: -o-linear-gradient(bottom, #0F1823 0%, #455463 100%); 14 | background-image: -moz-linear-gradient(bottom, #0F1823 0%, #455463 100%); 15 | background-image: -webkit-linear-gradient(bottom, #0F1823 0%, #455463 100%); 16 | background-image: -ms-linear-gradient(bottom, #0F1823 0%, #455463 100%); 17 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #0F1823), color-stop(1, #455463)); 18 | } 19 | 20 | body { 21 | font-family: Arial, Helvetica, sans-serif; 22 | font-size: 0.8em; 23 | margin-bottom:15px; 24 | padding-left:60px; padding-right:60px; 25 | } 26 | 27 | a { color:#0F388F; } 28 | 29 | h1 { 30 | font-size:3.5em; 31 | font-weight:700; 32 | color:#000; 33 | position:relative; 34 | display:inline; 35 | cursor:pointer; 36 | } 37 | 38 | h1:before { 39 | content:attr(title); 40 | position:absolute; 41 | color:rgba(255,255,255,0.15); 42 | top:1px; 43 | left:1px; 44 | cursor:pointer; 45 | } 46 | 47 | h2 { 48 | color:#000; 49 | font-size:1em; 50 | display:inline; 51 | font-style:italic; 52 | font-weight:bold; 53 | position:relative; 54 | bottom:8px;} 55 | 56 | h3 { 57 | color:#94a3b4; 58 | font-size:0.7em; 59 | display:inline; 60 | position:relative; 61 | bottom:8px;} 62 | 63 | #aboutbox { 64 | font-size:0.85em; 65 | color: #94a3b4; 66 | padding: 4px 8px 4px 16px; 67 | position:relative; 68 | top:10px; 69 | border-left: 2px solid #94a3b4; 70 | float:right; 71 | width:60%; 72 | } 73 | 74 | div#aboutbox a { color: #94a3b4; } 75 | textarea#message,div#cleartext,.replymessage, code { 76 | clear:both; 77 | color:black; 78 | background-color:#fff; 79 | white-space:pre-wrap; 80 | font-family:Consolas,"Lucida Console","DejaVu Sans Mono",Monaco,monospace; 81 | font-size:9pt; 82 | border: 1px solid #28343F; 83 | padding:5px; 84 | box-sizing:border-box; 85 | -webkit-box-sizing:border-box; 86 | -moz-box-sizing:border-box; 87 | -ms-box-sizing:border-box; 88 | -o-box-sizing:border-box; 89 | width:100%; 90 | } 91 | 92 | 93 | div#status { 94 | clear:both; 95 | padding:5px 10px; 96 | } 97 | 98 | 99 | div#pasteresult { 100 | background-color:#1F2833; 101 | color:white; 102 | padding:4px 12px; 103 | clear:both; 104 | -moz-box-shadow: inset 0px 2px 2px #000; 105 | -webkit-box-shadow: inset 0px 2px 2px #000; 106 | box-shadow: inset 0px 2px 5px #000; 107 | } 108 | div#pasteresult a { color:white; } 109 | div#pasteresult button { margin-left:11px } 110 | div#deletelink { float:right; } 111 | div#toolbar, div#status { margin-bottom:5px; } 112 | #copyhint { color: #666; font-size:8pt; } 113 | 114 | button,.button,div#expiration { 115 | color:#fff; 116 | background-color:#323B47; 117 | background-repeat:no-repeat; 118 | background-position:center left; 119 | padding:4px 8px; 120 | font-size:1em; 121 | margin-right:5px; 122 | display:inline; 123 | background-image: linear-gradient(bottom, #323B47 0%, #51606E 100%); 124 | background-image: -o-linear-gradient(bottom, #323B47 0%, #51606E 100%); 125 | background-image: -moz-linear-gradient(bottom, #323B47 0%, #51606E 100%); 126 | background-image: -webkit-linear-gradient(bottom, #323B47 0%, #51606E 100%); 127 | background-image: -ms-linear-gradient(bottom, #323B47 0%, #51606E 100%); 128 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #323B47), color-stop(1, #51606E)); 129 | border: 1px solid #28343F; 130 | -moz-box-shadow: inset 0px 1px 2px #647384; 131 | -webkit-box-shadow: inset 0px 1px 2px #647384; 132 | box-shadow: inset 0px 1px 2px #647384; 133 | -webkit-border-radius: 3px; 134 | -moz-border-radius: 3px; 135 | border-radius: 3px; 136 | -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; 137 | } 138 | button:hover { 139 | background-image: linear-gradient(bottom, #424B57 0%, #61707E 100%); 140 | background-image: -o-linear-gradient(bottom, #424B57 0%, #61707E 100%); 141 | background-image: -moz-linear-gradient(bottom, #424B57 0%, #61707E 100%); 142 | background-image: -webkit-linear-gradient(bottom, #424B57 0%, #61707E 100%); 143 | background-image: -ms-linear-gradient(bottom, #424B57 0%, #61707E 100%); 144 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #424B57), color-stop(1, #61707E)); 145 | } 146 | button:active { 147 | background-image: linear-gradient(bottom, #51606E 0%, #323B47 100%); 148 | background-image: -o-linear-gradient(bottom, #51606E 0%, #323B47 100%); 149 | background-image: -moz-linear-gradient(bottom, #51606E 0%, #323B47 100%); 150 | background-image: -webkit-linear-gradient(bottom, #51606E 0%, #323B47 100%); 151 | background-image: -ms-linear-gradient(bottom, #51606E 0%, #323B47 100%); 152 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #51606E), color-stop(1, #323B47)); 153 | position:relative; 154 | top:1px; 155 | } 156 | button:disabled, .buttondisabled { 157 | background:#ccc; 158 | color:#888; 159 | top:0px; 160 | } 161 | button img { 162 | margin-right:8px; 163 | position:relative; 164 | top:2px; 165 | } 166 | 167 | div#expiration, div#rawtextbutton, div#burnafterreadingoption, div#opendisc, div#syntaxcoloringoption { 168 | background-color:#414D5A; 169 | padding:6px 8px; 170 | margin:0px 5px 0px 0px;; 171 | position: relative; 172 | bottom:1px; /* WTF ? Why is this shifted by 1 pixel ? */ 173 | } 174 | div#expiration select { 175 | color:#eee; 176 | background: transparent; 177 | border: none; 178 | } 179 | 180 | 181 | div#expiration select option { 182 | color:#eee; 183 | background: #414D5A; 184 | background-color:#414D5A; 185 | } 186 | 187 | div#remainingtime { 188 | color: #94a3b4; 189 | display:inline; 190 | font-size:0.85em; 191 | } 192 | 193 | .foryoureyesonly { 194 | color: yellow !important; 195 | font-size: 1em !important; 196 | font-weight:bold !important; 197 | } 198 | 199 | button#newbutton { float:right; margin-right:0px;margin-bottom:5px; display:inline; } 200 | input { color:#777; font-size:1em; padding:6px; border: 1px solid #28343F; } 201 | 202 | .nonworking { 203 | background-color:#fff; 204 | color:#000; 205 | width:100%; 206 | text-align:center; 207 | font-weight:bold; 208 | font-size:10pt; 209 | -webkit-border-radius:4px; 210 | -moz-border-radius:4px; 211 | border-radius:4px; 212 | padding:5px; 213 | } 214 | 215 | div#ienotice { 216 | background-color:#7E98AF; 217 | color:#000; 218 | font-size:0.85em; 219 | padding:3px 5px; 220 | text-align:center; 221 | -webkit-border-radius:4px; 222 | -moz-border-radius:4px; 223 | border-radius:4px; 224 | display:none; 225 | } 226 | 227 | div#ienotice a { 228 | color:black; 229 | } 230 | 231 | div#oldienotice { 232 | display:none; 233 | } 234 | 235 | .errorMessage { 236 | background-color:#FF7979 !important; 237 | color:#FF0; 238 | } 239 | 240 | 241 | /* --- discussion related CSS ------- */ 242 | 243 | 244 | div#discussion { /* Discussion container */ 245 | margin-top:20px; 246 | width:100%; 247 | margin-left:-30px; 248 | min-width:200px; 249 | } 250 | 251 | h4 { 252 | font-size:1.2em; 253 | color: #94A3B4; 254 | font-style:italic; 255 | font-weight:bold; 256 | position:relative; 257 | margin-left:30px; 258 | } 259 | 260 | 261 | div.comment /* One single reply */ 262 | { 263 | background-color:#CECED6; 264 | color:#000; 265 | white-space:pre-wrap; 266 | font-family:Consolas,"Lucida Console","DejaVu Sans Mono",Monaco,monospace; 267 | font-size:9pt; 268 | border-left: 1px solid #859AAE; 269 | border-top: 1px solid #859AAE; 270 | padding:5px 0px 5px 5px; 271 | margin-left:30px; 272 | -moz-box-shadow: -3px -3px 5px rgba(0,0,0,0.15); 273 | -webkit-box-shadow: -3px -3px 5px rgba(0,0,0,0.15); 274 | box-shadow: -3px -3px 5px rgba(0,0,0,0.15); 275 | min-width:200px; 276 | overflow:auto; 277 | } 278 | /* FIXME: Add min-width */ 279 | 280 | div.reply { 281 | margin: 5px 0px 0px 30px; 282 | } 283 | 284 | div#replystatus { 285 | display:inline; 286 | padding:1px 7px; 287 | font-family: Arial, Helvetica, sans-serif; 288 | } 289 | 290 | div.comment button { 291 | color:#446; 292 | background-color:#aab; 293 | background-repeat:no-repeat; 294 | background-position:center left; 295 | padding:0px 2px; 296 | font-size:0.73em; 297 | margin: 3px 5px 3px 0px; 298 | display:inline; 299 | background-image: linear-gradient(bottom, #aab 0%, #ccc 100%); 300 | background-image: -o-linear-gradient(bottom, #aab 0%, #ccc 100%); 301 | background-image: -moz-linear-gradient(bottom, #aab 0%, #ccc 100%); 302 | background-image: -webkit-linear-gradient(bottom, #aab 0%, #ccc 100%); 303 | background-image: -ms-linear-gradient(bottom, #aab 0%, #ccc 100%); 304 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #aab), color-stop(1, #ccc)); 305 | border: 1px solid #ccd; 306 | -moz-box-shadow: inset 0px 1px 2px #ddd; 307 | -webkit-box-shadow: inset 0px 1px 2px #fff; 308 | box-shadow: inset 0px 1px 2px #eee; 309 | -webkit-border-radius: 3px; 310 | -moz-border-radius: 3px; 311 | border-radius: 3px; 312 | -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; 313 | } 314 | div.comment button:hover { 315 | background-image: linear-gradient(bottom, #ccd 0%, #fff 100%); 316 | background-image: -o-linear-gradient(bottom, #ccd 0%, #fff 100%); 317 | background-image: -moz-linear-gradient(bottom, #ccd 0%, #fff 100%); 318 | background-image: -webkit-linear-gradient(bottom, #ccd 0%, #fff 100%); 319 | background-image: -ms-linear-gradient(bottom, #ccd 0%, #fff 100%); 320 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccd), color-stop(1, #fff)); 321 | } 322 | div.comment button:active { 323 | background-image: linear-gradient(bottom, #fff 0%, #889 100%); 324 | background-image: -o-linear-gradient(bottom, #fff 0%, #889 100%); 325 | background-image: -moz-linear-gradient(bottom, #fff 0%, #889 100%); 326 | background-image: -webkit-linear-gradient(bottom, #fff 0%, #889 100%); 327 | background-image: -ms-linear-gradient(bottom, #fff 0%, #889 100%); 328 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(1, #889)); 329 | position:relative; 330 | top:1px; 331 | } 332 | 333 | div.comment input { 334 | padding:2px; 335 | } 336 | 337 | textarea#replymessage { 338 | margin-top:5px; 339 | } 340 | 341 | div.commentmeta { 342 | color: #fff; 343 | background-color:#8EA0B2; 344 | margin-bottom:3px; 345 | padding:0px 0px 0px 3px; 346 | } 347 | 348 | span.commentdate { 349 | color: #BFCEDE; 350 | } 351 | 352 | img.vizhash { 353 | width:16px; 354 | height:16px; 355 | position:relative; 356 | top:2px; 357 | left:-3px; 358 | } 359 | 360 | pre a { 361 | color:#58A5B4; 362 | } 363 | 364 | pre a:hover { 365 | color:#64B9C6; 366 | } 367 | 368 | 369 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | ", LOCK_EX); 28 | chmod($tfilename,0705); 29 | } 30 | require $tfilename; 31 | $tl=$GLOBALS['trafic_limiter']; 32 | if (!empty($tl[$ip]) && ($tl[$ip]+10>=time())) 33 | { 34 | return false; 35 | // FIXME: purge file of expired IPs to keep it small 36 | } 37 | $tl[$ip]=time(); 38 | file_put_contents($tfilename, "", LOCK_EX); 39 | return true; 40 | } 41 | 42 | // Constant time string comparison. 43 | // (Used to deter time attacks on hmac checking. See section 2.7 of https://defuse.ca/audits/zerobin.htm) 44 | function slow_equals($a, $b) 45 | { 46 | $diff = strlen($a) ^ strlen($b); 47 | for($i = 0; $i < strlen($a) && $i < strlen($b); $i++) 48 | { 49 | $diff |= ord($a[$i]) ^ ord($b[$i]); 50 | } 51 | return $diff === 0; 52 | } 53 | 54 | 55 | /* Convert paste id to storage path. 56 | The idea is to creates subdirectories in order to limit the number of files per directory. 57 | (A high number of files in a single directory can slow things down.) 58 | eg. "f468483c313401e8" will be stored in "data/f4/68/f468483c313401e8" 59 | High-trafic websites may want to deepen the directory structure (like Squid does). 60 | 61 | eg. input 'e3570978f9e4aa90' --> output 'data/e3/57/' 62 | */ 63 | function dataid2path($dataid) 64 | { 65 | return 'data/'.substr($dataid,0,2).'/'.substr($dataid,2,2).'/'; 66 | } 67 | 68 | /* Convert paste id to discussion storage path. 69 | eg. 'e3570978f9e4aa90' --> 'data/e3/57/e3570978f9e4aa90.discussion/' 70 | */ 71 | function dataid2discussionpath($dataid) 72 | { 73 | return dataid2path($dataid).$dataid.'.discussion/'; 74 | } 75 | 76 | // Checks if a json string is a proper SJCL encrypted message. 77 | // False if format is incorrect. 78 | function validSJCL($jsonstring) 79 | { 80 | $accepted_keys=array('iv','v','iter','ks','ts','mode','adata','cipher','salt','ct'); 81 | 82 | // Make sure content is valid json 83 | $decoded = json_decode($jsonstring); 84 | if ($decoded==null) return false; 85 | $decoded = (array)$decoded; 86 | 87 | // Make sure required fields are present 88 | foreach($accepted_keys as $k) 89 | { 90 | if (!array_key_exists($k,$decoded)) { return false; } 91 | } 92 | 93 | // Make sure some fields are base64 data 94 | if (base64_decode($decoded['iv'],$strict=true)==null) { return false; } 95 | if (base64_decode($decoded['salt'],$strict=true)==null) { return false; } 96 | if (base64_decode($decoded['cipher'],$strict=true)==null) { return false; } 97 | 98 | // Make sure no additionnal keys were added. 99 | if (count(array_intersect(array_keys($decoded),$accepted_keys))!=10) { return false; } 100 | 101 | // Reject data if entropy is too low 102 | $ct = base64_decode($decoded['ct'], $strict=true); 103 | if (strlen($ct) > strlen(gzdeflate($ct))) return false; 104 | 105 | // Make sure some fields have a reasonable size. 106 | if (strlen($decoded['iv'])>24) return false; 107 | if (strlen($decoded['salt'])>14) return false; 108 | return true; 109 | } 110 | 111 | // Delete a paste and its discussion. 112 | // Input: $pasteid : the paste identifier. 113 | function deletePaste($pasteid) 114 | { 115 | // Delete the paste itself 116 | unlink(dataid2path($pasteid).$pasteid); 117 | 118 | // Delete discussion if it exists. 119 | $discdir = dataid2discussionpath($pasteid); 120 | if (is_dir($discdir)) 121 | { 122 | // Delete all files in discussion directory 123 | $dhandle = opendir($discdir); 124 | while (false !== ($filename = readdir($dhandle))) 125 | { 126 | if (is_file($discdir.$filename)) unlink($discdir.$filename); 127 | } 128 | closedir($dhandle); 129 | 130 | // Delete the discussion directory. 131 | rmdir($discdir); 132 | } 133 | } 134 | 135 | if (!empty($_POST['data'])) // Create new paste/comment 136 | { 137 | /* POST contains: 138 | data (mandatory) = json encoded SJCL encrypted text (containing keys: iv,salt,ct) 139 | 140 | All optional data will go to meta information: 141 | expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never) 142 | opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0) 143 | syntaxcoloring (optional) = should this paste use syntax coloring when displaying. 144 | nickname (optional) = son encoded SJCL encrypted text nickname of author of comment (containing keys: iv,salt,ct) 145 | parentid (optional) = in discussion, which comment this comment replies to. 146 | pasteid (optional) = in discussion, which paste this comment belongs to. 147 | */ 148 | 149 | header('Content-type: application/json'); 150 | $error = false; 151 | 152 | // Create storage directory if it does not exist. 153 | if (!is_dir('data')) 154 | { 155 | mkdir('data',0705); 156 | file_put_contents('data/.htaccess',"Allow from none\nDeny from all\n", LOCK_EX); 157 | } 158 | 159 | // Make sure last paste from the IP address was more than 10 seconds ago. 160 | if (!trafic_limiter_canPass($_SERVER['REMOTE_ADDR'])) 161 | { echo json_encode(array('status'=>1,'message'=>'Please wait 10 seconds between each post.')); exit; } 162 | 163 | // Make sure content is not too big. 164 | $data = $_POST['data']; 165 | if (strlen($data)>2000000) 166 | { echo json_encode(array('status'=>1,'message'=>'Paste is limited to 2 Mb of encrypted data.')); exit; } 167 | 168 | // Make sure format is correct. 169 | if (!validSJCL($data)) 170 | { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; } 171 | 172 | // Read additional meta-information. 173 | $meta=array(); 174 | 175 | // Read expiration date 176 | if (!empty($_POST['expire'])) 177 | { 178 | $expire=$_POST['expire']; 179 | if ($expire=='5min') $meta['expire_date']=time()+5*60; 180 | elseif ($expire=='10min') $meta['expire_date']=time()+10*60; 181 | elseif ($expire=='1hour') $meta['expire_date']=time()+60*60; 182 | elseif ($expire=='1day') $meta['expire_date']=time()+24*60*60; 183 | elseif ($expire=='1week') $meta['expire_date']=time()+7*24*60*60; 184 | elseif ($expire=='1month') $meta['expire_date']=time()+30*24*60*60; // Well this is not *exactly* one month, it's 30 days. 185 | elseif ($expire=='1year') $meta['expire_date']=time()+365*24*60*60; 186 | } 187 | 188 | // Destroy the paste when it is read. 189 | if (!empty($_POST['burnafterreading'])) 190 | { 191 | $burnafterreading = $_POST['burnafterreading']; 192 | if ($burnafterreading!='0' && $burnafterreading!='1') { $error=true; } 193 | if ($burnafterreading!='0') { $meta['burnafterreading']=true; } 194 | } 195 | 196 | // Read open discussion flag 197 | if (!empty($_POST['opendiscussion'])) 198 | { 199 | $opendiscussion = $_POST['opendiscussion']; 200 | if ($opendiscussion!='0' && $opendiscussion!='1') { $error=true; } 201 | if ($opendiscussion!='0') { $meta['opendiscussion']=true; } 202 | } 203 | 204 | // Should we use syntax coloring when displaying ? 205 | if (!empty($_POST['syntaxcoloring'])) 206 | { 207 | $syntaxcoloring = $_POST['syntaxcoloring']; 208 | if ($syntaxcoloring!='0' && $syntaxcoloring!='1') { $error=true; } 209 | if ($syntaxcoloring!='0') { $meta['syntaxcoloring']=true; } 210 | } 211 | 212 | // You can't have an open discussion on a "Burn after reading" paste: 213 | if (isset($meta['burnafterreading'])) unset($meta['opendiscussion']); 214 | 215 | // Optional nickname for comments 216 | if (!empty($_POST['nickname'])) 217 | { 218 | $nick = $_POST['nickname']; 219 | if (!validSJCL($nick)) 220 | { 221 | $error=true; 222 | } 223 | else 224 | { 225 | $meta['nickname']=$nick; 226 | 227 | // Generation of the anonymous avatar (Vizhash): 228 | // If a nickname is provided, we generate a Vizhash. 229 | // (We assume that if the user did not enter a nickname, he/she wants 230 | // to be anonymous and we will not generate the vizhash.) 231 | $vz = new vizhash16x16(); 232 | $pngdata = $vz->generate($_SERVER['REMOTE_ADDR']); 233 | if ($pngdata!='') $meta['vizhash'] = 'data:image/png;base64,'.base64_encode($pngdata); 234 | // Once the avatar is generated, we do not keep the IP address, nor its hash. 235 | } 236 | } 237 | 238 | if ($error) 239 | { 240 | echo json_encode(array('status'=>1,'message'=>'Invalid data.')); 241 | exit; 242 | } 243 | 244 | // Add post date to meta. 245 | $meta['postdate']=time(); 246 | 247 | // We just want a small hash to avoid collisions: Half-MD5 (64 bits) will do the trick. 248 | $dataid = substr(hash('md5',$data),0,16); 249 | 250 | $is_comment = (!empty($_POST['parentid']) && !empty($_POST['pasteid'])); // Is this post a comment ? 251 | $storage = array('data'=>$data); 252 | if (count($meta)>0) $storage['meta'] = $meta; // Add meta-information only if necessary. 253 | 254 | if ($is_comment) // The user posts a comment. 255 | { 256 | $pasteid = $_POST['pasteid']; 257 | $parentid = $_POST['parentid']; 258 | if (!preg_match('/\A[a-f\d]{16}\z/',$pasteid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; } 259 | if (!preg_match('/\A[a-f\d]{16}\z/',$parentid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; } 260 | 261 | unset($storage['expire_date']); // Comment do not expire (it's the paste that expires) 262 | unset($storage['opendiscussion']); 263 | unset($storage['syntaxcoloring']); 264 | 265 | // Make sure paste exists. 266 | $storagedir = dataid2path($pasteid); 267 | if (!is_file($storagedir.$pasteid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; } 268 | 269 | // Make sure the discussion is opened in this paste. 270 | $paste=json_decode(file_get_contents($storagedir.$pasteid)); 271 | if (!$paste->meta->opendiscussion) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; } 272 | 273 | $discdir = dataid2discussionpath($pasteid); 274 | $filename = $pasteid.'.'.$dataid.'.'.$parentid; 275 | if (!is_dir($discdir)) mkdir($discdir,$mode=0705,$recursive=true); 276 | if (is_file($discdir.$filename)) // Oups... improbable collision. 277 | { 278 | echo json_encode(array('status'=>1,'message'=>'You are unlucky. Try again.')); 279 | exit; 280 | } 281 | 282 | file_put_contents($discdir.$filename,json_encode($storage), LOCK_EX); 283 | echo json_encode(array('status'=>0,'id'=>$dataid)); // 0 = no error 284 | exit; 285 | } 286 | else // a standard paste. 287 | { 288 | $storagedir = dataid2path($dataid); 289 | if (!is_dir($storagedir)) mkdir($storagedir,$mode=0705,$recursive=true); 290 | if (is_file($storagedir.$dataid)) // Oups... improbable collision. 291 | { 292 | echo json_encode(array('status'=>1,'message'=>'You are unlucky. Try again.')); 293 | exit; 294 | } 295 | // New paste 296 | file_put_contents($storagedir.$dataid,json_encode($storage), LOCK_EX); 297 | 298 | // Generate the "delete" token. 299 | // The token is the hmac of the pasteid signed with the server salt. 300 | // The paste can be delete by calling http://myserver.com/zerobin/?pasteid=&deletetoken= 301 | $deletetoken = hash_hmac('sha1', $dataid , getServerSalt()); 302 | 303 | echo json_encode(array('status'=>0,'id'=>$dataid,'deletetoken'=>$deletetoken)); // 0 = no error 304 | exit; 305 | } 306 | 307 | echo json_encode(array('status'=>1,'message'=>'Server error.')); 308 | exit; 309 | } 310 | 311 | /* Process a paste deletion request. 312 | Returns an array ('',$ERRORMESSAGE,$STATUS) 313 | */ 314 | function processPasteDelete($pasteid,$deletetoken) 315 | { 316 | if (preg_match('/\A[a-f\d]{16}\z/',$pasteid)) // Is this a valid paste identifier ? 317 | { 318 | $filename = dataid2path($pasteid).$pasteid; 319 | if (!is_file($filename)) // Check that paste exists. 320 | { 321 | return array('','Paste does not exist, has expired or has been deleted.',''); 322 | } 323 | } 324 | else 325 | { 326 | return array('','Invalid data',''); 327 | } 328 | 329 | if (!slow_equals($deletetoken, hash_hmac('sha1', $pasteid , getServerSalt()))) // Make sure token is valid. 330 | { 331 | return array('','Wrong deletion token. Paste was not deleted.',''); 332 | } 333 | 334 | // Paste exists and deletion token is valid: Delete the paste. 335 | deletePaste($pasteid); 336 | return array('','','Paste was properly deleted.'); 337 | } 338 | 339 | /* Process a paste fetch request. 340 | Returns an array ($CIPHERDATA,$ERRORMESSAGE,$STATUS) 341 | */ 342 | function processPasteFetch($pasteid) 343 | { 344 | if (preg_match('/\A[a-f\d]{16}\z/',$pasteid)) // Is this a valid paste identifier ? 345 | { 346 | $filename = dataid2path($pasteid).$pasteid; 347 | if (!is_file($filename)) // Check that paste exists. 348 | { 349 | return array('','Paste does not exist, has expired or has been deleted.',''); 350 | } 351 | } 352 | else 353 | { 354 | return array('','Invalid data',''); 355 | } 356 | 357 | // Get the paste itself. 358 | $paste=json_decode(file_get_contents($filename)); 359 | 360 | // See if paste has expired. 361 | if (isset($paste->meta->expire_date) && $paste->meta->expire_datemeta, 'expire_date')) $paste->meta->remaining_time = $paste->meta->expire_date - time(); 370 | 371 | $messages = array($paste); // The paste itself is the first in the list of encrypted messages. 372 | // If it's a discussion, get all comments. 373 | if (property_exists($paste->meta, 'opendiscussion') && $paste->meta->opendiscussion) 374 | { 375 | $comments=array(); 376 | $datadir = dataid2discussionpath($pasteid); 377 | if (!is_dir($datadir)) mkdir($datadir,$mode=0705,$recursive=true); 378 | $dhandle = opendir($datadir); 379 | while (false !== ($filename = readdir($dhandle))) 380 | { 381 | if (is_file($datadir.$filename)) 382 | { 383 | $comment=json_decode(file_get_contents($datadir.$filename)); 384 | // Filename is in the form pasteid.commentid.parentid: 385 | // - pasteid is the paste this reply belongs to. 386 | // - commentid is the comment identifier itself. 387 | // - parentid is the comment this comment replies to (It can be pasteid) 388 | $items=explode('.',$filename); 389 | $comment->meta->commentid=$items[1]; // Add some meta information not contained in file. 390 | $comment->meta->parentid=$items[2]; 391 | $comments[$comment->meta->postdate]=$comment; // Store in table 392 | } 393 | } 394 | closedir($dhandle); 395 | ksort($comments); // Sort comments by date, oldest first. 396 | $messages = array_merge($messages, $comments); 397 | } 398 | $CIPHERDATA = json_encode($messages); 399 | 400 | // If the paste was meant to be read only once, delete it. 401 | if (property_exists($paste->meta, 'burnafterreading') && $paste->meta->burnafterreading) deletePaste($pasteid); 402 | 403 | return array($CIPHERDATA,'',''); 404 | } 405 | 406 | 407 | 408 | $CIPHERDATA=''; 409 | $ERRORMESSAGE=''; 410 | $STATUS=''; 411 | 412 | if (!empty($_GET['deletetoken']) && !empty($_GET['pasteid'])) // Delete an existing paste 413 | { 414 | list ($CIPHERDATA, $ERRORMESSAGE, $STATUS) = processPasteDelete($_GET['pasteid'],$_GET['deletetoken']); 415 | } 416 | else if (!empty($_SERVER['QUERY_STRING'])) // Return an existing paste. 417 | { 418 | list ($CIPHERDATA, $ERRORMESSAGE, $STATUS) = processPasteFetch($_SERVER['QUERY_STRING']); 419 | } 420 | 421 | require_once "lib/rain.tpl.class.php"; 422 | header('Content-Type: text/html; charset=utf-8'); 423 | $page = new RainTPL; 424 | $page->assign('CIPHERDATA',htmlspecialchars($CIPHERDATA,ENT_NOQUOTES)); // We escape it here because ENT_NOQUOTES can't be used in RainTPL templates. 425 | $page->assign('VERSION',$VERSION); 426 | $page->assign('ERRORMESSAGE',$ERRORMESSAGE); 427 | $page->assign('STATUS',$STATUS); 428 | $page->draw('page'); 429 | ?> 430 | -------------------------------------------------------------------------------- /js/sjcl.js: -------------------------------------------------------------------------------- 1 | "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}}}; 2 | if(typeof module!="undefined"&&module.exports)module.exports=sjcl; 3 | sjcl.cipher.aes=function(a){this.i[0][0][0]||this.B();var b,c,d,e,f=this.i[0][4],g=this.i[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]]^ 4 | g[3][f[c&255]]}}; 5 | sjcl.cipher.aes.prototype={encrypt:function(a){return this.K(a,0)},decrypt:function(a){return this.K(a,1)},i:[[[],[],[],[],[]],[[],[],[],[],[]]],B:function(){var a=this.i[0],b=this.i[1],c=a[4],d=b[4],e,f,g,h=[],i=[],j,k,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^=j||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;k=h[e=h[j=h[f]]];m=k*0x1010101^e*0x10001^j*0x101^f*0x1010100;k=h[l]*0x101^l*0x1010100;for(e=0;e<4;e++){a[e][f]=k=k<<24^k>>>8;b[e][l]=m=m<<24^m>>>8}}for(e= 6 | 0;e<5;e++){a[e]=a[e].slice(0);b[e]=b[e].slice(0)}},K: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,j=c.length/4-2,k,l=4,m=[0,0,0,0];g=this.i[b];var n=g[0],o=g[1],p=g[2],q=g[3],r=g[4];for(k=0;k>>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& 7 | 255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(k=0;k<4;k++){m[b?3&-k:k]=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}}; 8 | sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.Q(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<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=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a);for(e=0;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},j:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}}; 11 | sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;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.H,f=0,g;if(b)e=e.substr(0,62)+"-_";for(b=0;b26){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.B();if(a){this.p=a.p.slice(0);this.k=a.k.slice(0);this.f=a.f}else this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; 15 | sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.p=this.O.slice(0);this.k=[];this.f=0;return this},update:function(a){if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);var b,c=this.k=sjcl.bitArray.concat(this.k,a);b=this.f;a=this.f=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.G(c.splice(0,16));return this},finalize:function(){var a,b=this.k,c=this.p;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.f/ 16 | 4294967296));for(b.push(this.f|0);b.length;)this.G(b.splice(0,16));this.reset();return c},O:[],a:[],B: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.O[b]=a(Math.pow(c,0.5));this.a[b]=a(Math.pow(c,1/3));b++}},G:function(a){var b,c,d=a.slice(0),e=this.p,f=this.a,g=e[0],h=e[1],i=e[2],j=e[3],k=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^ 17 | 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+(k>>>6^k>>>11^k>>>25^k<<26^k<<21^k<<7)+(m^k&(l^m))+f[a];n=m;m=l;l=k;k=j+b|0;j=i;i=h;h=g;g=b+(h&i^j&(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]+j|0;e[4]=e[4]+k|0;e[5]=e[5]+l|0;e[6]=e[6]+m|0;e[7]=e[7]+n|0}}; 18 | 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,j=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&&j>>>8*f;f++);if(f<15-i)f=15-i;c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.J(a,b,c,d,e,f);g=sjcl.mode.ccm.l(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),j=f.bitSlice(b, 19 | 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.l(a,i,c,j,e,b);a=sjcl.mode.ccm.J(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},J:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,i=h.j;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"); 20 | 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>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}}; 26 | sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;e=e||128;d=d||[];a=sjcl.mode.gcm.l(true,a,f,d,c,e);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];if(e<=h){b=g.bitSlice(f,h-e);f=g.bitSlice(f,0,h-e)}else{b=f;f=[]}a=sjcl.mode.gcm.l(false,a,f,d,c,e);if(!g.equal(a.tag,b))throw new sjcl.exception.corrupt("gcm: tag doesn't match");return a.data},T:function(a,b){var c,d,e,f,g=sjcl.bitArray.j; 27 | d=[0,0,0,0];e=b.slice(0);for(b=0;b<128;b++){if(c=(a[Math.floor(b/32)]&1<<31-b%32)!==0)d=g(d,e);f=(e[3]&1)!==0;for(c=3;c>0;c--)e[c]=e[c]>>>1|(e[c-1]&1)<<31;e[0]>>>=1;if(f)e[0]^=-0x1f000000}return d},e:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;dd)a=b.hash(a);for(b=0;b0;){b++;e>>>=1}this.b[g].update([d,this.z++,2,b,f,a.length].concat(a))}break;case "string":if(b=== 33 | undefined)b=a.length;this.b[g].update([d,this.z++,3,b,f,a.length]);this.b[g].update(a);break;default:i=1}if(i)throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");this.m[g]+=b;this.g+=b;if(h===0){this.isReady()!==0&&this.L("seeded",Math.max(this.h,this.g));this.L("progress",this.getProgress())}},isReady:function(a){a=this.F[a!==undefined?a:this.w];return this.h&&this.h>=a?this.m[0]>80&&(new Date).valueOf()>this.P?3:1:this.g>=a?2:0},getProgress:function(a){a= 34 | this.F[a?a:this.w];return this.h>=a?1:this.g>a?1:this.g/a},startCollectors:function(){if(!this.o){if(window.addEventListener){window.addEventListener("load",this.q,false);window.addEventListener("mousemove",this.r,false)}else if(document.attachEvent){document.attachEvent("onload",this.q);document.attachEvent("onmousemove",this.r)}else throw new sjcl.exception.bug("can't attach event");this.o=true}},stopCollectors:function(){if(this.o){if(window.removeEventListener){window.removeEventListener("load", 35 | this.q,false);window.removeEventListener("mousemove",this.r,false)}else if(window.detachEvent){window.detachEvent("onload",this.q);window.detachEvent("onmousemove",this.r)}this.o=false}},addEventListener:function(a,b){this.t[a][this.R++]=b},removeEventListener:function(a,b){var c;a=this.t[a];var d=[];for(c in a)a.hasOwnProperty(c)&&a[c]===b&&d.push(c);for(b=0;b=1<this.h)this.h=c;this.C++;this.V(b)},r:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX||0,a.y||a.clientY||a.offsetY||0],2,"mouse")},q:function(){sjcl.random.addEntropy((new Date).valueOf(),2,"loadtime")},L:function(a,b){var c;a=sjcl.random.t[a]; 38 | var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c4)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(f)},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)), 41 | 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=== 42 | "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+='"'+ 43 | sjcl.codec.base64.fromBits(a[b],0)+'"';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 11 | * Version: 1.0.0.1 12 | * LastModified: Dec 25 1999 13 | */ 14 | 15 | /* Interface: 16 | * data = zip_inflate(src); 17 | */ 18 | 19 | /* constant parameters */ 20 | var zip_WSIZE = 32768; // Sliding Window size 21 | var zip_STORED_BLOCK = 0; 22 | var zip_STATIC_TREES = 1; 23 | var zip_DYN_TREES = 2; 24 | 25 | /* for inflate */ 26 | var zip_lbits = 9; // bits in base literal/length lookup table 27 | var zip_dbits = 6; // bits in base distance lookup table 28 | var zip_INBUFSIZ = 32768; // Input buffer size 29 | var zip_INBUF_EXTRA = 64; // Extra buffer 30 | 31 | /* variables (inflate) */ 32 | var zip_slide; 33 | var zip_wp; // current position in slide 34 | var zip_fixed_tl = null; // inflate static 35 | var zip_fixed_td; // inflate static 36 | var zip_fixed_bl, fixed_bd; // inflate static 37 | var zip_bit_buf; // bit buffer 38 | var zip_bit_len; // bits in bit buffer 39 | var zip_method; 40 | var zip_eof; 41 | var zip_copy_leng; 42 | var zip_copy_dist; 43 | var zip_tl, zip_td; // literal/length and distance decoder tables 44 | var zip_bl, zip_bd; // number of bits decoded by tl and td 45 | 46 | var zip_inflate_data; 47 | var zip_inflate_pos; 48 | 49 | 50 | /* constant tables (inflate) */ 51 | var zip_MASK_BITS = new Array( 52 | 0x0000, 53 | 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 54 | 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff); 55 | // Tables for deflate from PKZIP's appnote.txt. 56 | var zip_cplens = new Array( // Copy lengths for literal codes 257..285 57 | 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 58 | 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0); 59 | /* note: see note #13 above about the 258 in this list. */ 60 | var zip_cplext = new Array( // Extra bits for literal codes 257..285 61 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 62 | 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99); // 99==invalid 63 | var zip_cpdist = new Array( // Copy offsets for distance codes 0..29 64 | 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 65 | 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 66 | 8193, 12289, 16385, 24577); 67 | var zip_cpdext = new Array( // Extra bits for distance codes 68 | 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 69 | 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 70 | 12, 12, 13, 13); 71 | var zip_border = new Array( // Order of the bit length code lengths 72 | 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); 73 | /* objects (inflate) */ 74 | 75 | var zip_HuftList = function() { 76 | this.next = null; 77 | this.list = null; 78 | } 79 | 80 | var zip_HuftNode = function() { 81 | this.e = 0; // number of extra bits or operation 82 | this.b = 0; // number of bits in this code or subcode 83 | 84 | // union 85 | this.n = 0; // literal, length base, or distance base 86 | this.t = null; // (zip_HuftNode) pointer to next level of table 87 | } 88 | 89 | var zip_HuftBuild = function(b, // code lengths in bits (all assumed <= BMAX) 90 | n, // number of codes (assumed <= N_MAX) 91 | s, // number of simple-valued codes (0..s-1) 92 | d, // list of base values for non-simple codes 93 | e, // list of extra bits for non-simple codes 94 | mm // maximum lookup bits 95 | ) { 96 | this.BMAX = 16; // maximum bit length of any code 97 | this.N_MAX = 288; // maximum number of codes in any set 98 | this.status = 0; // 0: success, 1: incomplete table, 2: bad input 99 | this.root = null; // (zip_HuftList) starting table 100 | this.m = 0; // maximum lookup bits, returns actual 101 | 102 | /* Given a list of code lengths and a maximum table size, make a set of 103 | tables to decode that set of codes. Return zero on success, one if 104 | the given code set is incomplete (the tables are still built in this 105 | case), two if the input is invalid (all zero length codes or an 106 | oversubscribed set of lengths), and three if not enough memory. 107 | The code with value 256 is special, and the tables are constructed 108 | so that no bits beyond that code are fetched when that code is 109 | decoded. */ 110 | { 111 | var a; // counter for codes of length k 112 | var c = new Array(this.BMAX+1); // bit length count table 113 | var el; // length of EOB code (value 256) 114 | var f; // i repeats in table every f entries 115 | var g; // maximum code length 116 | var h; // table level 117 | var i; // counter, current code 118 | var j; // counter 119 | var k; // number of bits in current code 120 | var lx = new Array(this.BMAX+1); // stack of bits per table 121 | var p; // pointer into c[], b[], or v[] 122 | var pidx; // index of p 123 | var q; // (zip_HuftNode) points to current table 124 | var r = new zip_HuftNode(); // table entry for structure assignment 125 | var u = new Array(this.BMAX); // zip_HuftNode[BMAX][] table stack 126 | var v = new Array(this.N_MAX); // values in order of bit length 127 | var w; 128 | var x = new Array(this.BMAX+1);// bit offsets, then code stack 129 | var xp; // pointer into x or c 130 | var y; // number of dummy codes added 131 | var z; // number of entries in current table 132 | var o; 133 | var tail; // (zip_HuftList) 134 | 135 | tail = this.root = null; 136 | for(i = 0; i < c.length; i++) 137 | c[i] = 0; 138 | for(i = 0; i < lx.length; i++) 139 | lx[i] = 0; 140 | for(i = 0; i < u.length; i++) 141 | u[i] = null; 142 | for(i = 0; i < v.length; i++) 143 | v[i] = 0; 144 | for(i = 0; i < x.length; i++) 145 | x[i] = 0; 146 | 147 | // Generate counts for each bit length 148 | el = n > 256 ? b[256] : this.BMAX; // set length of EOB code, if any 149 | p = b; pidx = 0; 150 | i = n; 151 | do { 152 | c[p[pidx]]++; // assume all entries <= BMAX 153 | pidx++; 154 | } while(--i > 0); 155 | if(c[0] == n) { // null input--all zero length codes 156 | this.root = null; 157 | this.m = 0; 158 | this.status = 0; 159 | return; 160 | } 161 | 162 | // Find minimum and maximum length, bound *m by those 163 | for(j = 1; j <= this.BMAX; j++) 164 | if(c[j] != 0) 165 | break; 166 | k = j; // minimum code length 167 | if(mm < j) 168 | mm = j; 169 | for(i = this.BMAX; i != 0; i--) 170 | if(c[i] != 0) 171 | break; 172 | g = i; // maximum code length 173 | if(mm > i) 174 | mm = i; 175 | 176 | // Adjust last length count to fill out codes, if needed 177 | for(y = 1 << j; j < i; j++, y <<= 1) 178 | if((y -= c[j]) < 0) { 179 | this.status = 2; // bad input: more codes than bits 180 | this.m = mm; 181 | return; 182 | } 183 | if((y -= c[i]) < 0) { 184 | this.status = 2; 185 | this.m = mm; 186 | return; 187 | } 188 | c[i] += y; 189 | 190 | // Generate starting offsets into the value table for each length 191 | x[1] = j = 0; 192 | p = c; 193 | pidx = 1; 194 | xp = 2; 195 | while(--i > 0) // note that i == g from above 196 | x[xp++] = (j += p[pidx++]); 197 | 198 | // Make a table of values in order of bit lengths 199 | p = b; pidx = 0; 200 | i = 0; 201 | do { 202 | if((j = p[pidx++]) != 0) 203 | v[x[j]++] = i; 204 | } while(++i < n); 205 | n = x[g]; // set n to length of v 206 | 207 | // Generate the Huffman codes and for each, make the table entries 208 | x[0] = i = 0; // first Huffman code is zero 209 | p = v; pidx = 0; // grab values in bit order 210 | h = -1; // no tables yet--level -1 211 | w = lx[0] = 0; // no bits decoded yet 212 | q = null; // ditto 213 | z = 0; // ditto 214 | 215 | // go through the bit lengths (k already is bits in shortest code) 216 | for(; k <= g; k++) { 217 | a = c[k]; 218 | while(a-- > 0) { 219 | // here i is the Huffman code of length k bits for value p[pidx] 220 | // make tables up to required level 221 | while(k > w + lx[1 + h]) { 222 | w += lx[1 + h]; // add bits already decoded 223 | h++; 224 | 225 | // compute minimum size table less than or equal to *m bits 226 | z = (z = g - w) > mm ? mm : z; // upper limit 227 | if((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table 228 | // too few codes for k-w bit table 229 | f -= a + 1; // deduct codes from patterns left 230 | xp = k; 231 | while(++j < z) { // try smaller tables up to z bits 232 | if((f <<= 1) <= c[++xp]) 233 | break; // enough codes to use up j bits 234 | f -= c[xp]; // else deduct codes from patterns 235 | } 236 | } 237 | if(w + j > el && w < el) 238 | j = el - w; // make EOB code end at table 239 | z = 1 << j; // table entries for j-bit table 240 | lx[1 + h] = j; // set table size in stack 241 | 242 | // allocate and link in new table 243 | q = new Array(z); 244 | for(o = 0; o < z; o++) { 245 | q[o] = new zip_HuftNode(); 246 | } 247 | 248 | if(tail == null) 249 | tail = this.root = new zip_HuftList(); 250 | else 251 | tail = tail.next = new zip_HuftList(); 252 | tail.next = null; 253 | tail.list = q; 254 | u[h] = q; // table starts after link 255 | 256 | /* connect to last table, if there is one */ 257 | if(h > 0) { 258 | x[h] = i; // save pattern for backing up 259 | r.b = lx[h]; // bits to dump before this table 260 | r.e = 16 + j; // bits in this table 261 | r.t = q; // pointer to this table 262 | j = (i & ((1 << w) - 1)) >> (w - lx[h]); 263 | u[h-1][j].e = r.e; 264 | u[h-1][j].b = r.b; 265 | u[h-1][j].n = r.n; 266 | u[h-1][j].t = r.t; 267 | } 268 | } 269 | 270 | // set up table entry in r 271 | r.b = k - w; 272 | if(pidx >= n) 273 | r.e = 99; // out of values--invalid code 274 | else if(p[pidx] < s) { 275 | r.e = (p[pidx] < 256 ? 16 : 15); // 256 is end-of-block code 276 | r.n = p[pidx++]; // simple code is just the value 277 | } else { 278 | r.e = e[p[pidx] - s]; // non-simple--look up in lists 279 | r.n = d[p[pidx++] - s]; 280 | } 281 | 282 | // fill code-like entries with r // 283 | f = 1 << (k - w); 284 | for(j = i >> w; j < z; j += f) { 285 | q[j].e = r.e; 286 | q[j].b = r.b; 287 | q[j].n = r.n; 288 | q[j].t = r.t; 289 | } 290 | 291 | // backwards increment the k-bit code i 292 | for(j = 1 << (k - 1); (i & j) != 0; j >>= 1) 293 | i ^= j; 294 | i ^= j; 295 | 296 | // backup over finished tables 297 | while((i & ((1 << w) - 1)) != x[h]) { 298 | w -= lx[h]; // don't need to update q 299 | h--; 300 | } 301 | } 302 | } 303 | 304 | /* return actual size of base table */ 305 | this.m = lx[1]; 306 | 307 | /* Return true (1) if we were given an incomplete table */ 308 | this.status = ((y != 0 && g != 1) ? 1 : 0); 309 | } /* end of constructor */ 310 | } 311 | 312 | 313 | /* routines (inflate) */ 314 | 315 | var zip_GET_BYTE = function() { 316 | if(zip_inflate_data.length == zip_inflate_pos) 317 | return -1; 318 | return zip_inflate_data.charCodeAt(zip_inflate_pos++) & 0xff; 319 | } 320 | 321 | var zip_NEEDBITS = function(n) { 322 | while(zip_bit_len < n) { 323 | zip_bit_buf |= zip_GET_BYTE() << zip_bit_len; 324 | zip_bit_len += 8; 325 | } 326 | } 327 | 328 | var zip_GETBITS = function(n) { 329 | return zip_bit_buf & zip_MASK_BITS[n]; 330 | } 331 | 332 | var zip_DUMPBITS = function(n) { 333 | zip_bit_buf >>= n; 334 | zip_bit_len -= n; 335 | } 336 | 337 | var zip_inflate_codes = function(buff, off, size) { 338 | /* inflate (decompress) the codes in a deflated (compressed) block. 339 | Return an error code or zero if it all goes ok. */ 340 | var e; // table entry flag/number of extra bits 341 | var t; // (zip_HuftNode) pointer to table entry 342 | var n; 343 | 344 | if(size == 0) 345 | return 0; 346 | 347 | // inflate the coded data 348 | n = 0; 349 | for(;;) { // do until end of block 350 | zip_NEEDBITS(zip_bl); 351 | t = zip_tl.list[zip_GETBITS(zip_bl)]; 352 | e = t.e; 353 | while(e > 16) { 354 | if(e == 99) 355 | return -1; 356 | zip_DUMPBITS(t.b); 357 | e -= 16; 358 | zip_NEEDBITS(e); 359 | t = t.t[zip_GETBITS(e)]; 360 | e = t.e; 361 | } 362 | zip_DUMPBITS(t.b); 363 | 364 | if(e == 16) { // then it's a literal 365 | zip_wp &= zip_WSIZE - 1; 366 | buff[off + n++] = zip_slide[zip_wp++] = t.n; 367 | if(n == size) 368 | return size; 369 | continue; 370 | } 371 | 372 | // exit if end of block 373 | if(e == 15) 374 | break; 375 | 376 | // it's an EOB or a length 377 | 378 | // get length of block to copy 379 | zip_NEEDBITS(e); 380 | zip_copy_leng = t.n + zip_GETBITS(e); 381 | zip_DUMPBITS(e); 382 | 383 | // decode distance of block to copy 384 | zip_NEEDBITS(zip_bd); 385 | t = zip_td.list[zip_GETBITS(zip_bd)]; 386 | e = t.e; 387 | 388 | while(e > 16) { 389 | if(e == 99) 390 | return -1; 391 | zip_DUMPBITS(t.b); 392 | e -= 16; 393 | zip_NEEDBITS(e); 394 | t = t.t[zip_GETBITS(e)]; 395 | e = t.e; 396 | } 397 | zip_DUMPBITS(t.b); 398 | zip_NEEDBITS(e); 399 | zip_copy_dist = zip_wp - t.n - zip_GETBITS(e); 400 | zip_DUMPBITS(e); 401 | 402 | // do the copy 403 | while(zip_copy_leng > 0 && n < size) { 404 | zip_copy_leng--; 405 | zip_copy_dist &= zip_WSIZE - 1; 406 | zip_wp &= zip_WSIZE - 1; 407 | buff[off + n++] = zip_slide[zip_wp++] 408 | = zip_slide[zip_copy_dist++]; 409 | } 410 | 411 | if(n == size) 412 | return size; 413 | } 414 | 415 | zip_method = -1; // done 416 | return n; 417 | } 418 | 419 | var zip_inflate_stored = function(buff, off, size) { 420 | /* "decompress" an inflated type 0 (stored) block. */ 421 | var n; 422 | 423 | // go to byte boundary 424 | n = zip_bit_len & 7; 425 | zip_DUMPBITS(n); 426 | 427 | // get the length and its complement 428 | zip_NEEDBITS(16); 429 | n = zip_GETBITS(16); 430 | zip_DUMPBITS(16); 431 | zip_NEEDBITS(16); 432 | if(n != ((~zip_bit_buf) & 0xffff)) 433 | return -1; // error in compressed data 434 | zip_DUMPBITS(16); 435 | 436 | // read and output the compressed data 437 | zip_copy_leng = n; 438 | 439 | n = 0; 440 | while(zip_copy_leng > 0 && n < size) { 441 | zip_copy_leng--; 442 | zip_wp &= zip_WSIZE - 1; 443 | zip_NEEDBITS(8); 444 | buff[off + n++] = zip_slide[zip_wp++] = 445 | zip_GETBITS(8); 446 | zip_DUMPBITS(8); 447 | } 448 | 449 | if(zip_copy_leng == 0) 450 | zip_method = -1; // done 451 | return n; 452 | } 453 | 454 | var zip_inflate_fixed = function(buff, off, size) { 455 | /* decompress an inflated type 1 (fixed Huffman codes) block. We should 456 | either replace this with a custom decoder, or at least precompute the 457 | Huffman tables. */ 458 | 459 | // if first time, set up tables for fixed blocks 460 | if(zip_fixed_tl == null) { 461 | var i; // temporary variable 462 | var l = new Array(288); // length list for huft_build 463 | var h; // zip_HuftBuild 464 | 465 | // literal table 466 | for(i = 0; i < 144; i++) 467 | l[i] = 8; 468 | for(; i < 256; i++) 469 | l[i] = 9; 470 | for(; i < 280; i++) 471 | l[i] = 7; 472 | for(; i < 288; i++) // make a complete, but wrong code set 473 | l[i] = 8; 474 | zip_fixed_bl = 7; 475 | 476 | h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext, 477 | zip_fixed_bl); 478 | if(h.status != 0) { 479 | alert("HufBuild error: "+h.status); 480 | return -1; 481 | } 482 | zip_fixed_tl = h.root; 483 | zip_fixed_bl = h.m; 484 | 485 | // distance table 486 | for(i = 0; i < 30; i++) // make an incomplete code set 487 | l[i] = 5; 488 | zip_fixed_bd = 5; 489 | 490 | h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd); 491 | if(h.status > 1) { 492 | zip_fixed_tl = null; 493 | alert("HufBuild error: "+h.status); 494 | return -1; 495 | } 496 | zip_fixed_td = h.root; 497 | zip_fixed_bd = h.m; 498 | } 499 | 500 | zip_tl = zip_fixed_tl; 501 | zip_td = zip_fixed_td; 502 | zip_bl = zip_fixed_bl; 503 | zip_bd = zip_fixed_bd; 504 | return zip_inflate_codes(buff, off, size); 505 | } 506 | 507 | var zip_inflate_dynamic = function(buff, off, size) { 508 | // decompress an inflated type 2 (dynamic Huffman codes) block. 509 | var i; // temporary variables 510 | var j; 511 | var l; // last length 512 | var n; // number of lengths to get 513 | var t; // (zip_HuftNode) literal/length code table 514 | var nb; // number of bit length codes 515 | var nl; // number of literal/length codes 516 | var nd; // number of distance codes 517 | var ll = new Array(286+30); // literal/length and distance code lengths 518 | var h; // (zip_HuftBuild) 519 | 520 | for(i = 0; i < ll.length; i++) 521 | ll[i] = 0; 522 | 523 | // read in table lengths 524 | zip_NEEDBITS(5); 525 | nl = 257 + zip_GETBITS(5); // number of literal/length codes 526 | zip_DUMPBITS(5); 527 | zip_NEEDBITS(5); 528 | nd = 1 + zip_GETBITS(5); // number of distance codes 529 | zip_DUMPBITS(5); 530 | zip_NEEDBITS(4); 531 | nb = 4 + zip_GETBITS(4); // number of bit length codes 532 | zip_DUMPBITS(4); 533 | if(nl > 286 || nd > 30) 534 | return -1; // bad lengths 535 | 536 | // read in bit-length-code lengths 537 | for(j = 0; j < nb; j++) 538 | { 539 | zip_NEEDBITS(3); 540 | ll[zip_border[j]] = zip_GETBITS(3); 541 | zip_DUMPBITS(3); 542 | } 543 | for(; j < 19; j++) 544 | ll[zip_border[j]] = 0; 545 | 546 | // build decoding table for trees--single level, 7 bit lookup 547 | zip_bl = 7; 548 | h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl); 549 | if(h.status != 0) 550 | return -1; // incomplete code set 551 | 552 | zip_tl = h.root; 553 | zip_bl = h.m; 554 | 555 | // read in literal and distance code lengths 556 | n = nl + nd; 557 | i = l = 0; 558 | while(i < n) { 559 | zip_NEEDBITS(zip_bl); 560 | t = zip_tl.list[zip_GETBITS(zip_bl)]; 561 | j = t.b; 562 | zip_DUMPBITS(j); 563 | j = t.n; 564 | if(j < 16) // length of code in bits (0..15) 565 | ll[i++] = l = j; // save last length in l 566 | else if(j == 16) { // repeat last length 3 to 6 times 567 | zip_NEEDBITS(2); 568 | j = 3 + zip_GETBITS(2); 569 | zip_DUMPBITS(2); 570 | if(i + j > n) 571 | return -1; 572 | while(j-- > 0) 573 | ll[i++] = l; 574 | } else if(j == 17) { // 3 to 10 zero length codes 575 | zip_NEEDBITS(3); 576 | j = 3 + zip_GETBITS(3); 577 | zip_DUMPBITS(3); 578 | if(i + j > n) 579 | return -1; 580 | while(j-- > 0) 581 | ll[i++] = 0; 582 | l = 0; 583 | } else { // j == 18: 11 to 138 zero length codes 584 | zip_NEEDBITS(7); 585 | j = 11 + zip_GETBITS(7); 586 | zip_DUMPBITS(7); 587 | if(i + j > n) 588 | return -1; 589 | while(j-- > 0) 590 | ll[i++] = 0; 591 | l = 0; 592 | } 593 | } 594 | 595 | // build the decoding tables for literal/length and distance codes 596 | zip_bl = zip_lbits; 597 | h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl); 598 | if(zip_bl == 0) // no literals or lengths 599 | h.status = 1; 600 | if(h.status != 0) { 601 | if(h.status == 1) 602 | ;// **incomplete literal tree** 603 | return -1; // incomplete code set 604 | } 605 | zip_tl = h.root; 606 | zip_bl = h.m; 607 | 608 | for(i = 0; i < nd; i++) 609 | ll[i] = ll[i + nl]; 610 | zip_bd = zip_dbits; 611 | h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd); 612 | zip_td = h.root; 613 | zip_bd = h.m; 614 | 615 | if(zip_bd == 0 && nl > 257) { // lengths but no distances 616 | // **incomplete distance tree** 617 | return -1; 618 | } 619 | 620 | if(h.status == 1) { 621 | ;// **incomplete distance tree** 622 | } 623 | if(h.status != 0) 624 | return -1; 625 | 626 | // decompress until an end-of-block code 627 | return zip_inflate_codes(buff, off, size); 628 | } 629 | 630 | var zip_inflate_start = function() { 631 | var i; 632 | 633 | if(zip_slide == null) 634 | zip_slide = new Array(2 * zip_WSIZE); 635 | zip_wp = 0; 636 | zip_bit_buf = 0; 637 | zip_bit_len = 0; 638 | zip_method = -1; 639 | zip_eof = false; 640 | zip_copy_leng = zip_copy_dist = 0; 641 | zip_tl = null; 642 | } 643 | 644 | var zip_inflate_internal = function(buff, off, size) { 645 | // decompress an inflated entry 646 | var n, i; 647 | 648 | n = 0; 649 | while(n < size) { 650 | if(zip_eof && zip_method == -1) 651 | return n; 652 | 653 | if(zip_copy_leng > 0) { 654 | if(zip_method != zip_STORED_BLOCK) { 655 | // STATIC_TREES or DYN_TREES 656 | while(zip_copy_leng > 0 && n < size) { 657 | zip_copy_leng--; 658 | zip_copy_dist &= zip_WSIZE - 1; 659 | zip_wp &= zip_WSIZE - 1; 660 | buff[off + n++] = zip_slide[zip_wp++] = 661 | zip_slide[zip_copy_dist++]; 662 | } 663 | } else { 664 | while(zip_copy_leng > 0 && n < size) { 665 | zip_copy_leng--; 666 | zip_wp &= zip_WSIZE - 1; 667 | zip_NEEDBITS(8); 668 | buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8); 669 | zip_DUMPBITS(8); 670 | } 671 | if(zip_copy_leng == 0) 672 | zip_method = -1; // done 673 | } 674 | if(n == size) 675 | return n; 676 | } 677 | 678 | if(zip_method == -1) { 679 | if(zip_eof) 680 | break; 681 | 682 | // read in last block bit 683 | zip_NEEDBITS(1); 684 | if(zip_GETBITS(1) != 0) 685 | zip_eof = true; 686 | zip_DUMPBITS(1); 687 | 688 | // read in block type 689 | zip_NEEDBITS(2); 690 | zip_method = zip_GETBITS(2); 691 | zip_DUMPBITS(2); 692 | zip_tl = null; 693 | zip_copy_leng = 0; 694 | } 695 | 696 | switch(zip_method) { 697 | case 0: // zip_STORED_BLOCK 698 | i = zip_inflate_stored(buff, off + n, size - n); 699 | break; 700 | 701 | case 1: // zip_STATIC_TREES 702 | if(zip_tl != null) 703 | i = zip_inflate_codes(buff, off + n, size - n); 704 | else 705 | i = zip_inflate_fixed(buff, off + n, size - n); 706 | break; 707 | 708 | case 2: // zip_DYN_TREES 709 | if(zip_tl != null) 710 | i = zip_inflate_codes(buff, off + n, size - n); 711 | else 712 | i = zip_inflate_dynamic(buff, off + n, size - n); 713 | break; 714 | 715 | default: // error 716 | i = -1; 717 | break; 718 | } 719 | 720 | if(i == -1) { 721 | if(zip_eof) 722 | return 0; 723 | return -1; 724 | } 725 | n += i; 726 | } 727 | return n; 728 | } 729 | 730 | var zip_inflate = function(str) { 731 | var i, j; 732 | 733 | zip_inflate_start(); 734 | zip_inflate_data = str; 735 | zip_inflate_pos = 0; 736 | 737 | var buff = new Array(1024); 738 | var aout = []; 739 | while((i = zip_inflate_internal(buff, 0, buff.length)) > 0) { 740 | var cbuf = new Array(i); 741 | for(j = 0; j < i; j++){ 742 | cbuf[j] = String.fromCharCode(buff[j]); 743 | } 744 | aout[aout.length] = cbuf.join(""); 745 | } 746 | zip_inflate_data = null; // G.C. 747 | return aout.join(""); 748 | } 749 | 750 | if (! window.RawDeflate) RawDeflate = {}; 751 | RawDeflate.inflate = zip_inflate; 752 | 753 | })(); 754 | -------------------------------------------------------------------------------- /js/zerobin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ZeroBin 0.19 3 | * 4 | * @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin 5 | * @author sebsauvage 6 | */ 7 | 8 | // Immediately start random number generator collector. 9 | sjcl.random.startCollectors(); 10 | 11 | /** 12 | * Converts a duration (in seconds) into human readable format. 13 | * 14 | * @param int seconds 15 | * @return string 16 | */ 17 | function secondsToHuman(seconds) 18 | { 19 | if (seconds<60) { var v=Math.floor(seconds); return v+' second'+((v>1)?'s':''); } 20 | if (seconds<60*60) { var v=Math.floor(seconds/60); return v+' minute'+((v>1)?'s':''); } 21 | if (seconds<60*60*24) { var v=Math.floor(seconds/(60*60)); return v+' hour'+((v>1)?'s':''); } 22 | // If less than 2 months, display in days: 23 | if (seconds<60*60*24*60) { var v=Math.floor(seconds/(60*60*24)); return v+' day'+((v>1)?'s':''); } 24 | var v=Math.floor(seconds/(60*60*24*30)); return v+' month'+((v>1)?'s':''); 25 | } 26 | 27 | /** 28 | * Converts an associative array to an encoded string 29 | * for appending to the anchor. 30 | * 31 | * @param object associative_array Object to be serialized 32 | * @return string 33 | */ 34 | function hashToParameterString(associativeArray) 35 | { 36 | var parameterString = "" 37 | for (key in associativeArray) 38 | { 39 | if( parameterString === "" ) 40 | { 41 | parameterString = encodeURIComponent(key); 42 | parameterString += "=" + encodeURIComponent(associativeArray[key]); 43 | } else { 44 | parameterString += "&" + encodeURIComponent(key); 45 | parameterString += "=" + encodeURIComponent(associativeArray[key]); 46 | } 47 | } 48 | //padding for URL shorteners 49 | parameterString += "&p=p"; 50 | 51 | return parameterString; 52 | } 53 | 54 | /** 55 | * Converts a string to an associative array. 56 | * 57 | * @param string parameter_string String containing parameters 58 | * @return object 59 | */ 60 | function parameterStringToHash(parameterString) 61 | { 62 | var parameterHash = {}; 63 | var parameterArray = parameterString.split("&"); 64 | for (var i = 0; i < parameterArray.length; i++) { 65 | //var currentParamterString = decodeURIComponent(parameterArray[i]); 66 | var pair = parameterArray[i].split("="); 67 | var key = decodeURIComponent(pair[0]); 68 | var value = decodeURIComponent(pair[1]); 69 | parameterHash[key] = value; 70 | } 71 | 72 | return parameterHash; 73 | } 74 | 75 | /** 76 | * Get an associative array of the parameters found in the anchor 77 | * 78 | * @return object 79 | **/ 80 | function getParameterHash() 81 | { 82 | var hashIndex = window.location.href.indexOf("#"); 83 | if (hashIndex >= 0) { 84 | return parameterStringToHash(window.location.href.substring(hashIndex + 1)); 85 | } else { 86 | return {}; 87 | } 88 | } 89 | 90 | /** 91 | * Compress a message (deflate compression). Returns base64 encoded data. 92 | * 93 | * @param string message 94 | * @return base64 string data 95 | */ 96 | function compress(message) { 97 | return Base64.toBase64( RawDeflate.deflate( Base64.utob(message) ) ); 98 | } 99 | 100 | /** 101 | * Decompress a message compressed with compress(). 102 | */ 103 | function decompress(data) { 104 | return Base64.btou( RawDeflate.inflate( Base64.fromBase64(data) ) ); 105 | } 106 | 107 | /** 108 | * Compress, then encrypt message with key. 109 | * 110 | * @param string key 111 | * @param string message 112 | * @return encrypted string data 113 | */ 114 | function zeroCipher(key, message) { 115 | return sjcl.encrypt(key,compress(message)); 116 | } 117 | /** 118 | * Decrypt message with key, then decompress. 119 | * 120 | * @param key 121 | * @param encrypted string data 122 | * @return string readable message 123 | */ 124 | function zeroDecipher(key, data) { 125 | return decompress(sjcl.decrypt(key,data)); 126 | } 127 | 128 | /** 129 | * @return the current script location (without search or hash part of the URL). 130 | * eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/ 131 | */ 132 | function scriptLocation() { 133 | var scriptLocation = window.location.href.substring(0,window.location.href.length 134 | - window.location.search.length - window.location.hash.length); 135 | var hashIndex = scriptLocation.indexOf("#"); 136 | if (hashIndex !== -1) { 137 | scriptLocation = scriptLocation.substring(0, hashIndex) 138 | } 139 | return scriptLocation 140 | } 141 | 142 | /** 143 | * @return the paste unique identifier from the URL 144 | * eg. 'c05354954c49a487' 145 | */ 146 | function pasteID() { 147 | return window.location.search.substring(1); 148 | } 149 | 150 | function htmlEntities(str) { 151 | return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); 152 | } 153 | /** 154 | * Set text of a DOM element (required for IE) 155 | * This is equivalent to element.text(text) 156 | * @param object element : a DOM element. 157 | * @param string text : the text to enter. 158 | */ 159 | function setElementText(element, text) { 160 | // For IE<10. 161 | if ($('div#oldienotice').is(":visible")) { 162 | // IE<10 does not support white-space:pre-wrap; so we have to do this BIG UGLY STINKING THING. 163 | var html = htmlEntities(text).replace(/\n/ig,"\r\n
"); 164 | element.html('
'+html+'
'); 165 | } 166 | // for other (sane) browsers: 167 | else { 168 | element.text(text); 169 | } 170 | } 171 | 172 | /** Apply syntax coloring to clear text area. 173 | */ 174 | function applySyntaxColoring() 175 | { 176 | if ($('div#cleartext').html().substring(0,11) != '
')
177 |     {
178 |         // highlight.js expects code to be surrounded by 

179 |         $('div#cleartext').html('
'+ $('div#cleartext').html()+'
'); 180 | } 181 | hljs.highlightBlock(document.getElementById('cleartext')); 182 | $('div#cleartext').css('padding','0'); // Remove white padding around code box. 183 | } 184 | 185 | 186 | /** 187 | * Show decrypted text in the display area, including discussion (if open) 188 | * 189 | * @param string key : decryption key 190 | * @param array comments : Array of messages to display (items = array with keys ('data','meta') 191 | */ 192 | function displayMessages(key, comments) { 193 | try { // Try to decrypt the paste. 194 | var cleartext = zeroDecipher(key, comments[0].data); 195 | } catch(err) { 196 | $('div#cleartext').hide(); 197 | $('button#clonebutton').hide(); 198 | showError('Could not decrypt data (Wrong key ?)'); 199 | return; 200 | } 201 | setElementText($('div#cleartext'), cleartext); 202 | urls2links($('div#cleartext')); // Convert URLs to clickable links. 203 | 204 | // comments[0] is the paste itself. 205 | 206 | if (comments[0].meta.syntaxcoloring) applySyntaxColoring(); 207 | 208 | // Display paste expiration. 209 | if (comments[0].meta.expire_date) $('div#remainingtime').removeClass('foryoureyesonly').text('This document will expire in '+secondsToHuman(comments[0].meta.remaining_time)+'.').show(); 210 | if (comments[0].meta.burnafterreading) { 211 | $('div#remainingtime').addClass('foryoureyesonly').text('FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.').show(); 212 | $('button#clonebutton').hide(); // Discourage cloning (as it can't really be prevented). 213 | } 214 | 215 | // If the discussion is opened on this paste, display it. 216 | if (comments[0].meta.opendiscussion) { 217 | $('div#comments').html(''); 218 | // For each comment. 219 | for (var i = 1; i < comments.length; i++) { 220 | var comment=comments[i]; 221 | var cleartext="[Could not decrypt comment ; Wrong key ?]"; 222 | try { 223 | cleartext = zeroDecipher(key, comment.data); 224 | } catch(err) { } 225 | var place = $('div#comments'); 226 | // If parent comment exists, display below (CSS will automatically shift it right.) 227 | var cname = 'div#comment_'+comment.meta.parentid 228 | 229 | // If the element exists in page 230 | if ($(cname).length) { 231 | place = $(cname); 232 | } 233 | var divComment = $('
' 234 | + '
' 235 | + '' 236 | + '
'); 237 | setElementText(divComment.find('div.commentdata'), cleartext); 238 | // Convert URLs to clickable links in comment. 239 | urls2links(divComment.find('div.commentdata')); 240 | divComment.find('span.nickname').html('(Anonymous)'); 241 | 242 | // Try to get optional nickname: 243 | try { 244 | divComment.find('span.nickname').text(zeroDecipher(key, comment.meta.nickname)); 245 | } catch(err) { } 246 | divComment.find('span.commentdate').text(' ('+(new Date(comment.meta.postdate*1000).toString())+')').attr('title','CommentID: ' + comment.meta.commentid); 247 | 248 | // If an avatar is available, display it. 249 | if (comment.meta.vizhash) { 250 | divComment.find('span.nickname').before(''); 251 | } 252 | 253 | place.append(divComment); 254 | } 255 | $('div#comments').append('
'); 256 | $('div#discussion').show(); 257 | } 258 | } 259 | 260 | /** 261 | * Open the comment entry when clicking the "Reply" button of a comment. 262 | * @param object source : element which emitted the event. 263 | * @param string commentid = identifier of the comment we want to reply to. 264 | */ 265 | function open_reply(source, commentid) { 266 | $('div.reply').remove(); // Remove any other reply area. 267 | source.after('
' 268 | + '' 269 | + '' 270 | + '
' 271 | + '
 
' 272 | + '
'); 273 | $('input#nickname').focus(function() { 274 | $(this).css('color', '#000'); 275 | if ($(this).val() == $(this).attr('title')) { 276 | $(this).val(''); 277 | } 278 | }); 279 | $('textarea#replymessage').focus(); 280 | } 281 | 282 | /** 283 | * Send a reply in a discussion. 284 | * @param string parentid : the comment identifier we want to send a reply to. 285 | */ 286 | function send_comment(parentid) { 287 | // Do not send if no data. 288 | if ($('textarea#replymessage').val().length==0) { 289 | return; 290 | } 291 | 292 | showStatus('Sending comment...', spin=true); 293 | var cipherdata = zeroCipher(pageKey(), $('textarea#replymessage').val()); 294 | var ciphernickname = ''; 295 | var nick=$('input#nickname').val(); 296 | if (nick != '' && nick != 'Optional nickname...') { 297 | ciphernickname = zeroCipher(pageKey(), nick); 298 | } 299 | var data_to_send = { data:cipherdata, 300 | parentid: parentid, 301 | pasteid: pasteID(), 302 | nickname: ciphernickname 303 | }; 304 | 305 | $.post(scriptLocation(), data_to_send, 'json') 306 | .error(function() { 307 | showError('Comment could not be sent (serveur error or not responding).'); 308 | }) 309 | .success(function(data) { 310 | if (data.status == 0) { 311 | showStatus('Comment posted.'); 312 | location.reload(); 313 | } 314 | else if (data.status==1) { 315 | showError('Could not post comment: '+data.message); 316 | } 317 | else { 318 | showError('Could not post comment.'); 319 | } 320 | }); 321 | } 322 | 323 | 324 | /** 325 | * Send a new paste to server 326 | */ 327 | function send_data() { 328 | // Do not send if no data. 329 | if ($('textarea#message').val().length == 0) { 330 | return; 331 | } 332 | 333 | // If sjcl has not collected enough entropy yet, display a message. 334 | if (!sjcl.random.isReady()) 335 | { 336 | showStatus('Sending paste (Please move your mouse for more entropy)...', spin=true); 337 | sjcl.random.addEventListener('seeded', function(){ send_data(); }); 338 | return; 339 | } 340 | 341 | showStatus('Sending paste...', spin=true); 342 | 343 | var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); 344 | var cipherdata = zeroCipher(randomkey, $('textarea#message').val()); 345 | var data_to_send = { data: cipherdata, 346 | expire: $('select#pasteExpiration').val(), 347 | burnafterreading: $('input#burnafterreading').is(':checked') ? 1 : 0, 348 | opendiscussion: $('input#opendiscussion').is(':checked') ? 1 : 0, 349 | syntaxcoloring: $('input#syntaxcoloring').is(':checked') ? 1 : 0 350 | }; 351 | $.post(scriptLocation(), data_to_send, 'json') 352 | .error(function() { 353 | showError('Data could not be sent (serveur error or not responding).'); 354 | }) 355 | .success(function(data) { 356 | if (data.status == 0) { 357 | stateExistingPaste(); 358 | var url = scriptLocation() + "?" + data.id + '#' + randomkey; 359 | var deleteUrl = scriptLocation() + "?pasteid=" + data.id + '&deletetoken=' + data.deletetoken; 360 | showStatus(''); 361 | 362 | $('div#pastelink').html('Your paste is ' + url + ' (Hit CTRL+C to copy)'); 363 | $('div#deletelink').html('Delete link'); 364 | $('div#pasteresult').show(); 365 | selectText('pasteurl'); // We pre-select the link so that the user only has to CTRL+C the link. 366 | 367 | setElementText($('div#cleartext'), $('textarea#message').val()); 368 | urls2links($('div#cleartext')); 369 | 370 | // FIXME: Add option to remove syntax highlighting ? 371 | if ($('input#syntaxcoloring').is(':checked')) applySyntaxColoring(); 372 | 373 | showStatus(''); 374 | } 375 | else if (data.status==1) { 376 | showError('Could not create paste: '+data.message); 377 | } 378 | else { 379 | showError('Could not create paste.'); 380 | } 381 | }); 382 | } 383 | 384 | 385 | /** Text range selection. 386 | * From: http://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse 387 | * @param string element : Indentifier of the element to select (id=""). 388 | */ 389 | function selectText(element) { 390 | var doc = document 391 | , text = doc.getElementById(element) 392 | , range, selection 393 | ; 394 | if (doc.body.createTextRange) { //ms 395 | range = doc.body.createTextRange(); 396 | range.moveToElementText(text); 397 | range.select(); 398 | } else if (window.getSelection) { //all others 399 | selection = window.getSelection(); 400 | range = doc.createRange(); 401 | range.selectNodeContents(text); 402 | selection.removeAllRanges(); 403 | selection.addRange(range); 404 | } 405 | } 406 | /** 407 | * Put the screen in "New paste" mode. 408 | */ 409 | function stateNewPaste() { 410 | $('button#sendbutton').show(); 411 | $('button#clonebutton').hide(); 412 | $('button#rawtextbutton').hide(); 413 | $('div#expiration').show(); 414 | $('div#remainingtime').hide(); 415 | $('div#burnafterreadingoption').show(); 416 | $('div#opendisc').show(); 417 | $('div#syntaxcoloringoption').show(); 418 | $('button#newbutton').show(); 419 | $('div#pasteresult').hide(); 420 | $('textarea#message').text(''); 421 | $('textarea#message').show(); 422 | $('div#cleartext').hide(); 423 | $('div#message').focus(); 424 | $('div#discussion').hide(); 425 | } 426 | 427 | /** 428 | * Put the screen in "Existing paste" mode. 429 | */ 430 | function stateExistingPaste() { 431 | $('button#sendbutton').hide(); 432 | 433 | // No "clone" for IE<10. 434 | if ($('div#oldienotice').is(":visible")) { 435 | $('button#clonebutton').hide(); 436 | } 437 | else { 438 | $('button#clonebutton').show(); 439 | } 440 | $('button#rawtextbutton').show(); 441 | 442 | $('div#expiration').hide(); 443 | $('div#burnafterreadingoption').hide(); 444 | $('div#opendisc').hide(); 445 | $('div#syntaxcoloringoption').hide(); 446 | $('button#newbutton').show(); 447 | $('div#pasteresult').hide(); 448 | $('textarea#message').hide(); 449 | $('div#cleartext').show(); 450 | } 451 | 452 | /** Return raw text 453 | */ 454 | function rawText() 455 | { 456 | var paste = $('div#cleartext').html(); 457 | var newDoc = document.open('text/html', 'replace'); 458 | newDoc.write('
'+paste+'
'); 459 | newDoc.close(); 460 | } 461 | 462 | /** 463 | * Clone the current paste. 464 | */ 465 | function clonePaste() { 466 | stateNewPaste(); 467 | 468 | //Erase the id and the key in url 469 | history.replaceState(document.title, document.title, scriptLocation()); 470 | 471 | showStatus(''); 472 | $('textarea#message').text($('div#cleartext').text()); 473 | } 474 | 475 | /** 476 | * Create a new paste. 477 | */ 478 | function newPaste() { 479 | stateNewPaste(); 480 | showStatus(''); 481 | $('textarea#message').text(''); 482 | } 483 | 484 | /** 485 | * Display an error message 486 | * (We use the same function for paste and reply to comments) 487 | */ 488 | function showError(message) { 489 | $('div#status').addClass('errorMessage').text(message); 490 | $('div#replystatus').addClass('errorMessage').text(message); 491 | } 492 | 493 | /** 494 | * Display status 495 | * (We use the same function for paste and reply to comments) 496 | * 497 | * @param string message : text to display 498 | * @param boolean spin (optional) : tell if the "spinning" animation should be displayed. 499 | */ 500 | function showStatus(message, spin) { 501 | $('div#replystatus').removeClass('errorMessage'); 502 | $('div#replystatus').text(message); 503 | if (!message) { 504 | $('div#status').html(' '); 505 | return; 506 | } 507 | if (message == '') { 508 | $('div#status').html(' '); 509 | return; 510 | } 511 | $('div#status').removeClass('errorMessage'); 512 | $('div#status').text(message); 513 | if (spin) { 514 | var img = ''; 515 | $('div#status').prepend(img); 516 | $('div#replystatus').prepend(img); 517 | } 518 | } 519 | 520 | /** 521 | * Convert URLs to clickable links. 522 | * URLs to handle: 523 | * 524 | * magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7 525 | * http://localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM= 526 | * http://user:password@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM= 527 | * 528 | * 529 | * @param object element : a jQuery DOM element. 530 | * @FIXME: add ppa & apt links. 531 | */ 532 | function urls2links(element) { 533 | var re = /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig; 534 | element.html(element.html().replace(re,'$1')); 535 | var re = /((magnet):[\w?=&.\/-;#@~%+-]+)/ig; 536 | element.html(element.html().replace(re,'$1')); 537 | } 538 | 539 | /** 540 | * Return the deciphering key stored in anchor part of the URL 541 | */ 542 | function pageKey() { 543 | var key = window.location.hash.substring(1); // Get key 544 | 545 | // Some stupid web 2.0 services and redirectors add data AFTER the anchor 546 | // (such as &utm_source=...). 547 | // We will strip any additional data. 548 | 549 | // First, strip everything after the equal sign (=) which signals end of base64 string. 550 | i = key.indexOf('='); if (i>-1) { key = key.substring(0,i+1); } 551 | 552 | // If the equal sign was not present, some parameters may remain: 553 | i = key.indexOf('&'); if (i>-1) { key = key.substring(0,i); } 554 | 555 | // Then add trailing equal sign if it's missing 556 | if (key.charAt(key.length-1)!=='=') key+='='; 557 | 558 | return key; 559 | } 560 | 561 | $(function() { 562 | 563 | // If "burn after reading" is checked, disable discussion. 564 | $('input#burnafterreading').change(function() { 565 | if ($(this).is(':checked') ) { 566 | $('div#opendisc').addClass('buttondisabled'); 567 | $('input#opendiscussion').attr({checked: false}); 568 | $('input#opendiscussion').attr('disabled',true); 569 | } 570 | else { 571 | $('div#opendisc').removeClass('buttondisabled'); 572 | $('input#opendiscussion').removeAttr('disabled'); 573 | } 574 | }); 575 | 576 | // Display status returned by php code if any (eg. Paste was properly deleted.) 577 | if ($('div#status').text().length > 0) { 578 | showStatus($('div#status').text(),false); 579 | return; 580 | } 581 | 582 | $('div#status').html(' '); // Keep line height even if content empty. 583 | 584 | // Display an existing paste 585 | if ($('div#cipherdata').text().length > 1) { 586 | // Missing decryption key in URL ? 587 | if (window.location.hash.length == 0) { 588 | showError('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL ?)'); 589 | return; 590 | } 591 | 592 | // List of messages to display 593 | var messages = jQuery.parseJSON($('div#cipherdata').text()); 594 | 595 | // Show proper elements on screen. 596 | stateExistingPaste(); 597 | 598 | displayMessages(pageKey(), messages); 599 | } 600 | // Display error message from php code. 601 | else if ($('div#errormessage').text().length>1) { 602 | showError($('div#errormessage').text()); 603 | } 604 | // Create a new paste. 605 | else { 606 | newPaste(); 607 | } 608 | }); 609 | -------------------------------------------------------------------------------- /lib/rain.tpl.class.php: -------------------------------------------------------------------------------- 1 | ), stylesheet (), script (