mirror of
https://github.com/YunoHost-Apps/privatebin_ynh.git
synced 2024-09-03 20:15:56 +02:00
initial commit
This commit is contained in:
commit
2392fe3f35
27 changed files with 5770 additions and 0 deletions
16
conf/nginx.conf
Normal file
16
conf/nginx.conf
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
location YNH_WWW_PATH {
|
||||||
|
alias YNH_WWW_ALIAS ;
|
||||||
|
if ($scheme = http) {
|
||||||
|
rewrite ^ https://$server_name$request_uri? permanent;
|
||||||
|
}
|
||||||
|
index index.php;
|
||||||
|
try_files $uri $uri/ index.php;
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
||||||
|
fastcgi_pass unix:/var/run/php5-fpm.sock;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param REMOTE_USER $remote_user;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
}
|
40
manifest.json
Normal file
40
manifest.json
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"name": "Zerobin",
|
||||||
|
"id": "zerobin",
|
||||||
|
"description": {
|
||||||
|
"en": "A minimalist, opensource online pastebin where the server has zero knowledge of pasted data"
|
||||||
|
},
|
||||||
|
"developer": {
|
||||||
|
"name": "julien",
|
||||||
|
"email": "julien.malik@paraiso.me",
|
||||||
|
"url": "http://sebsauvage.net/wiki/doku.php?id=php:zerobin"
|
||||||
|
},
|
||||||
|
"multi_instance": "false",
|
||||||
|
"arguments": {
|
||||||
|
"install" : [
|
||||||
|
{
|
||||||
|
"name": "domain",
|
||||||
|
"ask": {
|
||||||
|
"en": "Choose a domain for Zerobin"
|
||||||
|
},
|
||||||
|
"example": "domain.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"ask": {
|
||||||
|
"en": "Choose a path for Zerobin"
|
||||||
|
},
|
||||||
|
"example": "/zerobin",
|
||||||
|
"default": "/zerobin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "is_public",
|
||||||
|
"ask": {
|
||||||
|
"en": "Is it a public Zerobin site ?"
|
||||||
|
},
|
||||||
|
"choices": ["Yes", "No"],
|
||||||
|
"default": "Yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
40
scripts/install
Executable file
40
scripts/install
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Retrieve arguments
|
||||||
|
domain=$1
|
||||||
|
path=$2
|
||||||
|
is_public=$3
|
||||||
|
|
||||||
|
# Check domain/path availability
|
||||||
|
sudo yunohost app checkurl $domain$path -a zerobin
|
||||||
|
if [[ ! $? -eq 0 ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy files to the right place
|
||||||
|
final_path=/var/www/zerobin
|
||||||
|
sudo mkdir -p $final_path
|
||||||
|
sudo cp -a ../sources/* $final_path
|
||||||
|
|
||||||
|
# Files owned by root, www-data can just read
|
||||||
|
sudo find $final_path -type f | xargs sudo chmod 644
|
||||||
|
sudo find $final_path -type d | xargs sudo chmod 755
|
||||||
|
sudo chown -R root: $final_path
|
||||||
|
|
||||||
|
# except for data and tmp subdir, where www-data must have write permissions
|
||||||
|
sudo mkdir -p $final_path/{data,tmp}
|
||||||
|
sudo chown www-data:root $final_path/{data,tmp}
|
||||||
|
sudo chmod 700 $final_path/{data,tmp}
|
||||||
|
|
||||||
|
# Modify Nginx configuration file and copy it to Nginx conf directory
|
||||||
|
sed -i "s@YNH_WWW_PATH@$path@g" ../conf/nginx.conf
|
||||||
|
sed -i "s@YNH_WWW_ALIAS@$final_path/@g" ../conf/nginx.conf
|
||||||
|
sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/zerobin.conf
|
||||||
|
|
||||||
|
if [ $is_public = "Yes" ];
|
||||||
|
then
|
||||||
|
sudo yunohost app setting zerobin skipped_uris -v "/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo service nginx reload
|
||||||
|
sudo yunohost app ssowatconf
|
6
scripts/remove
Executable file
6
scripts/remove
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
domain=$(sudo yunohost app setting ttrss domain)
|
||||||
|
|
||||||
|
sudo rm -rf /var/www/zerobin
|
||||||
|
sudo rm -f /etc/nginx/conf.d/$domain.d/zerobin.conf
|
35
scripts/upgrade
Normal file
35
scripts/upgrade
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
domain=$(sudo yunohost app setting zerobin domain)
|
||||||
|
path=$(sudo yunohost app setting zerobin path)
|
||||||
|
is_public=$(sudo yunohost app setting zerobin is_public)
|
||||||
|
|
||||||
|
# Remove trailing "/" for next commands
|
||||||
|
path=${path%/}
|
||||||
|
|
||||||
|
# Copy files to the right place
|
||||||
|
final_path=/var/www/zerobin
|
||||||
|
sudo mkdir -p $final_path
|
||||||
|
sudo cp -a ../sources/* $final_path
|
||||||
|
|
||||||
|
# Files owned by root, www-data can just read
|
||||||
|
sudo find $final_path -type f | xargs sudo chmod 644
|
||||||
|
sudo find $final_path -type d | xargs sudo chmod 755
|
||||||
|
sudo chown -R root: $final_path
|
||||||
|
|
||||||
|
# except for data and tmp subdir, where www-data must have write permissions
|
||||||
|
sudo chown www-data:root $final_path/{data,tmp}
|
||||||
|
sudo chmod 700 $final_path/{data,tmp}
|
||||||
|
|
||||||
|
# Modify Nginx configuration file and copy it to Nginx conf directory
|
||||||
|
sed -i "s@YNH_WWW_PATH@$path@g" ../conf/nginx.conf
|
||||||
|
sed -i "s@YNH_WWW_ALIAS@$final_path/@g" ../conf/nginx.conf
|
||||||
|
sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/zerobin.conf
|
||||||
|
|
||||||
|
if [ $is_public = "Yes" ];
|
||||||
|
then
|
||||||
|
sudo yunohost app setting zerobin skipped_uris -v "/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo service nginx reload
|
||||||
|
sudo yunohost app ssowatconf
|
69
sources/CHANGELOG.md
Normal file
69
sources/CHANGELOG.md
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# ZeroBin version history #
|
||||||
|
|
||||||
|
* **Alpha 0.8 (2012-04-11):**
|
||||||
|
* Source code not published yet.
|
||||||
|
* Interface completely redesigned. Icons added.
|
||||||
|
* Now properly supports IE6/7 (ugly display, but it works. "Clone" button is disabled though.)
|
||||||
|
* Added one level of depth for storage directories (This is better for higher load servers).
|
||||||
|
* php version is now checked (min: 5.2.6)
|
||||||
|
* Better checks on posted json data on server.
|
||||||
|
* Added "1 year" expiration.
|
||||||
|
* URLs are now converted to clickable links. This include http, https, ftp and magnet links.
|
||||||
|
* Clickable links include ''rel="nofollow"'' to discourage SEO.
|
||||||
|
* On my public service (http://sebsauvage.net/paste/)
|
||||||
|
* All data will be deleted (you were warned - this is a test service)
|
||||||
|
* Default paste expiration is now 1 month to prevent clogging-up my host.
|
||||||
|
* **Alpha 0.9 (2012-04-11):**
|
||||||
|
* 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.
|
||||||
|
* **Alpha 0.10 (2012-04-12):**
|
||||||
|
* 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.)
|
||||||
|
* **Alpha 0.11 (2012-04-12):**
|
||||||
|
* Automatically ignore parameters (such as &utm_source=...) added //after// the anchor by some stupid Web 2.0 services.
|
||||||
|
* First public release.
|
||||||
|
* **Alpha 0.12 (2012-04-18):**
|
||||||
|
* **DISCUSSIONS !** Now you can enable discussions on your pastes. Of course, posted comments and nickname are also encrypted and the server cannot see them.
|
||||||
|
* This feature implies a change in storage format. You will have to delete all previous pastes in your ZeroBin.
|
||||||
|
* 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.
|
||||||
|
* Remaining time before expiration is now displayed.
|
||||||
|
* Explicit tags were added to CSS and jQuery selectors (eg. div#aaa instead of #aaa) to speed up browser.
|
||||||
|
* Better cleaning of the URL (to make sure the key is not broken by some stupid redirection service)
|
||||||
|
* **Alpha 0.13 (2012-04-18):**
|
||||||
|
* 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).
|
||||||
|
* FIXED: $error not properly initialized in index.php
|
||||||
|
* **Alpha 0.14 (2012-04-20):**
|
||||||
|
* ADDED: GD presence is checked.
|
||||||
|
* CHANGED: Traffic limiter data files moved to data/ (→easier rights management)
|
||||||
|
* ADDED: "Burn after reading" implemented. Opening the URL will display the paste and immediately destroy it on server.
|
||||||
|
* **Alpha 0.15 (2012-04-20):**
|
||||||
|
* FIXED: 2 minor corrections to avoid notices in php log.
|
||||||
|
* FIXED: Sources converted to UTF-8.
|
||||||
|
* **Alpha 0.15 (2012-04-20):**
|
||||||
|
* FIXED: 2 minor corrections to avoid notices in php log.
|
||||||
|
* FIXED: Sources converted to UTF-8.
|
||||||
|
* **Alpha 0.16**:
|
||||||
|
* FIXED minor php warnings.
|
||||||
|
* FIXED: zerobin.js reformated and properly commented.
|
||||||
|
* FIXED: Directory structure re-organized.
|
||||||
|
* CHANGED: URL shortening button was removed. (It was bad for privacy.)
|
||||||
|
* **Alpha 0.17 (2013-02-23)**:
|
||||||
|
* ADDED: Deletion URL.
|
||||||
|
* small refactoring.
|
||||||
|
* improved regex checks.
|
||||||
|
* larger server alt on installation.
|
||||||
|
* **Alpha 0.18 (2013-02-24)**:
|
||||||
|
* ADDED: The resulting URL is automatically selected after pressing "Send". You just have to press CTRL+C.
|
||||||
|
* ADDED: Automatic syntax highlighting for 53 languages using highlight.js
|
||||||
|
* ADDED: "5 minutes" and "1 week" expirations.
|
||||||
|
* ADDED: "Raw text" button.
|
||||||
|
* jQuery upgraded to 1.9.1
|
||||||
|
* sjcl upgraded to GitHub master 2013-02-23
|
||||||
|
* base64.js upgraded to 1.7
|
||||||
|
* FIXED: Dates in discussion are now proper local dates.
|
||||||
|
* ADDED: Robot meta tags in HTML to prevent search engines indexing.
|
||||||
|
* ADDED: Better json checking (including entropy).
|
||||||
|
* 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.
|
||||||
|
* "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.
|
||||||
|
* **Alpha 0.19 (2013-07-05)**:
|
||||||
|
* Corrected XSS security flaw which affected IE<10. Other browsers were not affected.
|
||||||
|
* Corrected spacing display in IE<10.
|
||||||
|
|
34
sources/README.md
Normal file
34
sources/README.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
ZeroBin 0.19 Alpha
|
||||||
|
|
||||||
|
==== THIS IS ALPHA SOFTWARE - USE AT YOUR OWN RISKS ====
|
||||||
|
|
||||||
|
ZeroBin is a minimalist, opensource online pastebin where the server
|
||||||
|
has zero knowledge of pasted data. Data is encrypted/decrypted in the
|
||||||
|
browser using 256 bits AES.
|
||||||
|
|
||||||
|
More information on the project page:
|
||||||
|
http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied warranty.
|
||||||
|
In no event will the authors be held liable for any damages arising from
|
||||||
|
the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must
|
||||||
|
not claim that you wrote the original software. If you use this
|
||||||
|
software in a product, an acknowledgment in the product documentation
|
||||||
|
would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must
|
||||||
|
not be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
368
sources/css/zerobin.css
Normal file
368
sources/css/zerobin.css
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
/* ZeroBin 0.19 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
||||||
|
|
||||||
|
|
||||||
|
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.
|
||||||
|
Licensed under the BSD License. - http://yuilibrary.com/license/ */
|
||||||
|
html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{*font-size:100%}legend{color:#000}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color:#455463;
|
||||||
|
color:white;
|
||||||
|
min-height:100%;
|
||||||
|
background-image: linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #0F1823 0%, #455463 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #0F1823), color-stop(1, #455463));
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin-bottom:15px;
|
||||||
|
padding-left:60px; padding-right:60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a { color:#0F388F; }
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size:3.5em;
|
||||||
|
font-weight:700;
|
||||||
|
color:#000;
|
||||||
|
position:relative;
|
||||||
|
display:inline;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:before {
|
||||||
|
content:attr(title);
|
||||||
|
position:absolute;
|
||||||
|
color:rgba(255,255,255,0.15);
|
||||||
|
top:1px;
|
||||||
|
left:1px;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color:#000;
|
||||||
|
font-size:1em;
|
||||||
|
display:inline;
|
||||||
|
font-style:italic;
|
||||||
|
font-weight:bold;
|
||||||
|
position:relative;
|
||||||
|
bottom:8px;}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color:#94a3b4;
|
||||||
|
font-size:0.7em;
|
||||||
|
display:inline;
|
||||||
|
position:relative;
|
||||||
|
bottom:8px;}
|
||||||
|
|
||||||
|
#aboutbox {
|
||||||
|
font-size:0.85em;
|
||||||
|
color: #94a3b4;
|
||||||
|
padding: 4px 8px 4px 16px;
|
||||||
|
position:relative;
|
||||||
|
top:10px;
|
||||||
|
border-left: 2px solid #94a3b4;
|
||||||
|
float:right;
|
||||||
|
width:60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#aboutbox a { color: #94a3b4; }
|
||||||
|
textarea#message,div#cleartext,.replymessage, code {
|
||||||
|
clear:both;
|
||||||
|
color:black;
|
||||||
|
background-color:#fff;
|
||||||
|
white-space:pre-wrap;
|
||||||
|
font-family:Consolas,"Lucida Console","DejaVu Sans Mono",Monaco,monospace;
|
||||||
|
font-size:9pt;
|
||||||
|
border: 1px solid #28343F;
|
||||||
|
padding:5px;
|
||||||
|
box-sizing:border-box;
|
||||||
|
-webkit-box-sizing:border-box;
|
||||||
|
-moz-box-sizing:border-box;
|
||||||
|
-ms-box-sizing:border-box;
|
||||||
|
-o-box-sizing:border-box;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div#status {
|
||||||
|
clear:both;
|
||||||
|
padding:5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div#pasteresult {
|
||||||
|
background-color:#1F2833;
|
||||||
|
color:white;
|
||||||
|
padding:4px 12px;
|
||||||
|
clear:both;
|
||||||
|
-moz-box-shadow: inset 0px 2px 2px #000;
|
||||||
|
-webkit-box-shadow: inset 0px 2px 2px #000;
|
||||||
|
box-shadow: inset 0px 2px 5px #000;
|
||||||
|
}
|
||||||
|
div#pasteresult a { color:white; }
|
||||||
|
div#pasteresult button { margin-left:11px }
|
||||||
|
div#deletelink { float:right; }
|
||||||
|
div#toolbar, div#status { margin-bottom:5px; }
|
||||||
|
#copyhint { color: #666; font-size:8pt; }
|
||||||
|
|
||||||
|
button,.button,div#expiration {
|
||||||
|
color:#fff;
|
||||||
|
background-color:#323B47;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-position:center left;
|
||||||
|
padding:4px 8px;
|
||||||
|
font-size:1em;
|
||||||
|
margin-right:5px;
|
||||||
|
display:inline;
|
||||||
|
background-image: linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #323B47 0%, #51606E 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #323B47), color-stop(1, #51606E));
|
||||||
|
border: 1px solid #28343F;
|
||||||
|
-moz-box-shadow: inset 0px 1px 2px #647384;
|
||||||
|
-webkit-box-shadow: inset 0px 1px 2px #647384;
|
||||||
|
box-shadow: inset 0px 1px 2px #647384;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
-moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background-image: linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #424B57 0%, #61707E 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #424B57), color-stop(1, #61707E));
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
background-image: linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #51606E 0%, #323B47 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #51606E), color-stop(1, #323B47));
|
||||||
|
position:relative;
|
||||||
|
top:1px;
|
||||||
|
}
|
||||||
|
button:disabled, .buttondisabled {
|
||||||
|
background:#ccc;
|
||||||
|
color:#888;
|
||||||
|
top:0px;
|
||||||
|
}
|
||||||
|
button img {
|
||||||
|
margin-right:8px;
|
||||||
|
position:relative;
|
||||||
|
top:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#expiration, div#rawtextbutton, div#burnafterreadingoption, div#opendisc, div#syntaxcoloringoption {
|
||||||
|
background-color:#414D5A;
|
||||||
|
padding:6px 8px;
|
||||||
|
margin:0px 5px 0px 0px;;
|
||||||
|
position: relative;
|
||||||
|
bottom:1px; /* WTF ? Why is this shifted by 1 pixel ? */
|
||||||
|
}
|
||||||
|
div#expiration select {
|
||||||
|
color:#eee;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div#expiration select option {
|
||||||
|
color:#eee;
|
||||||
|
background: #414D5A;
|
||||||
|
background-color:#414D5A;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#remainingtime {
|
||||||
|
color: #94a3b4;
|
||||||
|
display:inline;
|
||||||
|
font-size:0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.foryoureyesonly {
|
||||||
|
color: yellow !important;
|
||||||
|
font-size: 1em !important;
|
||||||
|
font-weight:bold !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button#newbutton { float:right; margin-right:0px;margin-bottom:5px; display:inline; }
|
||||||
|
input { color:#777; font-size:1em; padding:6px; border: 1px solid #28343F; }
|
||||||
|
|
||||||
|
.nonworking {
|
||||||
|
background-color:#fff;
|
||||||
|
color:#000;
|
||||||
|
width:100%;
|
||||||
|
text-align:center;
|
||||||
|
font-weight:bold;
|
||||||
|
font-size:10pt;
|
||||||
|
-webkit-border-radius:4px;
|
||||||
|
-moz-border-radius:4px;
|
||||||
|
border-radius:4px;
|
||||||
|
padding:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#ienotice {
|
||||||
|
background-color:#7E98AF;
|
||||||
|
color:#000;
|
||||||
|
font-size:0.85em;
|
||||||
|
padding:3px 5px;
|
||||||
|
text-align:center;
|
||||||
|
-webkit-border-radius:4px;
|
||||||
|
-moz-border-radius:4px;
|
||||||
|
border-radius:4px;
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#ienotice a {
|
||||||
|
color:black;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#oldienotice {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorMessage {
|
||||||
|
background-color:#FF7979 !important;
|
||||||
|
color:#FF0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --- discussion related CSS ------- */
|
||||||
|
|
||||||
|
|
||||||
|
div#discussion { /* Discussion container */
|
||||||
|
margin-top:20px;
|
||||||
|
width:100%;
|
||||||
|
margin-left:-30px;
|
||||||
|
min-width:200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size:1.2em;
|
||||||
|
color: #94A3B4;
|
||||||
|
font-style:italic;
|
||||||
|
font-weight:bold;
|
||||||
|
position:relative;
|
||||||
|
margin-left:30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.comment /* One single reply */
|
||||||
|
{
|
||||||
|
background-color:#CECED6;
|
||||||
|
color:#000;
|
||||||
|
white-space:pre-wrap;
|
||||||
|
font-family:Consolas,"Lucida Console","DejaVu Sans Mono",Monaco,monospace;
|
||||||
|
font-size:9pt;
|
||||||
|
border-left: 1px solid #859AAE;
|
||||||
|
border-top: 1px solid #859AAE;
|
||||||
|
padding:5px 0px 5px 5px;
|
||||||
|
margin-left:30px;
|
||||||
|
-moz-box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
|
||||||
|
-webkit-box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
|
||||||
|
box-shadow: -3px -3px 5px rgba(0,0,0,0.15);
|
||||||
|
min-width:200px;
|
||||||
|
overflow:auto;
|
||||||
|
}
|
||||||
|
/* FIXME: Add min-width */
|
||||||
|
|
||||||
|
div.reply {
|
||||||
|
margin: 5px 0px 0px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#replystatus {
|
||||||
|
display:inline;
|
||||||
|
padding:1px 7px;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.comment button {
|
||||||
|
color:#446;
|
||||||
|
background-color:#aab;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-position:center left;
|
||||||
|
padding:0px 2px;
|
||||||
|
font-size:0.73em;
|
||||||
|
margin: 3px 5px 3px 0px;
|
||||||
|
display:inline;
|
||||||
|
background-image: linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #aab 0%, #ccc 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #aab), color-stop(1, #ccc));
|
||||||
|
border: 1px solid #ccd;
|
||||||
|
-moz-box-shadow: inset 0px 1px 2px #ddd;
|
||||||
|
-webkit-box-shadow: inset 0px 1px 2px #fff;
|
||||||
|
box-shadow: inset 0px 1px 2px #eee;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
-moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;
|
||||||
|
}
|
||||||
|
div.comment button:hover {
|
||||||
|
background-image: linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #ccd 0%, #fff 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccd), color-stop(1, #fff));
|
||||||
|
}
|
||||||
|
div.comment button:active {
|
||||||
|
background-image: linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -o-linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -moz-linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -webkit-linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -ms-linear-gradient(bottom, #fff 0%, #889 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(1, #889));
|
||||||
|
position:relative;
|
||||||
|
top:1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.comment input {
|
||||||
|
padding:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea#replymessage {
|
||||||
|
margin-top:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.commentmeta {
|
||||||
|
color: #fff;
|
||||||
|
background-color:#8EA0B2;
|
||||||
|
margin-bottom:3px;
|
||||||
|
padding:0px 0px 0px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.commentdate {
|
||||||
|
color: #BFCEDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.vizhash {
|
||||||
|
width:16px;
|
||||||
|
height:16px;
|
||||||
|
position:relative;
|
||||||
|
top:2px;
|
||||||
|
left:-3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre a {
|
||||||
|
color:#58A5B4;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre a:hover {
|
||||||
|
color:#64B9C6;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
BIN
sources/img/busy.gif
Normal file
BIN
sources/img/busy.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 671 B |
BIN
sources/img/icon_clone.png
Normal file
BIN
sources/img/icon_clone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 391 B |
BIN
sources/img/icon_new.png
Normal file
BIN
sources/img/icon_new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 209 B |
BIN
sources/img/icon_raw.png
Normal file
BIN
sources/img/icon_raw.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
sources/img/icon_send.png
Normal file
BIN
sources/img/icon_send.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 261 B |
412
sources/index.php
Normal file
412
sources/index.php
Normal file
|
@ -0,0 +1,412 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
ZeroBin - a zero-knowledge paste bin
|
||||||
|
Please see project page: http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
|
*/
|
||||||
|
$VERSION='Alpha 0.19';
|
||||||
|
if (version_compare(PHP_VERSION, '5.2.6') < 0) die('ZeroBin requires php 5.2.6 or above to work. Sorry.');
|
||||||
|
require_once "lib/serversalt.php";
|
||||||
|
require_once "lib/vizhash_gd_zero.php";
|
||||||
|
|
||||||
|
// In case stupid admin has left magic_quotes enabled in php.ini:
|
||||||
|
if (get_magic_quotes_gpc())
|
||||||
|
{
|
||||||
|
function stripslashes_deep($value) { $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value); return $value; }
|
||||||
|
$_POST = array_map('stripslashes_deep', $_POST);
|
||||||
|
$_GET = array_map('stripslashes_deep', $_GET);
|
||||||
|
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// trafic_limiter : Make sure the IP address makes at most 1 request every 10 seconds.
|
||||||
|
// Will return false if IP address made a call less than 10 seconds ago.
|
||||||
|
function trafic_limiter_canPass($ip)
|
||||||
|
{
|
||||||
|
$tfilename='./data/trafic_limiter.php';
|
||||||
|
if (!is_file($tfilename))
|
||||||
|
{
|
||||||
|
file_put_contents($tfilename,"<?php\n\$GLOBALS['trafic_limiter']=array();\n?>");
|
||||||
|
chmod($tfilename,0705);
|
||||||
|
}
|
||||||
|
require $tfilename;
|
||||||
|
$tl=$GLOBALS['trafic_limiter'];
|
||||||
|
if (!empty($tl[$ip]) && ($tl[$ip]+10>=time()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
// FIXME: purge file of expired IPs to keep it small
|
||||||
|
}
|
||||||
|
$tl[$ip]=time();
|
||||||
|
file_put_contents($tfilename, "<?php\n\$GLOBALS['trafic_limiter']=".var_export($tl,true).";\n?>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert paste id to storage path.
|
||||||
|
The idea is to creates subdirectories in order to limit the number of files per directory.
|
||||||
|
(A high number of files in a single directory can slow things down.)
|
||||||
|
eg. "f468483c313401e8" will be stored in "data/f4/68/f468483c313401e8"
|
||||||
|
High-trafic websites may want to deepen the directory structure (like Squid does).
|
||||||
|
|
||||||
|
eg. input 'e3570978f9e4aa90' --> output 'data/e3/57/'
|
||||||
|
*/
|
||||||
|
function dataid2path($dataid)
|
||||||
|
{
|
||||||
|
return 'data/'.substr($dataid,0,2).'/'.substr($dataid,2,2).'/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert paste id to discussion storage path.
|
||||||
|
eg. 'e3570978f9e4aa90' --> 'data/e3/57/e3570978f9e4aa90.discussion/'
|
||||||
|
*/
|
||||||
|
function dataid2discussionpath($dataid)
|
||||||
|
{
|
||||||
|
return dataid2path($dataid).$dataid.'.discussion/';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a json string is a proper SJCL encrypted message.
|
||||||
|
// False if format is incorrect.
|
||||||
|
function validSJCL($jsonstring)
|
||||||
|
{
|
||||||
|
$accepted_keys=array('iv','v','iter','ks','ts','mode','adata','cipher','salt','ct');
|
||||||
|
|
||||||
|
// Make sure content is valid json
|
||||||
|
$decoded = json_decode($jsonstring);
|
||||||
|
if ($decoded==null) return false;
|
||||||
|
$decoded = (array)$decoded;
|
||||||
|
|
||||||
|
// Make sure required fields are present
|
||||||
|
foreach($accepted_keys as $k)
|
||||||
|
{
|
||||||
|
if (!array_key_exists($k,$decoded)) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure some fields are base64 data
|
||||||
|
if (base64_decode($decoded['iv'],$strict=true)==null) { return false; }
|
||||||
|
if (base64_decode($decoded['salt'],$strict=true)==null) { return false; }
|
||||||
|
if (base64_decode($decoded['cipher'],$strict=true)==null) { return false; }
|
||||||
|
|
||||||
|
// Make sure no additionnal keys were added.
|
||||||
|
if (count(array_intersect(array_keys($decoded),$accepted_keys))!=10) { return false; }
|
||||||
|
|
||||||
|
// Reject data if entropy is too low
|
||||||
|
$ct = base64_decode($decoded['ct'], $strict=true);
|
||||||
|
if (strlen($ct) > strlen(gzdeflate($ct))) return false;
|
||||||
|
|
||||||
|
// Make sure some fields have a reasonable size.
|
||||||
|
if (strlen($decoded['iv'])>24) return false;
|
||||||
|
if (strlen($decoded['salt'])>14) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a paste and its discussion.
|
||||||
|
// Input: $pasteid : the paste identifier.
|
||||||
|
function deletePaste($pasteid)
|
||||||
|
{
|
||||||
|
// Delete the paste itself
|
||||||
|
unlink(dataid2path($pasteid).$pasteid);
|
||||||
|
|
||||||
|
// Delete discussion if it exists.
|
||||||
|
$discdir = dataid2discussionpath($pasteid);
|
||||||
|
if (is_dir($discdir))
|
||||||
|
{
|
||||||
|
// Delete all files in discussion directory
|
||||||
|
$dhandle = opendir($discdir);
|
||||||
|
while (false !== ($filename = readdir($dhandle)))
|
||||||
|
{
|
||||||
|
if (is_file($discdir.$filename)) unlink($discdir.$filename);
|
||||||
|
}
|
||||||
|
closedir($dhandle);
|
||||||
|
|
||||||
|
// Delete the discussion directory.
|
||||||
|
rmdir($discdir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($_POST['data'])) // Create new paste/comment
|
||||||
|
{
|
||||||
|
/* POST contains:
|
||||||
|
data (mandatory) = json encoded SJCL encrypted text (containing keys: iv,salt,ct)
|
||||||
|
|
||||||
|
All optional data will go to meta information:
|
||||||
|
expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never)
|
||||||
|
opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
|
||||||
|
syntaxcoloring (optional) = should this paste use syntax coloring when displaying.
|
||||||
|
nickname (optional) = son encoded SJCL encrypted text nickname of author of comment (containing keys: iv,salt,ct)
|
||||||
|
parentid (optional) = in discussion, which comment this comment replies to.
|
||||||
|
pasteid (optional) = in discussion, which paste this comment belongs to.
|
||||||
|
*/
|
||||||
|
|
||||||
|
header('Content-type: application/json');
|
||||||
|
$error = false;
|
||||||
|
|
||||||
|
// Create storage directory if it does not exist.
|
||||||
|
if (!is_dir('data'))
|
||||||
|
{
|
||||||
|
mkdir('data',0705);
|
||||||
|
file_put_contents('data/.htaccess',"Allow from none\nDeny from all\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure last paste from the IP address was more than 10 seconds ago.
|
||||||
|
if (!trafic_limiter_canPass($_SERVER['REMOTE_ADDR']))
|
||||||
|
{ echo json_encode(array('status'=>1,'message'=>'Please wait 10 seconds between each post.')); exit; }
|
||||||
|
|
||||||
|
// Make sure content is not too big.
|
||||||
|
$data = $_POST['data'];
|
||||||
|
if (strlen($data)>2000000)
|
||||||
|
{ echo json_encode(array('status'=>1,'message'=>'Paste is limited to 2 Mb of encrypted data.')); exit; }
|
||||||
|
|
||||||
|
// Make sure format is correct.
|
||||||
|
if (!validSJCL($data))
|
||||||
|
{ echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
|
||||||
|
// Read additional meta-information.
|
||||||
|
$meta=array();
|
||||||
|
|
||||||
|
// Read expiration date
|
||||||
|
if (!empty($_POST['expire']))
|
||||||
|
{
|
||||||
|
$expire=$_POST['expire'];
|
||||||
|
if ($expire=='5min') $meta['expire_date']=time()+5*60;
|
||||||
|
elseif ($expire=='10min') $meta['expire_date']=time()+10*60;
|
||||||
|
elseif ($expire=='1hour') $meta['expire_date']=time()+60*60;
|
||||||
|
elseif ($expire=='1day') $meta['expire_date']=time()+24*60*60;
|
||||||
|
elseif ($expire=='1week') $meta['expire_date']=time()+7*24*60*60;
|
||||||
|
elseif ($expire=='1month') $meta['expire_date']=time()+30*24*60*60; // Well this is not *exactly* one month, it's 30 days.
|
||||||
|
elseif ($expire=='1year') $meta['expire_date']=time()+365*24*60*60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the paste when it is read.
|
||||||
|
if (!empty($_POST['burnafterreading']))
|
||||||
|
{
|
||||||
|
$burnafterreading = $_POST['burnafterreading'];
|
||||||
|
if ($burnafterreading!='0' && $burnafterreading!='1') { $error=true; }
|
||||||
|
if ($burnafterreading!='0') { $meta['burnafterreading']=true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read open discussion flag
|
||||||
|
if (!empty($_POST['opendiscussion']))
|
||||||
|
{
|
||||||
|
$opendiscussion = $_POST['opendiscussion'];
|
||||||
|
if ($opendiscussion!='0' && $opendiscussion!='1') { $error=true; }
|
||||||
|
if ($opendiscussion!='0') { $meta['opendiscussion']=true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should we use syntax coloring when displaying ?
|
||||||
|
if (!empty($_POST['syntaxcoloring']))
|
||||||
|
{
|
||||||
|
$syntaxcoloring = $_POST['syntaxcoloring'];
|
||||||
|
if ($syntaxcoloring!='0' && $syntaxcoloring!='1') { $error=true; }
|
||||||
|
if ($syntaxcoloring!='0') { $meta['syntaxcoloring']=true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can't have an open discussion on a "Burn after reading" paste:
|
||||||
|
if (isset($meta['burnafterreading'])) unset($meta['opendiscussion']);
|
||||||
|
|
||||||
|
// Optional nickname for comments
|
||||||
|
if (!empty($_POST['nickname']))
|
||||||
|
{
|
||||||
|
$nick = $_POST['nickname'];
|
||||||
|
if (!validSJCL($nick))
|
||||||
|
{
|
||||||
|
$error=true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$meta['nickname']=$nick;
|
||||||
|
|
||||||
|
// Generation of the anonymous avatar (Vizhash):
|
||||||
|
// If a nickname is provided, we generate a Vizhash.
|
||||||
|
// (We assume that if the user did not enter a nickname, he/she wants
|
||||||
|
// to be anonymous and we will not generate the vizhash.)
|
||||||
|
$vz = new vizhash16x16();
|
||||||
|
$pngdata = $vz->generate($_SERVER['REMOTE_ADDR']);
|
||||||
|
if ($pngdata!='') $meta['vizhash'] = 'data:image/png;base64,'.base64_encode($pngdata);
|
||||||
|
// Once the avatar is generated, we do not keep the IP address, nor its hash.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($error)
|
||||||
|
{
|
||||||
|
echo json_encode(array('status'=>1,'message'=>'Invalid data.'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add post date to meta.
|
||||||
|
$meta['postdate']=time();
|
||||||
|
|
||||||
|
// We just want a small hash to avoid collisions: Half-MD5 (64 bits) will do the trick.
|
||||||
|
$dataid = substr(hash('md5',$data),0,16);
|
||||||
|
|
||||||
|
$is_comment = (!empty($_POST['parentid']) && !empty($_POST['pasteid'])); // Is this post a comment ?
|
||||||
|
$storage = array('data'=>$data);
|
||||||
|
if (count($meta)>0) $storage['meta'] = $meta; // Add meta-information only if necessary.
|
||||||
|
|
||||||
|
if ($is_comment) // The user posts a comment.
|
||||||
|
{
|
||||||
|
$pasteid = $_POST['pasteid'];
|
||||||
|
$parentid = $_POST['parentid'];
|
||||||
|
if (!preg_match('/\A[a-f\d]{16}\z/',$pasteid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
if (!preg_match('/\A[a-f\d]{16}\z/',$parentid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
|
||||||
|
unset($storage['expire_date']); // Comment do not expire (it's the paste that expires)
|
||||||
|
unset($storage['opendiscussion']);
|
||||||
|
unset($storage['syntaxcoloring']);
|
||||||
|
|
||||||
|
// Make sure paste exists.
|
||||||
|
$storagedir = dataid2path($pasteid);
|
||||||
|
if (!is_file($storagedir.$pasteid)) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
|
||||||
|
// Make sure the discussion is opened in this paste.
|
||||||
|
$paste=json_decode(file_get_contents($storagedir.$pasteid));
|
||||||
|
if (!$paste->meta->opendiscussion) { echo json_encode(array('status'=>1,'message'=>'Invalid data.')); exit; }
|
||||||
|
|
||||||
|
$discdir = dataid2discussionpath($pasteid);
|
||||||
|
$filename = $pasteid.'.'.$dataid.'.'.$parentid;
|
||||||
|
if (!is_dir($discdir)) mkdir($discdir,$mode=0705,$recursive=true);
|
||||||
|
if (is_file($discdir.$filename)) // Oups... improbable collision.
|
||||||
|
{
|
||||||
|
echo json_encode(array('status'=>1,'message'=>'You are unlucky. Try again.'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($discdir.$filename,json_encode($storage));
|
||||||
|
echo json_encode(array('status'=>0,'id'=>$dataid)); // 0 = no error
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
else // a standard paste.
|
||||||
|
{
|
||||||
|
$storagedir = dataid2path($dataid);
|
||||||
|
if (!is_dir($storagedir)) mkdir($storagedir,$mode=0705,$recursive=true);
|
||||||
|
if (is_file($storagedir.$dataid)) // Oups... improbable collision.
|
||||||
|
{
|
||||||
|
echo json_encode(array('status'=>1,'message'=>'You are unlucky. Try again.'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
// New paste
|
||||||
|
file_put_contents($storagedir.$dataid,json_encode($storage));
|
||||||
|
|
||||||
|
// Generate the "delete" token.
|
||||||
|
// The token is the hmac of the pasteid signed with the server salt.
|
||||||
|
// The paste can be delete by calling http://myserver.com/zerobin/?pasteid=<pasteid>&deletetoken=<deletetoken>
|
||||||
|
$deletetoken = hash_hmac('sha1', $dataid , getServerSalt());
|
||||||
|
|
||||||
|
echo json_encode(array('status'=>0,'id'=>$dataid,'deletetoken'=>$deletetoken)); // 0 = no error
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(array('status'=>1,'message'=>'Server error.'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process a paste deletion request.
|
||||||
|
Returns an array ('',$ERRORMESSAGE,$STATUS)
|
||||||
|
*/
|
||||||
|
function processPasteDelete($pasteid,$deletetoken)
|
||||||
|
{
|
||||||
|
if (preg_match('/\A[a-f\d]{16}\z/',$pasteid)) // Is this a valid paste identifier ?
|
||||||
|
{
|
||||||
|
$filename = dataid2path($pasteid).$pasteid;
|
||||||
|
if (!is_file($filename)) // Check that paste exists.
|
||||||
|
{
|
||||||
|
return array('','Paste does not exist, has expired or has been deleted.','');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($deletetoken != hash_hmac('sha1', $pasteid , getServerSalt())) // Make sure token is valid.
|
||||||
|
{
|
||||||
|
return array('','Wrong deletion token. Paste was not deleted.','');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paste exists and deletion token is valid: Delete the paste.
|
||||||
|
deletePaste($pasteid);
|
||||||
|
return array('','','Paste was properly deleted.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process a paste fetch request.
|
||||||
|
Returns an array ($CIPHERDATA,$ERRORMESSAGE,$STATUS)
|
||||||
|
*/
|
||||||
|
function processPasteFetch($pasteid)
|
||||||
|
{
|
||||||
|
if (preg_match('/\A[a-f\d]{16}\z/',$pasteid)) // Is this a valid paste identifier ?
|
||||||
|
{
|
||||||
|
$filename = dataid2path($pasteid).$pasteid;
|
||||||
|
if (!is_file($filename)) // Check that paste exists.
|
||||||
|
{
|
||||||
|
return array('','Paste does not exist, has expired or has been deleted.','');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return array('','Invalid data','');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the paste itself.
|
||||||
|
$paste=json_decode(file_get_contents($filename));
|
||||||
|
|
||||||
|
// See if paste has expired.
|
||||||
|
if (isset($paste->meta->expire_date) && $paste->meta->expire_date<time())
|
||||||
|
{
|
||||||
|
deletePaste($pasteid); // Delete the paste
|
||||||
|
return array('','Paste does not exist, has expired or has been deleted.','');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// We kindly provide the remaining time before expiration (in seconds)
|
||||||
|
if (property_exists($paste->meta, 'expire_date')) $paste->meta->remaining_time = $paste->meta->expire_date - time();
|
||||||
|
|
||||||
|
$messages = array($paste); // The paste itself is the first in the list of encrypted messages.
|
||||||
|
// If it's a discussion, get all comments.
|
||||||
|
if (property_exists($paste->meta, 'opendiscussion') && $paste->meta->opendiscussion)
|
||||||
|
{
|
||||||
|
$comments=array();
|
||||||
|
$datadir = dataid2discussionpath($pasteid);
|
||||||
|
if (!is_dir($datadir)) mkdir($datadir,$mode=0705,$recursive=true);
|
||||||
|
$dhandle = opendir($datadir);
|
||||||
|
while (false !== ($filename = readdir($dhandle)))
|
||||||
|
{
|
||||||
|
if (is_file($datadir.$filename))
|
||||||
|
{
|
||||||
|
$comment=json_decode(file_get_contents($datadir.$filename));
|
||||||
|
// Filename is in the form pasteid.commentid.parentid:
|
||||||
|
// - pasteid is the paste this reply belongs to.
|
||||||
|
// - commentid is the comment identifier itself.
|
||||||
|
// - parentid is the comment this comment replies to (It can be pasteid)
|
||||||
|
$items=explode('.',$filename);
|
||||||
|
$comment->meta->commentid=$items[1]; // Add some meta information not contained in file.
|
||||||
|
$comment->meta->parentid=$items[2];
|
||||||
|
$comments[$comment->meta->postdate]=$comment; // Store in table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($dhandle);
|
||||||
|
ksort($comments); // Sort comments by date, oldest first.
|
||||||
|
$messages = array_merge($messages, $comments);
|
||||||
|
}
|
||||||
|
$CIPHERDATA = json_encode($messages);
|
||||||
|
|
||||||
|
// If the paste was meant to be read only once, delete it.
|
||||||
|
if (property_exists($paste->meta, 'burnafterreading') && $paste->meta->burnafterreading) deletePaste($pasteid);
|
||||||
|
|
||||||
|
return array($CIPHERDATA,'','');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$CIPHERDATA='';
|
||||||
|
$ERRORMESSAGE='';
|
||||||
|
$STATUS='';
|
||||||
|
|
||||||
|
if (!empty($_GET['deletetoken']) && !empty($_GET['pasteid'])) // Delete an existing paste
|
||||||
|
{
|
||||||
|
list ($CIPHERDATA, $ERRORMESSAGE, $STATUS) = processPasteDelete($_GET['pasteid'],$_GET['deletetoken']);
|
||||||
|
}
|
||||||
|
else if (!empty($_SERVER['QUERY_STRING'])) // Return an existing paste.
|
||||||
|
{
|
||||||
|
list ($CIPHERDATA, $ERRORMESSAGE, $STATUS) = processPasteFetch($_SERVER['QUERY_STRING']);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once "lib/rain.tpl.class.php";
|
||||||
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
|
$page = new RainTPL;
|
||||||
|
$page->assign('CIPHERDATA',htmlspecialchars($CIPHERDATA,ENT_NOQUOTES)); // We escape it here because ENT_NOQUOTES can't be used in RainTPL templates.
|
||||||
|
$page->assign('VERSION',$VERSION);
|
||||||
|
$page->assign('ERRORMESSAGE',$ERRORMESSAGE);
|
||||||
|
$page->assign('STATUS',$STATUS);
|
||||||
|
$page->draw('page');
|
||||||
|
?>
|
237
sources/js/base64.js
Normal file
237
sources/js/base64.js
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* $Id: base64.js,v 1.7 2012/08/23 10:30:18 dankogai Exp dankogai $
|
||||||
|
*
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*
|
||||||
|
* References:
|
||||||
|
* http://en.wikipedia.org/wiki/Base64
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(global){
|
||||||
|
|
||||||
|
var b64chars
|
||||||
|
= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||||
|
|
||||||
|
var b64charcodes = function(){
|
||||||
|
var a = [];
|
||||||
|
var codeA = 'A'.charCodeAt(0);
|
||||||
|
var codea = 'a'.charCodeAt(0);
|
||||||
|
var code0 = '0'.charCodeAt(0);
|
||||||
|
for (var i = 0; i < 26; i ++) a.push(codeA + i);
|
||||||
|
for (var i = 0; i < 26; i ++) a.push(codea + i);
|
||||||
|
for (var i = 0; i < 10; i ++) a.push(code0 + i);
|
||||||
|
a.push('+'.charCodeAt(0));
|
||||||
|
a.push('/'.charCodeAt(0));
|
||||||
|
return a;
|
||||||
|
}();
|
||||||
|
|
||||||
|
var b64tab = function(bin){
|
||||||
|
var t = {};
|
||||||
|
for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i;
|
||||||
|
return t;
|
||||||
|
}(b64chars);
|
||||||
|
|
||||||
|
var stringToArray = function(s){
|
||||||
|
var a = [];
|
||||||
|
for (var i = 0, l = s.length; i < l; i ++) a[i] = s.charCodeAt(i);
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF8ArrayToBase64 = function(bin){
|
||||||
|
var padlen = 0;
|
||||||
|
while (bin.length % 3){
|
||||||
|
bin.push(0);
|
||||||
|
padlen++;
|
||||||
|
};
|
||||||
|
var b64 = [];
|
||||||
|
for (var i = 0, l = bin.length; i < l; i += 3){
|
||||||
|
var c0 = bin[i], c1 = bin[i+1], c2 = bin[i+2];
|
||||||
|
if (c0 >= 256 || c1 >= 256 || c2 >= 256)
|
||||||
|
throw 'unsupported character found';
|
||||||
|
var n = (c0 << 16) | (c1 << 8) | c2;
|
||||||
|
b64.push(
|
||||||
|
b64charcodes[ n >>> 18],
|
||||||
|
b64charcodes[(n >>> 12) & 63],
|
||||||
|
b64charcodes[(n >>> 6) & 63],
|
||||||
|
b64charcodes[ n & 63]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
while (padlen--) b64[b64.length - padlen - 1] = '='.charCodeAt(0);
|
||||||
|
return chunkStringFromCharCodeApply(b64);
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertBase64ToUTF8Array = function(b64){
|
||||||
|
b64 = b64.replace(/[^A-Za-z0-9+\/]+/g, '');
|
||||||
|
var bin = [];
|
||||||
|
var padlen = b64.length % 4;
|
||||||
|
for (var i = 0, l = b64.length; i < l; i += 4){
|
||||||
|
var n = ((b64tab[b64.charAt(i )] || 0) << 18)
|
||||||
|
| ((b64tab[b64.charAt(i+1)] || 0) << 12)
|
||||||
|
| ((b64tab[b64.charAt(i+2)] || 0) << 6)
|
||||||
|
| ((b64tab[b64.charAt(i+3)] || 0));
|
||||||
|
bin.push(
|
||||||
|
( n >> 16 ),
|
||||||
|
( (n >> 8) & 0xff ),
|
||||||
|
( n & 0xff )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
bin.length -= [0,0,2,1][padlen];
|
||||||
|
return bin;
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF16ArrayToUTF8Array = function(uni){
|
||||||
|
var bin = [];
|
||||||
|
for (var i = 0, l = uni.length; i < l; i++){
|
||||||
|
var n = uni[i];
|
||||||
|
if (n < 0x80)
|
||||||
|
bin.push(n);
|
||||||
|
else if (n < 0x800)
|
||||||
|
bin.push(
|
||||||
|
0xc0 | (n >>> 6),
|
||||||
|
0x80 | (n & 0x3f));
|
||||||
|
else
|
||||||
|
bin.push(
|
||||||
|
0xe0 | ((n >>> 12) & 0x0f),
|
||||||
|
0x80 | ((n >>> 6) & 0x3f),
|
||||||
|
0x80 | (n & 0x3f));
|
||||||
|
}
|
||||||
|
return bin;
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF8ArrayToUTF16Array = function(bin){
|
||||||
|
var uni = [];
|
||||||
|
for (var i = 0, l = bin.length; i < l; i++){
|
||||||
|
var c0 = bin[i];
|
||||||
|
if (c0 < 0x80){
|
||||||
|
uni.push(c0);
|
||||||
|
}else{
|
||||||
|
var c1 = bin[++i];
|
||||||
|
if (c0 < 0xe0){
|
||||||
|
uni.push(((c0 & 0x1f) << 6) | (c1 & 0x3f));
|
||||||
|
}else{
|
||||||
|
var c2 = bin[++i];
|
||||||
|
uni.push(
|
||||||
|
((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uni;
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF8StringToBase64 = function(bin){
|
||||||
|
return convertUTF8ArrayToBase64(stringToArray(bin));
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertBase64ToUTF8String = function(b64){
|
||||||
|
return chunkStringFromCharCodeApply(convertBase64ToUTF8Array(b64));
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF8StringToUTF16Array = function(bin){
|
||||||
|
return convertUTF8ArrayToUTF16Array(stringToArray(bin));
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF8ArrayToUTF16String = function(bin){
|
||||||
|
return chunkStringFromCharCodeApply(convertUTF8ArrayToUTF16Array(bin));
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF8StringToUTF16String = function(bin){
|
||||||
|
return chunkStringFromCharCodeApply(
|
||||||
|
convertUTF8ArrayToUTF16Array(stringToArray(bin))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF16StringToUTF8Array = function(uni){
|
||||||
|
return convertUTF16ArrayToUTF8Array(stringToArray(uni));
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF16ArrayToUTF8String = function(uni){
|
||||||
|
return chunkStringFromCharCodeApply(convertUTF16ArrayToUTF8Array(uni));
|
||||||
|
};
|
||||||
|
|
||||||
|
var convertUTF16StringToUTF8String = function(uni){
|
||||||
|
return chunkStringFromCharCodeApply(
|
||||||
|
convertUTF16ArrayToUTF8Array(stringToArray(uni))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* String.fromCharCode.apply will only handle arrays as big as 65536,
|
||||||
|
* after that it'll return a truncated string with no warning.
|
||||||
|
*/
|
||||||
|
var chunkStringFromCharCodeApply = function(arr){
|
||||||
|
var strs = [], i;
|
||||||
|
for (i = 0; i < arr.length; i += 65536){
|
||||||
|
strs.push(String.fromCharCode.apply(String, arr.slice(i, i+65536)));
|
||||||
|
}
|
||||||
|
return strs.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
if (global.btoa){
|
||||||
|
var btoa = global.btoa;
|
||||||
|
var convertUTF16StringToBase64 = function (uni){
|
||||||
|
return btoa(convertUTF16StringToUTF8String(uni));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var btoa = convertUTF8StringToBase64;
|
||||||
|
var convertUTF16StringToBase64 = function (uni){
|
||||||
|
return convertUTF8ArrayToBase64(convertUTF16StringToUTF8Array(uni));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.atob){
|
||||||
|
var atob = global.atob;
|
||||||
|
var convertBase64ToUTF16String = function (b64){
|
||||||
|
return convertUTF8StringToUTF16String(atob(b64));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var atob = convertBase64ToUTF8String;
|
||||||
|
var convertBase64ToUTF16String = function (b64){
|
||||||
|
return convertUTF8ArrayToUTF16String(convertBase64ToUTF8Array(b64));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Base64 = {
|
||||||
|
convertUTF8ArrayToBase64:convertUTF8ArrayToBase64,
|
||||||
|
convertByteArrayToBase64:convertUTF8ArrayToBase64,
|
||||||
|
convertBase64ToUTF8Array:convertBase64ToUTF8Array,
|
||||||
|
convertBase64ToByteArray:convertBase64ToUTF8Array,
|
||||||
|
convertUTF16ArrayToUTF8Array:convertUTF16ArrayToUTF8Array,
|
||||||
|
convertUTF16ArrayToByteArray:convertUTF16ArrayToUTF8Array,
|
||||||
|
convertUTF8ArrayToUTF16Array:convertUTF8ArrayToUTF16Array,
|
||||||
|
convertByteArrayToUTF16Array:convertUTF8ArrayToUTF16Array,
|
||||||
|
convertUTF8StringToBase64:convertUTF8StringToBase64,
|
||||||
|
convertBase64ToUTF8String:convertBase64ToUTF8String,
|
||||||
|
convertUTF8StringToUTF16Array:convertUTF8StringToUTF16Array,
|
||||||
|
convertUTF8ArrayToUTF16String:convertUTF8ArrayToUTF16String,
|
||||||
|
convertByteArrayToUTF16String:convertUTF8ArrayToUTF16String,
|
||||||
|
convertUTF8StringToUTF16String:convertUTF8StringToUTF16String,
|
||||||
|
convertUTF16StringToUTF8Array:convertUTF16StringToUTF8Array,
|
||||||
|
convertUTF16StringToByteArray:convertUTF16StringToUTF8Array,
|
||||||
|
convertUTF16ArrayToUTF8String:convertUTF16ArrayToUTF8String,
|
||||||
|
convertUTF16StringToUTF8String:convertUTF16StringToUTF8String,
|
||||||
|
convertUTF16StringToBase64:convertUTF16StringToBase64,
|
||||||
|
convertBase64ToUTF16String:convertBase64ToUTF16String,
|
||||||
|
fromBase64:convertBase64ToUTF8String,
|
||||||
|
toBase64:convertUTF8StringToBase64,
|
||||||
|
atob:atob,
|
||||||
|
btoa:btoa,
|
||||||
|
utob:convertUTF16StringToUTF8String,
|
||||||
|
btou:convertUTF8StringToUTF16String,
|
||||||
|
encode:convertUTF16StringToBase64,
|
||||||
|
encodeURI:function(u){
|
||||||
|
return convertUTF16StringToBase64(u).replace(/[+\/]/g, function(m0){
|
||||||
|
return m0 == '+' ? '-' : '_';
|
||||||
|
}).replace(/=+$/, '');
|
||||||
|
},
|
||||||
|
decode:function(a){
|
||||||
|
return convertBase64ToUTF16String(a.replace(/[-_]/g, function(m0){
|
||||||
|
return m0 == '-' ? '+' : '/';
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})(this);
|
1
sources/js/highlight.pack.js
Normal file
1
sources/js/highlight.pack.js
Normal file
File diff suppressed because one or more lines are too long
114
sources/js/highlight.styles/monokai.css
Normal file
114
sources/js/highlight.styles/monokai.css
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
Monokai style - ported by Luigi Maselli - http://grigio.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
display: block; padding: 0.5em;
|
||||||
|
background: #272822;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .tag,
|
||||||
|
pre .tag .title,
|
||||||
|
pre .keyword,
|
||||||
|
pre .literal,
|
||||||
|
pre .change,
|
||||||
|
pre .winutils,
|
||||||
|
pre .flow,
|
||||||
|
pre .lisp .title,
|
||||||
|
pre .clojure .built_in,
|
||||||
|
pre .nginx .title,
|
||||||
|
pre .tex .special {
|
||||||
|
color: #F92672;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
color: #DDD;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code .constant {
|
||||||
|
color: #66D9EF;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .class .title {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .attribute,
|
||||||
|
pre .symbol,
|
||||||
|
pre .symbol .string,
|
||||||
|
pre .value,
|
||||||
|
pre .regexp {
|
||||||
|
color: #BF79DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .tag .value,
|
||||||
|
pre .string,
|
||||||
|
pre .subst,
|
||||||
|
pre .title,
|
||||||
|
pre .haskell .type,
|
||||||
|
pre .preprocessor,
|
||||||
|
pre .ruby .class .parent,
|
||||||
|
pre .built_in,
|
||||||
|
pre .sql .aggregate,
|
||||||
|
pre .django .template_tag,
|
||||||
|
pre .django .variable,
|
||||||
|
pre .smalltalk .class,
|
||||||
|
pre .javadoc,
|
||||||
|
pre .django .filter .argument,
|
||||||
|
pre .smalltalk .localvars,
|
||||||
|
pre .smalltalk .array,
|
||||||
|
pre .attr_selector,
|
||||||
|
pre .pseudo,
|
||||||
|
pre .addition,
|
||||||
|
pre .stream,
|
||||||
|
pre .envvar,
|
||||||
|
pre .apache .tag,
|
||||||
|
pre .apache .cbracket,
|
||||||
|
pre .tex .command,
|
||||||
|
pre .prompt {
|
||||||
|
color: #A6E22E;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .comment,
|
||||||
|
pre .java .annotation,
|
||||||
|
pre .python .decorator,
|
||||||
|
pre .template_comment,
|
||||||
|
pre .pi,
|
||||||
|
pre .doctype,
|
||||||
|
pre .deletion,
|
||||||
|
pre .shebang,
|
||||||
|
pre .apache .sqbracket,
|
||||||
|
pre .tex .formula {
|
||||||
|
color: #75715E;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .keyword,
|
||||||
|
pre .literal,
|
||||||
|
pre .css .id,
|
||||||
|
pre .phpdoc,
|
||||||
|
pre .title,
|
||||||
|
pre .haskell .type,
|
||||||
|
pre .vbscript .built_in,
|
||||||
|
pre .sql .aggregate,
|
||||||
|
pre .rsl .built_in,
|
||||||
|
pre .smalltalk .class,
|
||||||
|
pre .diff .header,
|
||||||
|
pre .chunk,
|
||||||
|
pre .winutils,
|
||||||
|
pre .bash .variable,
|
||||||
|
pre .apache .tag,
|
||||||
|
pre .tex .special,
|
||||||
|
pre .request,
|
||||||
|
pre .status {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .coffeescript .javascript,
|
||||||
|
pre .javascript .xml,
|
||||||
|
pre .tex .formula,
|
||||||
|
pre .xml .javascript,
|
||||||
|
pre .xml .vbscript,
|
||||||
|
pre .xml .css,
|
||||||
|
pre .xml .cdata {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
5
sources/js/jquery.js
vendored
Normal file
5
sources/js/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1671
sources/js/rawdeflate.js
Normal file
1671
sources/js/rawdeflate.js
Normal file
File diff suppressed because it is too large
Load diff
753
sources/js/rawinflate.js
Normal file
753
sources/js/rawinflate.js
Normal file
|
@ -0,0 +1,753 @@
|
||||||
|
/*
|
||||||
|
* $Id: rawinflate.js,v 0.2 2009/03/01 18:32:24 dankogai Exp $
|
||||||
|
*
|
||||||
|
* original:
|
||||||
|
* http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
|
||||||
|
* Version: 1.0.0.1
|
||||||
|
* LastModified: Dec 25 1999
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Interface:
|
||||||
|
* data = zip_inflate(src);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* constant parameters */
|
||||||
|
var zip_WSIZE = 32768; // Sliding Window size
|
||||||
|
var zip_STORED_BLOCK = 0;
|
||||||
|
var zip_STATIC_TREES = 1;
|
||||||
|
var zip_DYN_TREES = 2;
|
||||||
|
|
||||||
|
/* for inflate */
|
||||||
|
var zip_lbits = 9; // bits in base literal/length lookup table
|
||||||
|
var zip_dbits = 6; // bits in base distance lookup table
|
||||||
|
var zip_INBUFSIZ = 32768; // Input buffer size
|
||||||
|
var zip_INBUF_EXTRA = 64; // Extra buffer
|
||||||
|
|
||||||
|
/* variables (inflate) */
|
||||||
|
var zip_slide;
|
||||||
|
var zip_wp; // current position in slide
|
||||||
|
var zip_fixed_tl = null; // inflate static
|
||||||
|
var zip_fixed_td; // inflate static
|
||||||
|
var zip_fixed_bl, fixed_bd; // inflate static
|
||||||
|
var zip_bit_buf; // bit buffer
|
||||||
|
var zip_bit_len; // bits in bit buffer
|
||||||
|
var zip_method;
|
||||||
|
var zip_eof;
|
||||||
|
var zip_copy_leng;
|
||||||
|
var zip_copy_dist;
|
||||||
|
var zip_tl, zip_td; // literal/length and distance decoder tables
|
||||||
|
var zip_bl, zip_bd; // number of bits decoded by tl and td
|
||||||
|
|
||||||
|
var zip_inflate_data;
|
||||||
|
var zip_inflate_pos;
|
||||||
|
|
||||||
|
|
||||||
|
/* constant tables (inflate) */
|
||||||
|
var zip_MASK_BITS = new Array(
|
||||||
|
0x0000,
|
||||||
|
0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
|
||||||
|
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff);
|
||||||
|
// Tables for deflate from PKZIP's appnote.txt.
|
||||||
|
var zip_cplens = new Array( // Copy lengths for literal codes 257..285
|
||||||
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||||||
|
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0);
|
||||||
|
/* note: see note #13 above about the 258 in this list. */
|
||||||
|
var zip_cplext = new Array( // Extra bits for literal codes 257..285
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||||
|
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99); // 99==invalid
|
||||||
|
var zip_cpdist = new Array( // Copy offsets for distance codes 0..29
|
||||||
|
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||||
|
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||||
|
8193, 12289, 16385, 24577);
|
||||||
|
var zip_cpdext = new Array( // Extra bits for distance codes
|
||||||
|
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
||||||
|
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
||||||
|
12, 12, 13, 13);
|
||||||
|
var zip_border = new Array( // Order of the bit length code lengths
|
||||||
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
|
||||||
|
/* objects (inflate) */
|
||||||
|
|
||||||
|
var zip_HuftList = function() {
|
||||||
|
this.next = null;
|
||||||
|
this.list = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_HuftNode = function() {
|
||||||
|
this.e = 0; // number of extra bits or operation
|
||||||
|
this.b = 0; // number of bits in this code or subcode
|
||||||
|
|
||||||
|
// union
|
||||||
|
this.n = 0; // literal, length base, or distance base
|
||||||
|
this.t = null; // (zip_HuftNode) pointer to next level of table
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_HuftBuild = function(b, // code lengths in bits (all assumed <= BMAX)
|
||||||
|
n, // number of codes (assumed <= N_MAX)
|
||||||
|
s, // number of simple-valued codes (0..s-1)
|
||||||
|
d, // list of base values for non-simple codes
|
||||||
|
e, // list of extra bits for non-simple codes
|
||||||
|
mm // maximum lookup bits
|
||||||
|
) {
|
||||||
|
this.BMAX = 16; // maximum bit length of any code
|
||||||
|
this.N_MAX = 288; // maximum number of codes in any set
|
||||||
|
this.status = 0; // 0: success, 1: incomplete table, 2: bad input
|
||||||
|
this.root = null; // (zip_HuftList) starting table
|
||||||
|
this.m = 0; // maximum lookup bits, returns actual
|
||||||
|
|
||||||
|
/* Given a list of code lengths and a maximum table size, make a set of
|
||||||
|
tables to decode that set of codes. Return zero on success, one if
|
||||||
|
the given code set is incomplete (the tables are still built in this
|
||||||
|
case), two if the input is invalid (all zero length codes or an
|
||||||
|
oversubscribed set of lengths), and three if not enough memory.
|
||||||
|
The code with value 256 is special, and the tables are constructed
|
||||||
|
so that no bits beyond that code are fetched when that code is
|
||||||
|
decoded. */
|
||||||
|
{
|
||||||
|
var a; // counter for codes of length k
|
||||||
|
var c = new Array(this.BMAX+1); // bit length count table
|
||||||
|
var el; // length of EOB code (value 256)
|
||||||
|
var f; // i repeats in table every f entries
|
||||||
|
var g; // maximum code length
|
||||||
|
var h; // table level
|
||||||
|
var i; // counter, current code
|
||||||
|
var j; // counter
|
||||||
|
var k; // number of bits in current code
|
||||||
|
var lx = new Array(this.BMAX+1); // stack of bits per table
|
||||||
|
var p; // pointer into c[], b[], or v[]
|
||||||
|
var pidx; // index of p
|
||||||
|
var q; // (zip_HuftNode) points to current table
|
||||||
|
var r = new zip_HuftNode(); // table entry for structure assignment
|
||||||
|
var u = new Array(this.BMAX); // zip_HuftNode[BMAX][] table stack
|
||||||
|
var v = new Array(this.N_MAX); // values in order of bit length
|
||||||
|
var w;
|
||||||
|
var x = new Array(this.BMAX+1);// bit offsets, then code stack
|
||||||
|
var xp; // pointer into x or c
|
||||||
|
var y; // number of dummy codes added
|
||||||
|
var z; // number of entries in current table
|
||||||
|
var o;
|
||||||
|
var tail; // (zip_HuftList)
|
||||||
|
|
||||||
|
tail = this.root = null;
|
||||||
|
for(i = 0; i < c.length; i++)
|
||||||
|
c[i] = 0;
|
||||||
|
for(i = 0; i < lx.length; i++)
|
||||||
|
lx[i] = 0;
|
||||||
|
for(i = 0; i < u.length; i++)
|
||||||
|
u[i] = null;
|
||||||
|
for(i = 0; i < v.length; i++)
|
||||||
|
v[i] = 0;
|
||||||
|
for(i = 0; i < x.length; i++)
|
||||||
|
x[i] = 0;
|
||||||
|
|
||||||
|
// Generate counts for each bit length
|
||||||
|
el = n > 256 ? b[256] : this.BMAX; // set length of EOB code, if any
|
||||||
|
p = b; pidx = 0;
|
||||||
|
i = n;
|
||||||
|
do {
|
||||||
|
c[p[pidx]]++; // assume all entries <= BMAX
|
||||||
|
pidx++;
|
||||||
|
} while(--i > 0);
|
||||||
|
if(c[0] == n) { // null input--all zero length codes
|
||||||
|
this.root = null;
|
||||||
|
this.m = 0;
|
||||||
|
this.status = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find minimum and maximum length, bound *m by those
|
||||||
|
for(j = 1; j <= this.BMAX; j++)
|
||||||
|
if(c[j] != 0)
|
||||||
|
break;
|
||||||
|
k = j; // minimum code length
|
||||||
|
if(mm < j)
|
||||||
|
mm = j;
|
||||||
|
for(i = this.BMAX; i != 0; i--)
|
||||||
|
if(c[i] != 0)
|
||||||
|
break;
|
||||||
|
g = i; // maximum code length
|
||||||
|
if(mm > i)
|
||||||
|
mm = i;
|
||||||
|
|
||||||
|
// Adjust last length count to fill out codes, if needed
|
||||||
|
for(y = 1 << j; j < i; j++, y <<= 1)
|
||||||
|
if((y -= c[j]) < 0) {
|
||||||
|
this.status = 2; // bad input: more codes than bits
|
||||||
|
this.m = mm;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if((y -= c[i]) < 0) {
|
||||||
|
this.status = 2;
|
||||||
|
this.m = mm;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
c[i] += y;
|
||||||
|
|
||||||
|
// Generate starting offsets into the value table for each length
|
||||||
|
x[1] = j = 0;
|
||||||
|
p = c;
|
||||||
|
pidx = 1;
|
||||||
|
xp = 2;
|
||||||
|
while(--i > 0) // note that i == g from above
|
||||||
|
x[xp++] = (j += p[pidx++]);
|
||||||
|
|
||||||
|
// Make a table of values in order of bit lengths
|
||||||
|
p = b; pidx = 0;
|
||||||
|
i = 0;
|
||||||
|
do {
|
||||||
|
if((j = p[pidx++]) != 0)
|
||||||
|
v[x[j]++] = i;
|
||||||
|
} while(++i < n);
|
||||||
|
n = x[g]; // set n to length of v
|
||||||
|
|
||||||
|
// Generate the Huffman codes and for each, make the table entries
|
||||||
|
x[0] = i = 0; // first Huffman code is zero
|
||||||
|
p = v; pidx = 0; // grab values in bit order
|
||||||
|
h = -1; // no tables yet--level -1
|
||||||
|
w = lx[0] = 0; // no bits decoded yet
|
||||||
|
q = null; // ditto
|
||||||
|
z = 0; // ditto
|
||||||
|
|
||||||
|
// go through the bit lengths (k already is bits in shortest code)
|
||||||
|
for(; k <= g; k++) {
|
||||||
|
a = c[k];
|
||||||
|
while(a-- > 0) {
|
||||||
|
// here i is the Huffman code of length k bits for value p[pidx]
|
||||||
|
// make tables up to required level
|
||||||
|
while(k > w + lx[1 + h]) {
|
||||||
|
w += lx[1 + h]; // add bits already decoded
|
||||||
|
h++;
|
||||||
|
|
||||||
|
// compute minimum size table less than or equal to *m bits
|
||||||
|
z = (z = g - w) > mm ? mm : z; // upper limit
|
||||||
|
if((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table
|
||||||
|
// too few codes for k-w bit table
|
||||||
|
f -= a + 1; // deduct codes from patterns left
|
||||||
|
xp = k;
|
||||||
|
while(++j < z) { // try smaller tables up to z bits
|
||||||
|
if((f <<= 1) <= c[++xp])
|
||||||
|
break; // enough codes to use up j bits
|
||||||
|
f -= c[xp]; // else deduct codes from patterns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(w + j > el && w < el)
|
||||||
|
j = el - w; // make EOB code end at table
|
||||||
|
z = 1 << j; // table entries for j-bit table
|
||||||
|
lx[1 + h] = j; // set table size in stack
|
||||||
|
|
||||||
|
// allocate and link in new table
|
||||||
|
q = new Array(z);
|
||||||
|
for(o = 0; o < z; o++) {
|
||||||
|
q[o] = new zip_HuftNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tail == null)
|
||||||
|
tail = this.root = new zip_HuftList();
|
||||||
|
else
|
||||||
|
tail = tail.next = new zip_HuftList();
|
||||||
|
tail.next = null;
|
||||||
|
tail.list = q;
|
||||||
|
u[h] = q; // table starts after link
|
||||||
|
|
||||||
|
/* connect to last table, if there is one */
|
||||||
|
if(h > 0) {
|
||||||
|
x[h] = i; // save pattern for backing up
|
||||||
|
r.b = lx[h]; // bits to dump before this table
|
||||||
|
r.e = 16 + j; // bits in this table
|
||||||
|
r.t = q; // pointer to this table
|
||||||
|
j = (i & ((1 << w) - 1)) >> (w - lx[h]);
|
||||||
|
u[h-1][j].e = r.e;
|
||||||
|
u[h-1][j].b = r.b;
|
||||||
|
u[h-1][j].n = r.n;
|
||||||
|
u[h-1][j].t = r.t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up table entry in r
|
||||||
|
r.b = k - w;
|
||||||
|
if(pidx >= n)
|
||||||
|
r.e = 99; // out of values--invalid code
|
||||||
|
else if(p[pidx] < s) {
|
||||||
|
r.e = (p[pidx] < 256 ? 16 : 15); // 256 is end-of-block code
|
||||||
|
r.n = p[pidx++]; // simple code is just the value
|
||||||
|
} else {
|
||||||
|
r.e = e[p[pidx] - s]; // non-simple--look up in lists
|
||||||
|
r.n = d[p[pidx++] - s];
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill code-like entries with r //
|
||||||
|
f = 1 << (k - w);
|
||||||
|
for(j = i >> w; j < z; j += f) {
|
||||||
|
q[j].e = r.e;
|
||||||
|
q[j].b = r.b;
|
||||||
|
q[j].n = r.n;
|
||||||
|
q[j].t = r.t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// backwards increment the k-bit code i
|
||||||
|
for(j = 1 << (k - 1); (i & j) != 0; j >>= 1)
|
||||||
|
i ^= j;
|
||||||
|
i ^= j;
|
||||||
|
|
||||||
|
// backup over finished tables
|
||||||
|
while((i & ((1 << w) - 1)) != x[h]) {
|
||||||
|
w -= lx[h]; // don't need to update q
|
||||||
|
h--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return actual size of base table */
|
||||||
|
this.m = lx[1];
|
||||||
|
|
||||||
|
/* Return true (1) if we were given an incomplete table */
|
||||||
|
this.status = ((y != 0 && g != 1) ? 1 : 0);
|
||||||
|
} /* end of constructor */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* routines (inflate) */
|
||||||
|
|
||||||
|
var zip_GET_BYTE = function() {
|
||||||
|
if(zip_inflate_data.length == zip_inflate_pos)
|
||||||
|
return -1;
|
||||||
|
return zip_inflate_data.charCodeAt(zip_inflate_pos++) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_NEEDBITS = function(n) {
|
||||||
|
while(zip_bit_len < n) {
|
||||||
|
zip_bit_buf |= zip_GET_BYTE() << zip_bit_len;
|
||||||
|
zip_bit_len += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_GETBITS = function(n) {
|
||||||
|
return zip_bit_buf & zip_MASK_BITS[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_DUMPBITS = function(n) {
|
||||||
|
zip_bit_buf >>= n;
|
||||||
|
zip_bit_len -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_codes = function(buff, off, size) {
|
||||||
|
/* inflate (decompress) the codes in a deflated (compressed) block.
|
||||||
|
Return an error code or zero if it all goes ok. */
|
||||||
|
var e; // table entry flag/number of extra bits
|
||||||
|
var t; // (zip_HuftNode) pointer to table entry
|
||||||
|
var n;
|
||||||
|
|
||||||
|
if(size == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// inflate the coded data
|
||||||
|
n = 0;
|
||||||
|
for(;;) { // do until end of block
|
||||||
|
zip_NEEDBITS(zip_bl);
|
||||||
|
t = zip_tl.list[zip_GETBITS(zip_bl)];
|
||||||
|
e = t.e;
|
||||||
|
while(e > 16) {
|
||||||
|
if(e == 99)
|
||||||
|
return -1;
|
||||||
|
zip_DUMPBITS(t.b);
|
||||||
|
e -= 16;
|
||||||
|
zip_NEEDBITS(e);
|
||||||
|
t = t.t[zip_GETBITS(e)];
|
||||||
|
e = t.e;
|
||||||
|
}
|
||||||
|
zip_DUMPBITS(t.b);
|
||||||
|
|
||||||
|
if(e == 16) { // then it's a literal
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++] = t.n;
|
||||||
|
if(n == size)
|
||||||
|
return size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit if end of block
|
||||||
|
if(e == 15)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// it's an EOB or a length
|
||||||
|
|
||||||
|
// get length of block to copy
|
||||||
|
zip_NEEDBITS(e);
|
||||||
|
zip_copy_leng = t.n + zip_GETBITS(e);
|
||||||
|
zip_DUMPBITS(e);
|
||||||
|
|
||||||
|
// decode distance of block to copy
|
||||||
|
zip_NEEDBITS(zip_bd);
|
||||||
|
t = zip_td.list[zip_GETBITS(zip_bd)];
|
||||||
|
e = t.e;
|
||||||
|
|
||||||
|
while(e > 16) {
|
||||||
|
if(e == 99)
|
||||||
|
return -1;
|
||||||
|
zip_DUMPBITS(t.b);
|
||||||
|
e -= 16;
|
||||||
|
zip_NEEDBITS(e);
|
||||||
|
t = t.t[zip_GETBITS(e)];
|
||||||
|
e = t.e;
|
||||||
|
}
|
||||||
|
zip_DUMPBITS(t.b);
|
||||||
|
zip_NEEDBITS(e);
|
||||||
|
zip_copy_dist = zip_wp - t.n - zip_GETBITS(e);
|
||||||
|
zip_DUMPBITS(e);
|
||||||
|
|
||||||
|
// do the copy
|
||||||
|
while(zip_copy_leng > 0 && n < size) {
|
||||||
|
zip_copy_leng--;
|
||||||
|
zip_copy_dist &= zip_WSIZE - 1;
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++]
|
||||||
|
= zip_slide[zip_copy_dist++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n == size)
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
zip_method = -1; // done
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_stored = function(buff, off, size) {
|
||||||
|
/* "decompress" an inflated type 0 (stored) block. */
|
||||||
|
var n;
|
||||||
|
|
||||||
|
// go to byte boundary
|
||||||
|
n = zip_bit_len & 7;
|
||||||
|
zip_DUMPBITS(n);
|
||||||
|
|
||||||
|
// get the length and its complement
|
||||||
|
zip_NEEDBITS(16);
|
||||||
|
n = zip_GETBITS(16);
|
||||||
|
zip_DUMPBITS(16);
|
||||||
|
zip_NEEDBITS(16);
|
||||||
|
if(n != ((~zip_bit_buf) & 0xffff))
|
||||||
|
return -1; // error in compressed data
|
||||||
|
zip_DUMPBITS(16);
|
||||||
|
|
||||||
|
// read and output the compressed data
|
||||||
|
zip_copy_leng = n;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
while(zip_copy_leng > 0 && n < size) {
|
||||||
|
zip_copy_leng--;
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
zip_NEEDBITS(8);
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++] =
|
||||||
|
zip_GETBITS(8);
|
||||||
|
zip_DUMPBITS(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(zip_copy_leng == 0)
|
||||||
|
zip_method = -1; // done
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_fixed = function(buff, off, size) {
|
||||||
|
/* decompress an inflated type 1 (fixed Huffman codes) block. We should
|
||||||
|
either replace this with a custom decoder, or at least precompute the
|
||||||
|
Huffman tables. */
|
||||||
|
|
||||||
|
// if first time, set up tables for fixed blocks
|
||||||
|
if(zip_fixed_tl == null) {
|
||||||
|
var i; // temporary variable
|
||||||
|
var l = new Array(288); // length list for huft_build
|
||||||
|
var h; // zip_HuftBuild
|
||||||
|
|
||||||
|
// literal table
|
||||||
|
for(i = 0; i < 144; i++)
|
||||||
|
l[i] = 8;
|
||||||
|
for(; i < 256; i++)
|
||||||
|
l[i] = 9;
|
||||||
|
for(; i < 280; i++)
|
||||||
|
l[i] = 7;
|
||||||
|
for(; i < 288; i++) // make a complete, but wrong code set
|
||||||
|
l[i] = 8;
|
||||||
|
zip_fixed_bl = 7;
|
||||||
|
|
||||||
|
h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext,
|
||||||
|
zip_fixed_bl);
|
||||||
|
if(h.status != 0) {
|
||||||
|
alert("HufBuild error: "+h.status);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
zip_fixed_tl = h.root;
|
||||||
|
zip_fixed_bl = h.m;
|
||||||
|
|
||||||
|
// distance table
|
||||||
|
for(i = 0; i < 30; i++) // make an incomplete code set
|
||||||
|
l[i] = 5;
|
||||||
|
zip_fixed_bd = 5;
|
||||||
|
|
||||||
|
h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd);
|
||||||
|
if(h.status > 1) {
|
||||||
|
zip_fixed_tl = null;
|
||||||
|
alert("HufBuild error: "+h.status);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
zip_fixed_td = h.root;
|
||||||
|
zip_fixed_bd = h.m;
|
||||||
|
}
|
||||||
|
|
||||||
|
zip_tl = zip_fixed_tl;
|
||||||
|
zip_td = zip_fixed_td;
|
||||||
|
zip_bl = zip_fixed_bl;
|
||||||
|
zip_bd = zip_fixed_bd;
|
||||||
|
return zip_inflate_codes(buff, off, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_dynamic = function(buff, off, size) {
|
||||||
|
// decompress an inflated type 2 (dynamic Huffman codes) block.
|
||||||
|
var i; // temporary variables
|
||||||
|
var j;
|
||||||
|
var l; // last length
|
||||||
|
var n; // number of lengths to get
|
||||||
|
var t; // (zip_HuftNode) literal/length code table
|
||||||
|
var nb; // number of bit length codes
|
||||||
|
var nl; // number of literal/length codes
|
||||||
|
var nd; // number of distance codes
|
||||||
|
var ll = new Array(286+30); // literal/length and distance code lengths
|
||||||
|
var h; // (zip_HuftBuild)
|
||||||
|
|
||||||
|
for(i = 0; i < ll.length; i++)
|
||||||
|
ll[i] = 0;
|
||||||
|
|
||||||
|
// read in table lengths
|
||||||
|
zip_NEEDBITS(5);
|
||||||
|
nl = 257 + zip_GETBITS(5); // number of literal/length codes
|
||||||
|
zip_DUMPBITS(5);
|
||||||
|
zip_NEEDBITS(5);
|
||||||
|
nd = 1 + zip_GETBITS(5); // number of distance codes
|
||||||
|
zip_DUMPBITS(5);
|
||||||
|
zip_NEEDBITS(4);
|
||||||
|
nb = 4 + zip_GETBITS(4); // number of bit length codes
|
||||||
|
zip_DUMPBITS(4);
|
||||||
|
if(nl > 286 || nd > 30)
|
||||||
|
return -1; // bad lengths
|
||||||
|
|
||||||
|
// read in bit-length-code lengths
|
||||||
|
for(j = 0; j < nb; j++)
|
||||||
|
{
|
||||||
|
zip_NEEDBITS(3);
|
||||||
|
ll[zip_border[j]] = zip_GETBITS(3);
|
||||||
|
zip_DUMPBITS(3);
|
||||||
|
}
|
||||||
|
for(; j < 19; j++)
|
||||||
|
ll[zip_border[j]] = 0;
|
||||||
|
|
||||||
|
// build decoding table for trees--single level, 7 bit lookup
|
||||||
|
zip_bl = 7;
|
||||||
|
h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl);
|
||||||
|
if(h.status != 0)
|
||||||
|
return -1; // incomplete code set
|
||||||
|
|
||||||
|
zip_tl = h.root;
|
||||||
|
zip_bl = h.m;
|
||||||
|
|
||||||
|
// read in literal and distance code lengths
|
||||||
|
n = nl + nd;
|
||||||
|
i = l = 0;
|
||||||
|
while(i < n) {
|
||||||
|
zip_NEEDBITS(zip_bl);
|
||||||
|
t = zip_tl.list[zip_GETBITS(zip_bl)];
|
||||||
|
j = t.b;
|
||||||
|
zip_DUMPBITS(j);
|
||||||
|
j = t.n;
|
||||||
|
if(j < 16) // length of code in bits (0..15)
|
||||||
|
ll[i++] = l = j; // save last length in l
|
||||||
|
else if(j == 16) { // repeat last length 3 to 6 times
|
||||||
|
zip_NEEDBITS(2);
|
||||||
|
j = 3 + zip_GETBITS(2);
|
||||||
|
zip_DUMPBITS(2);
|
||||||
|
if(i + j > n)
|
||||||
|
return -1;
|
||||||
|
while(j-- > 0)
|
||||||
|
ll[i++] = l;
|
||||||
|
} else if(j == 17) { // 3 to 10 zero length codes
|
||||||
|
zip_NEEDBITS(3);
|
||||||
|
j = 3 + zip_GETBITS(3);
|
||||||
|
zip_DUMPBITS(3);
|
||||||
|
if(i + j > n)
|
||||||
|
return -1;
|
||||||
|
while(j-- > 0)
|
||||||
|
ll[i++] = 0;
|
||||||
|
l = 0;
|
||||||
|
} else { // j == 18: 11 to 138 zero length codes
|
||||||
|
zip_NEEDBITS(7);
|
||||||
|
j = 11 + zip_GETBITS(7);
|
||||||
|
zip_DUMPBITS(7);
|
||||||
|
if(i + j > n)
|
||||||
|
return -1;
|
||||||
|
while(j-- > 0)
|
||||||
|
ll[i++] = 0;
|
||||||
|
l = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the decoding tables for literal/length and distance codes
|
||||||
|
zip_bl = zip_lbits;
|
||||||
|
h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl);
|
||||||
|
if(zip_bl == 0) // no literals or lengths
|
||||||
|
h.status = 1;
|
||||||
|
if(h.status != 0) {
|
||||||
|
if(h.status == 1)
|
||||||
|
;// **incomplete literal tree**
|
||||||
|
return -1; // incomplete code set
|
||||||
|
}
|
||||||
|
zip_tl = h.root;
|
||||||
|
zip_bl = h.m;
|
||||||
|
|
||||||
|
for(i = 0; i < nd; i++)
|
||||||
|
ll[i] = ll[i + nl];
|
||||||
|
zip_bd = zip_dbits;
|
||||||
|
h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd);
|
||||||
|
zip_td = h.root;
|
||||||
|
zip_bd = h.m;
|
||||||
|
|
||||||
|
if(zip_bd == 0 && nl > 257) { // lengths but no distances
|
||||||
|
// **incomplete distance tree**
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(h.status == 1) {
|
||||||
|
;// **incomplete distance tree**
|
||||||
|
}
|
||||||
|
if(h.status != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// decompress until an end-of-block code
|
||||||
|
return zip_inflate_codes(buff, off, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_start = function() {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
if(zip_slide == null)
|
||||||
|
zip_slide = new Array(2 * zip_WSIZE);
|
||||||
|
zip_wp = 0;
|
||||||
|
zip_bit_buf = 0;
|
||||||
|
zip_bit_len = 0;
|
||||||
|
zip_method = -1;
|
||||||
|
zip_eof = false;
|
||||||
|
zip_copy_leng = zip_copy_dist = 0;
|
||||||
|
zip_tl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate_internal = function(buff, off, size) {
|
||||||
|
// decompress an inflated entry
|
||||||
|
var n, i;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
while(n < size) {
|
||||||
|
if(zip_eof && zip_method == -1)
|
||||||
|
return n;
|
||||||
|
|
||||||
|
if(zip_copy_leng > 0) {
|
||||||
|
if(zip_method != zip_STORED_BLOCK) {
|
||||||
|
// STATIC_TREES or DYN_TREES
|
||||||
|
while(zip_copy_leng > 0 && n < size) {
|
||||||
|
zip_copy_leng--;
|
||||||
|
zip_copy_dist &= zip_WSIZE - 1;
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++] =
|
||||||
|
zip_slide[zip_copy_dist++];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while(zip_copy_leng > 0 && n < size) {
|
||||||
|
zip_copy_leng--;
|
||||||
|
zip_wp &= zip_WSIZE - 1;
|
||||||
|
zip_NEEDBITS(8);
|
||||||
|
buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8);
|
||||||
|
zip_DUMPBITS(8);
|
||||||
|
}
|
||||||
|
if(zip_copy_leng == 0)
|
||||||
|
zip_method = -1; // done
|
||||||
|
}
|
||||||
|
if(n == size)
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(zip_method == -1) {
|
||||||
|
if(zip_eof)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// read in last block bit
|
||||||
|
zip_NEEDBITS(1);
|
||||||
|
if(zip_GETBITS(1) != 0)
|
||||||
|
zip_eof = true;
|
||||||
|
zip_DUMPBITS(1);
|
||||||
|
|
||||||
|
// read in block type
|
||||||
|
zip_NEEDBITS(2);
|
||||||
|
zip_method = zip_GETBITS(2);
|
||||||
|
zip_DUMPBITS(2);
|
||||||
|
zip_tl = null;
|
||||||
|
zip_copy_leng = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(zip_method) {
|
||||||
|
case 0: // zip_STORED_BLOCK
|
||||||
|
i = zip_inflate_stored(buff, off + n, size - n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // zip_STATIC_TREES
|
||||||
|
if(zip_tl != null)
|
||||||
|
i = zip_inflate_codes(buff, off + n, size - n);
|
||||||
|
else
|
||||||
|
i = zip_inflate_fixed(buff, off + n, size - n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // zip_DYN_TREES
|
||||||
|
if(zip_tl != null)
|
||||||
|
i = zip_inflate_codes(buff, off + n, size - n);
|
||||||
|
else
|
||||||
|
i = zip_inflate_dynamic(buff, off + n, size - n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // error
|
||||||
|
i = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i == -1) {
|
||||||
|
if(zip_eof)
|
||||||
|
return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n += i;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zip_inflate = function(str) {
|
||||||
|
var i, j;
|
||||||
|
|
||||||
|
zip_inflate_start();
|
||||||
|
zip_inflate_data = str;
|
||||||
|
zip_inflate_pos = 0;
|
||||||
|
|
||||||
|
var buff = new Array(1024);
|
||||||
|
var aout = [];
|
||||||
|
while((i = zip_inflate_internal(buff, 0, buff.length)) > 0) {
|
||||||
|
var cbuf = new Array(i);
|
||||||
|
for(j = 0; j < i; j++){
|
||||||
|
cbuf[j] = String.fromCharCode(buff[j]);
|
||||||
|
}
|
||||||
|
aout[aout.length] = cbuf.join("");
|
||||||
|
}
|
||||||
|
zip_inflate_data = null; // G.C.
|
||||||
|
return aout.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! window.RawDeflate) RawDeflate = {};
|
||||||
|
RawDeflate.inflate = zip_inflate;
|
||||||
|
|
||||||
|
})();
|
45
sources/js/sjcl.js
Normal file
45
sources/js/sjcl.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
|
||||||
|
if(typeof module!="undefined"&&module.exports)module.exports=sjcl;
|
||||||
|
sjcl.cipher.aes=function(a){this.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]]^
|
||||||
|
g[3][f[c&255]]}};
|
||||||
|
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=
|
||||||
|
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<j;k++){g=n[d>>>24]^o[e>>16&255]^p[f>>8&255]^q[a&255]^c[l];h=n[e>>>24]^o[f>>16&255]^p[a>>8&255]^q[d&255]^c[l+1];i=n[f>>>24]^o[a>>16&255]^p[d>>8&255]^q[e&255]^c[l+2];a=n[a>>>24]^o[d>>16&
|
||||||
|
255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(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}};
|
||||||
|
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<<c)-1},concat:function(a,b){if(a.length===0||b.length===0)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return d===32?a.concat(b):sjcl.bitArray.Q(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;
|
||||||
|
if(b===0)return 0;return(b-1)*32+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(a.length*32<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;if(c>0&&b)a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1);return a},partial:function(a,b,c){if(a===32)return b;return(c?b|0:b<<32-a)+a*0x10000000000},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return false;var c=0,d;for(d=0;d<a.length;d++)c|=
|
||||||
|
a[d]^b[d];return c===0},Q:function(a,b,c,d){var e;e=0;if(d===undefined)d=[];for(;b>=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a);for(e=0;e<a.length;e++){d.push(c|a[e]>>>b);c=a[e]<<32-b}e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,b+a>32?c:d.pop(),1));return d},j:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
|
||||||
|
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++){if((d&3)===0)e=a[d/4];b+=String.fromCharCode(e>>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++){d=d<<8|a.charCodeAt(c);if((c&3)===3){b.push(d);d=0}}c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
|
||||||
|
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,d*4)}};
|
||||||
|
sjcl.codec.base64={H:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.H,g=0,h=sjcl.bitArray.bitLength(a);if(c)f=f.substr(0,62)+"-_";for(c=0;d.length*6<h;){d+=f.charAt((g^a[c]>>>e)>>>26);if(e<6){g=a[c]<<6-e;e+=26;c++}else{g<<=6;e-=6}}for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d=0,e=sjcl.codec.base64.H,f=0,g;if(b)e=e.substr(0,62)+"-_";for(b=0;b<a.length;b++){g=e.indexOf(a.charAt(b));
|
||||||
|
if(g<0)throw new sjcl.exception.invalid("this isn't base64!");if(d>26){d-=26;c.push(f^g>>>d);f=g<<32-d}else{d+=6;f^=g<<32-d}}d&56&&c.push(sjcl.bitArray.partial(d&56,f,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.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()};
|
||||||
|
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/
|
||||||
|
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^
|
||||||
|
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}};
|
||||||
|
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,
|
||||||
|
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");
|
||||||
|
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;if(c<=65279)g=[h.partial(16,c)];else if(c<=0xffffffff)g=h.concat([h.partial(16,65534)],[c]);g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(i(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(i(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,e*8)},l:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.j;var i=b.length,j=h.bitLength(b);c=h.concat([h.partial(8,
|
||||||
|
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!i)return{tag:d,data:[]};for(g=0;g<i;g+=4){c[3]++;e=a.encrypt(c);b[g]^=e[0];b[g+1]^=e[1];b[g+2]^=e[2];b[g+3]^=e[3]}return{tag:d,data:h.clamp(b,j)}}};
|
||||||
|
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.D,i=sjcl.bitArray,j=i.j,k=[0,0,0,0];c=h(a.encrypt(c));var l,m=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4){l=b.slice(g,g+4);k=j(k,l);m=m.concat(j(c,a.encrypt(j(c,l))));c=h(c)}l=b.slice(g);b=i.bitLength(l);g=a.encrypt(j(c,[0,0,0,b]));l=i.clamp(j(l.concat([0,0,0]),g),b);k=j(k,j(l.concat([0,0,0]),g));k=a.encrypt(j(k,j(c,h(c))));
|
||||||
|
if(d.length)k=j(k,f?d:sjcl.mode.ocb2.pmac(a,d));return m.concat(i.concat(l,i.clamp(k,e)))},decrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");e=e||64;var g=sjcl.mode.ocb2.D,h=sjcl.bitArray,i=h.j,j=[0,0,0,0],k=g(a.encrypt(c)),l,m,n=sjcl.bitArray.bitLength(b)-e,o=[];d=d||[];for(c=0;c+4<n/32;c+=4){l=i(k,a.decrypt(i(k,b.slice(c,c+4))));j=i(j,l);o=o.concat(l);k=g(k)}m=n-c*32;l=a.encrypt(i(k,[0,0,0,m]));l=i(l,h.clamp(b.slice(c),
|
||||||
|
m).concat([0,0,0]));j=i(j,l);j=a.encrypt(i(j,i(k,g(k))));if(d.length)j=i(j,f?d:sjcl.mode.ocb2.pmac(a,d));if(!h.equal(h.clamp(j,e),h.bitSlice(b,n)))throw new sjcl.exception.corrupt("ocb: tag doesn't match");return o.concat(h.clamp(l,m))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.D,e=sjcl.bitArray,f=e.j,g=[0,0,0,0],h=a.encrypt([0,0,0,0]);h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4){h=d(h);g=f(g,a.encrypt(f(h,b.slice(c,c+4))))}b=b.slice(c);if(e.bitLength(b)<128){h=f(h,d(h));b=e.concat(b,[2147483648|0,0,
|
||||||
|
0,0])}g=f(g,b);return a.encrypt(f(d(f(h,d(h))),g))},D:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}};
|
||||||
|
sjcl.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;
|
||||||
|
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;d<e;d+=4){b[0]^=0xffffffff&c[d];b[1]^=0xffffffff&c[d+1];b[2]^=0xffffffff&c[d+2];b[3]^=0xffffffff&c[d+3];b=sjcl.mode.gcm.T(b,a)}return b},l:function(a,b,c,d,e,f){var g,h,i,j,k,l,m,n,o=sjcl.bitArray;l=c.length;m=o.bitLength(c);n=o.bitLength(d);h=o.bitLength(e);
|
||||||
|
g=b.encrypt([0,0,0,0]);if(h===96){e=e.slice(0);e=o.concat(e,[1])}else{e=sjcl.mode.gcm.e(g,[0,0,0,0],e);e=sjcl.mode.gcm.e(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff])}h=sjcl.mode.gcm.e(g,[0,0,0,0],d);k=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.e(g,h,c));for(j=0;j<l;j+=4){k[3]++;i=b.encrypt(k);c[j]^=i[0];c[j+1]^=i[1];c[j+2]^=i[2];c[j+3]^=i[3]}c=o.clamp(c,m);if(a)d=sjcl.mode.gcm.e(g,h,c);a=[Math.floor(n/0x100000000),n&0xffffffff,Math.floor(m/0x100000000),m&0xffffffff];d=sjcl.mode.gcm.e(g,d,a);i=
|
||||||
|
b.encrypt(e);d[0]^=i[0];d[1]^=i[1];d[2]^=i[2];d[3]^=i[3];return{tag:o.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.N=b=b||sjcl.hash.sha256;var c=[[],[]],d=b.prototype.blockSize/32;this.n=[new b,new b];if(a.length>d)a=b.hash(a);for(b=0;b<d;b++){c[0][b]=a[b]^909522486;c[1][b]=a[b]^1549556828}this.n[0].update(c[0]);this.n[1].update(c[1])};sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){a=(new this.N(this.n[0])).update(a).finalize();return(new this.N(this.n[1])).update(a).finalize()};
|
||||||
|
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;if(d<0||c<0)throw sjcl.exception.invalid("invalid params to pbkdf2");if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,i,j=[],k=sjcl.bitArray;for(i=1;32*j.length<(d||1);i++){e=f=a.encrypt(k.concat(b,[i]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}j=j.concat(e)}if(d)j=k.clamp(j,d);return j};
|
||||||
|
sjcl.random={randomWords:function(a,b){var c=[];b=this.isReady(b);var d;if(b===0)throw new sjcl.exception.notReady("generator isn't seeded");else b&2&&this.W(!(b&1));for(b=0;b<a;b+=4){(b+1)%0x10000===0&&this.M();d=this.A();c.push(d[0],d[1],d[2],d[3])}this.M();return c.slice(0,a)},setDefaultParanoia:function(a){this.w=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.s[c],h=this.isReady(),i=0;d=this.I[c];if(d===undefined)d=this.I[c]=this.S++;if(g===undefined)g=this.s[c]=
|
||||||
|
0;this.s[c]=(this.s[c]+1)%this.b.length;switch(typeof a){case "number":if(b===undefined)b=1;this.b[g].update([d,this.z++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if(c==="[object Uint32Array]"){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{if(c!=="[object Array]")i=1;for(c=0;c<a.length&&!i;c++)if(typeof a[c]!="number")i=1}if(!i){if(b===undefined)for(c=b=0;c<a.length;c++)for(e=a[c];e>0;){b++;e>>>=1}this.b[g].update([d,this.z++,2,b,f,a.length].concat(a))}break;case "string":if(b===
|
||||||
|
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=
|
||||||
|
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",
|
||||||
|
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<d.length;b++){c=d[b];delete a[c]}},b:[new sjcl.hash.sha256],m:[0],C:0,s:{},z:0,I:{},S:0,h:0,g:0,P:0,a:[0,0,0,0,0,0,0,0],d:[0,0,0,0],u:undefined,
|
||||||
|
w:6,o:false,t:{progress:{},seeded:{}},R:0,F:[0,48,64,96,128,192,0x100,384,512,768,1024],A:function(){for(var a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}return this.u.encrypt(this.d)},M:function(){this.a=this.A().concat(this.A());this.u=new sjcl.cipher.aes(this.a)},V:function(a){this.a=sjcl.hash.sha256.hash(this.a.concat(a));this.u=new sjcl.cipher.aes(this.a);for(a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}},W:function(a){var b=[],c=0,d;this.P=b[0]=(new Date).valueOf()+3E4;for(d=
|
||||||
|
0;d<16;d++)b.push(Math.random()*0x100000000|0);for(d=0;d<this.b.length;d++){b=b.concat(this.b[d].finalize());c+=this.m[d];this.m[d]=0;if(!a&&this.C&1<<d)break}if(this.C>=1<<this.b.length){this.b.push(new sjcl.hash.sha256);this.m.push(0)}this.g-=c;if(c>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];
|
||||||
|
var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c<d.length;c++)d[c](b)}};try{var s=new Uint32Array(32);crypto.getRandomValues(s);sjcl.random.addEntropy(s,1024,"crypto['getRandomValues']")}catch(t){}
|
||||||
|
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.c({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.c(f,c);c=f.adata;if(typeof f.salt==="string")f.salt=sjcl.codec.base64.toBits(f.salt);if(typeof f.iv==="string")f.iv=sjcl.codec.base64.toBits(f.iv);if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||typeof a==="string"&&f.iter<=100||f.ts!==64&&f.ts!==96&&f.ts!==128||f.ks!==128&&f.ks!==192&&f.ks!==0x100||f.iv.length<
|
||||||
|
2||f.iv.length>4)throw new sjcl.exception.invalid("json encrypt: invalid parameters");if(typeof a==="string"){g=sjcl.misc.cachedPbkdf2(a,f);a=g.key.slice(0,f.ks/32);f.salt=g.salt}if(typeof b==="string")b=sjcl.codec.utf8String.toBits(b);if(typeof c==="string")c=sjcl.codec.utf8String.toBits(c);g=new sjcl.cipher[f.cipher](a);e.c(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(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)),
|
||||||
|
c,true);var f;c=b.adata;if(typeof b.salt==="string")b.salt=sjcl.codec.base64.toBits(b.salt);if(typeof b.iv==="string")b.iv=sjcl.codec.base64.toBits(b.iv);if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||typeof a==="string"&&b.iter<=100||b.ts!==64&&b.ts!==96&&b.ts!==128||b.ks!==128&&b.ks!==192&&b.ks!==0x100||!b.iv||b.iv.length<2||b.iv.length>4)throw new sjcl.exception.invalid("json decrypt: invalid parameters");if(typeof a==="string"){f=sjcl.misc.cachedPbkdf2(a,b);a=f.key.slice(0,b.ks/32);b.salt=f.salt}if(typeof c===
|
||||||
|
"string")c=sjcl.codec.utf8String.toBits(c);f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.c(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+
|
||||||
|
sjcl.codec.base64.fromBits(a[b],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<a.length;c++){if(!(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");b[d[2]]=
|
||||||
|
d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4])}return b},c:function(a,b,c){if(a===undefined)a={};if(b===undefined)return a;var d;for(d in b)if(b.hasOwnProperty(d)){if(c&&a[d]!==undefined&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},Y:function(a,b){var c={},d;for(d in a)if(a.hasOwnProperty(d)&&a[d]!==b[d])c[d]=a[d];return c},X:function(a,b){var c={},d;for(d=0;d<b.length;d++)if(a[b[d]]!==undefined)c[b[d]]=
|
||||||
|
a[b[d]];return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.U={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.U,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===undefined?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
|
609
sources/js/zerobin.js
Normal file
609
sources/js/zerobin.js
Normal file
|
@ -0,0 +1,609 @@
|
||||||
|
/**
|
||||||
|
* ZeroBin 0.19
|
||||||
|
*
|
||||||
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
|
* @author sebsauvage
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Immediately start random number generator collector.
|
||||||
|
sjcl.random.startCollectors();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a duration (in seconds) into human readable format.
|
||||||
|
*
|
||||||
|
* @param int seconds
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function secondsToHuman(seconds)
|
||||||
|
{
|
||||||
|
if (seconds<60) { var v=Math.floor(seconds); return v+' second'+((v>1)?'s':''); }
|
||||||
|
if (seconds<60*60) { var v=Math.floor(seconds/60); return v+' minute'+((v>1)?'s':''); }
|
||||||
|
if (seconds<60*60*24) { var v=Math.floor(seconds/(60*60)); return v+' hour'+((v>1)?'s':''); }
|
||||||
|
// If less than 2 months, display in days:
|
||||||
|
if (seconds<60*60*24*60) { var v=Math.floor(seconds/(60*60*24)); return v+' day'+((v>1)?'s':''); }
|
||||||
|
var v=Math.floor(seconds/(60*60*24*30)); return v+' month'+((v>1)?'s':'');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an associative array to an encoded string
|
||||||
|
* for appending to the anchor.
|
||||||
|
*
|
||||||
|
* @param object associative_array Object to be serialized
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function hashToParameterString(associativeArray)
|
||||||
|
{
|
||||||
|
var parameterString = ""
|
||||||
|
for (key in associativeArray)
|
||||||
|
{
|
||||||
|
if( parameterString === "" )
|
||||||
|
{
|
||||||
|
parameterString = encodeURIComponent(key);
|
||||||
|
parameterString += "=" + encodeURIComponent(associativeArray[key]);
|
||||||
|
} else {
|
||||||
|
parameterString += "&" + encodeURIComponent(key);
|
||||||
|
parameterString += "=" + encodeURIComponent(associativeArray[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//padding for URL shorteners
|
||||||
|
parameterString += "&p=p";
|
||||||
|
|
||||||
|
return parameterString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string to an associative array.
|
||||||
|
*
|
||||||
|
* @param string parameter_string String containing parameters
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
function parameterStringToHash(parameterString)
|
||||||
|
{
|
||||||
|
var parameterHash = {};
|
||||||
|
var parameterArray = parameterString.split("&");
|
||||||
|
for (var i = 0; i < parameterArray.length; i++) {
|
||||||
|
//var currentParamterString = decodeURIComponent(parameterArray[i]);
|
||||||
|
var pair = parameterArray[i].split("=");
|
||||||
|
var key = decodeURIComponent(pair[0]);
|
||||||
|
var value = decodeURIComponent(pair[1]);
|
||||||
|
parameterHash[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameterHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an associative array of the parameters found in the anchor
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
**/
|
||||||
|
function getParameterHash()
|
||||||
|
{
|
||||||
|
var hashIndex = window.location.href.indexOf("#");
|
||||||
|
if (hashIndex >= 0) {
|
||||||
|
return parameterStringToHash(window.location.href.substring(hashIndex + 1));
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a message (deflate compression). Returns base64 encoded data.
|
||||||
|
*
|
||||||
|
* @param string message
|
||||||
|
* @return base64 string data
|
||||||
|
*/
|
||||||
|
function compress(message) {
|
||||||
|
return Base64.toBase64( RawDeflate.deflate( Base64.utob(message) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompress a message compressed with compress().
|
||||||
|
*/
|
||||||
|
function decompress(data) {
|
||||||
|
return Base64.btou( RawDeflate.inflate( Base64.fromBase64(data) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress, then encrypt message with key.
|
||||||
|
*
|
||||||
|
* @param string key
|
||||||
|
* @param string message
|
||||||
|
* @return encrypted string data
|
||||||
|
*/
|
||||||
|
function zeroCipher(key, message) {
|
||||||
|
return sjcl.encrypt(key,compress(message));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Decrypt message with key, then decompress.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param encrypted string data
|
||||||
|
* @return string readable message
|
||||||
|
*/
|
||||||
|
function zeroDecipher(key, data) {
|
||||||
|
return decompress(sjcl.decrypt(key,data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current script location (without search or hash part of the URL).
|
||||||
|
* eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/
|
||||||
|
*/
|
||||||
|
function scriptLocation() {
|
||||||
|
var scriptLocation = window.location.href.substring(0,window.location.href.length
|
||||||
|
- window.location.search.length - window.location.hash.length);
|
||||||
|
var hashIndex = scriptLocation.indexOf("#");
|
||||||
|
if (hashIndex !== -1) {
|
||||||
|
scriptLocation = scriptLocation.substring(0, hashIndex)
|
||||||
|
}
|
||||||
|
return scriptLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the paste unique identifier from the URL
|
||||||
|
* eg. 'c05354954c49a487'
|
||||||
|
*/
|
||||||
|
function pasteID() {
|
||||||
|
return window.location.search.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function htmlEntities(str) {
|
||||||
|
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set text of a DOM element (required for IE)
|
||||||
|
* This is equivalent to element.text(text)
|
||||||
|
* @param object element : a DOM element.
|
||||||
|
* @param string text : the text to enter.
|
||||||
|
*/
|
||||||
|
function setElementText(element, text) {
|
||||||
|
// For IE<10.
|
||||||
|
if ($('div#oldienotice').is(":visible")) {
|
||||||
|
// IE<10 does not support white-space:pre-wrap; so we have to do this BIG UGLY STINKING THING.
|
||||||
|
var html = htmlEntities(text).replace(/\n/ig,"\r\n<br>");
|
||||||
|
element.html('<pre>'+html+'</pre>');
|
||||||
|
}
|
||||||
|
// for other (sane) browsers:
|
||||||
|
else {
|
||||||
|
element.text(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Apply syntax coloring to clear text area.
|
||||||
|
*/
|
||||||
|
function applySyntaxColoring()
|
||||||
|
{
|
||||||
|
if ($('div#cleartext').html().substring(0,11) != '<pre><code>')
|
||||||
|
{
|
||||||
|
// highlight.js expects code to be surrounded by <pre><code>
|
||||||
|
$('div#cleartext').html('<pre><code>'+ $('div#cleartext').html()+'</code></pre>');
|
||||||
|
}
|
||||||
|
hljs.highlightBlock(document.getElementById('cleartext'));
|
||||||
|
$('div#cleartext').css('padding','0'); // Remove white padding around code box.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show decrypted text in the display area, including discussion (if open)
|
||||||
|
*
|
||||||
|
* @param string key : decryption key
|
||||||
|
* @param array comments : Array of messages to display (items = array with keys ('data','meta')
|
||||||
|
*/
|
||||||
|
function displayMessages(key, comments) {
|
||||||
|
try { // Try to decrypt the paste.
|
||||||
|
var cleartext = zeroDecipher(key, comments[0].data);
|
||||||
|
} catch(err) {
|
||||||
|
$('div#cleartext').hide();
|
||||||
|
$('button#clonebutton').hide();
|
||||||
|
showError('Could not decrypt data (Wrong key ?)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setElementText($('div#cleartext'), cleartext);
|
||||||
|
urls2links($('div#cleartext')); // Convert URLs to clickable links.
|
||||||
|
|
||||||
|
// comments[0] is the paste itself.
|
||||||
|
|
||||||
|
if (comments[0].meta.syntaxcoloring) applySyntaxColoring();
|
||||||
|
|
||||||
|
// Display paste expiration.
|
||||||
|
if (comments[0].meta.expire_date) $('div#remainingtime').removeClass('foryoureyesonly').text('This document will expire in '+secondsToHuman(comments[0].meta.remaining_time)+'.').show();
|
||||||
|
if (comments[0].meta.burnafterreading) {
|
||||||
|
$('div#remainingtime').addClass('foryoureyesonly').text('FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.').show();
|
||||||
|
$('button#clonebutton').hide(); // Discourage cloning (as it can't really be prevented).
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the discussion is opened on this paste, display it.
|
||||||
|
if (comments[0].meta.opendiscussion) {
|
||||||
|
$('div#comments').html('');
|
||||||
|
// For each comment.
|
||||||
|
for (var i = 1; i < comments.length; i++) {
|
||||||
|
var comment=comments[i];
|
||||||
|
var cleartext="[Could not decrypt comment ; Wrong key ?]";
|
||||||
|
try {
|
||||||
|
cleartext = zeroDecipher(key, comment.data);
|
||||||
|
} catch(err) { }
|
||||||
|
var place = $('div#comments');
|
||||||
|
// If parent comment exists, display below (CSS will automatically shift it right.)
|
||||||
|
var cname = 'div#comment_'+comment.meta.parentid
|
||||||
|
|
||||||
|
// If the element exists in page
|
||||||
|
if ($(cname).length) {
|
||||||
|
place = $(cname);
|
||||||
|
}
|
||||||
|
var divComment = $('<div class="comment" id="comment_' + comment.meta.commentid+'">'
|
||||||
|
+ '<div class="commentmeta"><span class="nickname"></span><span class="commentdate"></span></div><div class="commentdata"></div>'
|
||||||
|
+ '<button onclick="open_reply($(this),\'' + comment.meta.commentid + '\');return false;">Reply</button>'
|
||||||
|
+ '</div>');
|
||||||
|
setElementText(divComment.find('div.commentdata'), cleartext);
|
||||||
|
// Convert URLs to clickable links in comment.
|
||||||
|
urls2links(divComment.find('div.commentdata'));
|
||||||
|
divComment.find('span.nickname').html('<i>(Anonymous)</i>');
|
||||||
|
|
||||||
|
// Try to get optional nickname:
|
||||||
|
try {
|
||||||
|
divComment.find('span.nickname').text(zeroDecipher(key, comment.meta.nickname));
|
||||||
|
} catch(err) { }
|
||||||
|
divComment.find('span.commentdate').text(' ('+(new Date(comment.meta.postdate*1000).toString())+')').attr('title','CommentID: ' + comment.meta.commentid);
|
||||||
|
|
||||||
|
// If an avatar is available, display it.
|
||||||
|
if (comment.meta.vizhash) {
|
||||||
|
divComment.find('span.nickname').before('<img src="' + comment.meta.vizhash + '" class="vizhash" title="Anonymous avatar (Vizhash of the IP address)" />');
|
||||||
|
}
|
||||||
|
|
||||||
|
place.append(divComment);
|
||||||
|
}
|
||||||
|
$('div#comments').append('<div class="comment"><button onclick="open_reply($(this),\'' + pasteID() + '\');return false;">Add comment</button></div>');
|
||||||
|
$('div#discussion').show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the comment entry when clicking the "Reply" button of a comment.
|
||||||
|
* @param object source : element which emitted the event.
|
||||||
|
* @param string commentid = identifier of the comment we want to reply to.
|
||||||
|
*/
|
||||||
|
function open_reply(source, commentid) {
|
||||||
|
$('div.reply').remove(); // Remove any other reply area.
|
||||||
|
source.after('<div class="reply">'
|
||||||
|
+ '<input type="text" id="nickname" title="Optional nickname..." value="Optional nickname..." />'
|
||||||
|
+ '<textarea id="replymessage" class="replymessage" cols="80" rows="7"></textarea>'
|
||||||
|
+ '<br><button id="replybutton" onclick="send_comment(\'' + commentid + '\');return false;">Post comment</button>'
|
||||||
|
+ '<div id="replystatus"> </div>'
|
||||||
|
+ '</div>');
|
||||||
|
$('input#nickname').focus(function() {
|
||||||
|
$(this).css('color', '#000');
|
||||||
|
if ($(this).val() == $(this).attr('title')) {
|
||||||
|
$(this).val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('textarea#replymessage').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a reply in a discussion.
|
||||||
|
* @param string parentid : the comment identifier we want to send a reply to.
|
||||||
|
*/
|
||||||
|
function send_comment(parentid) {
|
||||||
|
// Do not send if no data.
|
||||||
|
if ($('textarea#replymessage').val().length==0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showStatus('Sending comment...', spin=true);
|
||||||
|
var cipherdata = zeroCipher(pageKey(), $('textarea#replymessage').val());
|
||||||
|
var ciphernickname = '';
|
||||||
|
var nick=$('input#nickname').val();
|
||||||
|
if (nick != '' && nick != 'Optional nickname...') {
|
||||||
|
ciphernickname = zeroCipher(pageKey(), nick);
|
||||||
|
}
|
||||||
|
var data_to_send = { data:cipherdata,
|
||||||
|
parentid: parentid,
|
||||||
|
pasteid: pasteID(),
|
||||||
|
nickname: ciphernickname
|
||||||
|
};
|
||||||
|
|
||||||
|
$.post(scriptLocation(), data_to_send, 'json')
|
||||||
|
.error(function() {
|
||||||
|
showError('Comment could not be sent (serveur error or not responding).');
|
||||||
|
})
|
||||||
|
.success(function(data) {
|
||||||
|
if (data.status == 0) {
|
||||||
|
showStatus('Comment posted.');
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
else if (data.status==1) {
|
||||||
|
showError('Could not post comment: '+data.message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showError('Could not post comment.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a new paste to server
|
||||||
|
*/
|
||||||
|
function send_data() {
|
||||||
|
// Do not send if no data.
|
||||||
|
if ($('textarea#message').val().length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If sjcl has not collected enough entropy yet, display a message.
|
||||||
|
if (!sjcl.random.isReady())
|
||||||
|
{
|
||||||
|
showStatus('Sending paste (Please move your mouse for more entropy)...', spin=true);
|
||||||
|
sjcl.random.addEventListener('seeded', function(){ send_data(); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showStatus('Sending paste...', spin=true);
|
||||||
|
|
||||||
|
var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0);
|
||||||
|
var cipherdata = zeroCipher(randomkey, $('textarea#message').val());
|
||||||
|
var data_to_send = { data: cipherdata,
|
||||||
|
expire: $('select#pasteExpiration').val(),
|
||||||
|
burnafterreading: $('input#burnafterreading').is(':checked') ? 1 : 0,
|
||||||
|
opendiscussion: $('input#opendiscussion').is(':checked') ? 1 : 0,
|
||||||
|
syntaxcoloring: $('input#syntaxcoloring').is(':checked') ? 1 : 0
|
||||||
|
};
|
||||||
|
$.post(scriptLocation(), data_to_send, 'json')
|
||||||
|
.error(function() {
|
||||||
|
showError('Data could not be sent (serveur error or not responding).');
|
||||||
|
})
|
||||||
|
.success(function(data) {
|
||||||
|
if (data.status == 0) {
|
||||||
|
stateExistingPaste();
|
||||||
|
var url = scriptLocation() + "?" + data.id + '#' + randomkey;
|
||||||
|
var deleteUrl = scriptLocation() + "?pasteid=" + data.id + '&deletetoken=' + data.deletetoken;
|
||||||
|
showStatus('');
|
||||||
|
|
||||||
|
$('div#pastelink').html('Your paste is <a id="pasteurl" href="' + url + '">' + url + '</a> <span id="copyhint">(Hit CTRL+C to copy)</span>');
|
||||||
|
$('div#deletelink').html('<a href="' + deleteUrl + '">Delete link</a>');
|
||||||
|
$('div#pasteresult').show();
|
||||||
|
selectText('pasteurl'); // We pre-select the link so that the user only has to CTRL+C the link.
|
||||||
|
|
||||||
|
setElementText($('div#cleartext'), $('textarea#message').val());
|
||||||
|
urls2links($('div#cleartext'));
|
||||||
|
|
||||||
|
// FIXME: Add option to remove syntax highlighting ?
|
||||||
|
if ($('input#syntaxcoloring').is(':checked')) applySyntaxColoring();
|
||||||
|
|
||||||
|
showStatus('');
|
||||||
|
}
|
||||||
|
else if (data.status==1) {
|
||||||
|
showError('Could not create paste: '+data.message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showError('Could not create paste.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Text range selection.
|
||||||
|
* From: http://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse
|
||||||
|
* @param string element : Indentifier of the element to select (id="").
|
||||||
|
*/
|
||||||
|
function selectText(element) {
|
||||||
|
var doc = document
|
||||||
|
, text = doc.getElementById(element)
|
||||||
|
, range, selection
|
||||||
|
;
|
||||||
|
if (doc.body.createTextRange) { //ms
|
||||||
|
range = doc.body.createTextRange();
|
||||||
|
range.moveToElementText(text);
|
||||||
|
range.select();
|
||||||
|
} else if (window.getSelection) { //all others
|
||||||
|
selection = window.getSelection();
|
||||||
|
range = doc.createRange();
|
||||||
|
range.selectNodeContents(text);
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Put the screen in "New paste" mode.
|
||||||
|
*/
|
||||||
|
function stateNewPaste() {
|
||||||
|
$('button#sendbutton').show();
|
||||||
|
$('button#clonebutton').hide();
|
||||||
|
$('button#rawtextbutton').hide();
|
||||||
|
$('div#expiration').show();
|
||||||
|
$('div#remainingtime').hide();
|
||||||
|
$('div#burnafterreadingoption').show();
|
||||||
|
$('div#opendisc').show();
|
||||||
|
$('div#syntaxcoloringoption').show();
|
||||||
|
$('button#newbutton').show();
|
||||||
|
$('div#pasteresult').hide();
|
||||||
|
$('textarea#message').text('');
|
||||||
|
$('textarea#message').show();
|
||||||
|
$('div#cleartext').hide();
|
||||||
|
$('div#message').focus();
|
||||||
|
$('div#discussion').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put the screen in "Existing paste" mode.
|
||||||
|
*/
|
||||||
|
function stateExistingPaste() {
|
||||||
|
$('button#sendbutton').hide();
|
||||||
|
|
||||||
|
// No "clone" for IE<10.
|
||||||
|
if ($('div#oldienotice').is(":visible")) {
|
||||||
|
$('button#clonebutton').hide();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('button#clonebutton').show();
|
||||||
|
}
|
||||||
|
$('button#rawtextbutton').show();
|
||||||
|
|
||||||
|
$('div#expiration').hide();
|
||||||
|
$('div#burnafterreadingoption').hide();
|
||||||
|
$('div#opendisc').hide();
|
||||||
|
$('div#syntaxcoloringoption').hide();
|
||||||
|
$('button#newbutton').show();
|
||||||
|
$('div#pasteresult').hide();
|
||||||
|
$('textarea#message').hide();
|
||||||
|
$('div#cleartext').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return raw text
|
||||||
|
*/
|
||||||
|
function rawText()
|
||||||
|
{
|
||||||
|
history.pushState(document.title, document.title, 'document.txt');
|
||||||
|
var paste = $('div#cleartext').text();
|
||||||
|
var newDoc = document.open('text/plain', 'replace');
|
||||||
|
newDoc.write(paste);
|
||||||
|
newDoc.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone the current paste.
|
||||||
|
*/
|
||||||
|
function clonePaste() {
|
||||||
|
stateNewPaste();
|
||||||
|
|
||||||
|
//Erase the id and the key in url
|
||||||
|
history.replaceState(document.title, document.title, scriptLocation());
|
||||||
|
|
||||||
|
showStatus('');
|
||||||
|
$('textarea#message').text($('div#cleartext').text());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new paste.
|
||||||
|
*/
|
||||||
|
function newPaste() {
|
||||||
|
stateNewPaste();
|
||||||
|
showStatus('');
|
||||||
|
$('textarea#message').text('');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display an error message
|
||||||
|
* (We use the same function for paste and reply to comments)
|
||||||
|
*/
|
||||||
|
function showError(message) {
|
||||||
|
$('div#status').addClass('errorMessage').text(message);
|
||||||
|
$('div#replystatus').addClass('errorMessage').text(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display status
|
||||||
|
* (We use the same function for paste and reply to comments)
|
||||||
|
*
|
||||||
|
* @param string message : text to display
|
||||||
|
* @param boolean spin (optional) : tell if the "spinning" animation should be displayed.
|
||||||
|
*/
|
||||||
|
function showStatus(message, spin) {
|
||||||
|
$('div#replystatus').removeClass('errorMessage');
|
||||||
|
$('div#replystatus').text(message);
|
||||||
|
if (!message) {
|
||||||
|
$('div#status').html(' ');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message == '') {
|
||||||
|
$('div#status').html(' ');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('div#status').removeClass('errorMessage');
|
||||||
|
$('div#status').text(message);
|
||||||
|
if (spin) {
|
||||||
|
var img = '<img src="img/busy.gif" style="width:16px;height:9px;margin:0px 4px 0px 0px;" />';
|
||||||
|
$('div#status').prepend(img);
|
||||||
|
$('div#replystatus').prepend(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert URLs to clickable links.
|
||||||
|
* URLs to handle:
|
||||||
|
* <code>
|
||||||
|
* magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
|
||||||
|
* http://localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
|
||||||
|
* http://user:password@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param object element : a jQuery DOM element.
|
||||||
|
* @FIXME: add ppa & apt links.
|
||||||
|
*/
|
||||||
|
function urls2links(element) {
|
||||||
|
var re = /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig;
|
||||||
|
element.html(element.html().replace(re,'<a href="$1" rel="nofollow">$1</a>'));
|
||||||
|
var re = /((magnet):[\w?=&.\/-;#@~%+-]+)/ig;
|
||||||
|
element.html(element.html().replace(re,'<a href="$1">$1</a>'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the deciphering key stored in anchor part of the URL
|
||||||
|
*/
|
||||||
|
function pageKey() {
|
||||||
|
var key = window.location.hash.substring(1); // Get key
|
||||||
|
|
||||||
|
// Some stupid web 2.0 services and redirectors add data AFTER the anchor
|
||||||
|
// (such as &utm_source=...).
|
||||||
|
// We will strip any additional data.
|
||||||
|
|
||||||
|
// First, strip everything after the equal sign (=) which signals end of base64 string.
|
||||||
|
i = key.indexOf('='); if (i>-1) { key = key.substring(0,i+1); }
|
||||||
|
|
||||||
|
// If the equal sign was not present, some parameters may remain:
|
||||||
|
i = key.indexOf('&'); if (i>-1) { key = key.substring(0,i); }
|
||||||
|
|
||||||
|
// Then add trailing equal sign if it's missing
|
||||||
|
if (key.charAt(key.length-1)!=='=') key+='=';
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
// If "burn after reading" is checked, disable discussion.
|
||||||
|
$('input#burnafterreading').change(function() {
|
||||||
|
if ($(this).is(':checked') ) {
|
||||||
|
$('div#opendisc').addClass('buttondisabled');
|
||||||
|
$('input#opendiscussion').attr({checked: false});
|
||||||
|
$('input#opendiscussion').attr('disabled',true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('div#opendisc').removeClass('buttondisabled');
|
||||||
|
$('input#opendiscussion').removeAttr('disabled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Display status returned by php code if any (eg. Paste was properly deleted.)
|
||||||
|
if ($('div#status').text().length > 0) {
|
||||||
|
showStatus($('div#status').text(),false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('div#status').html(' '); // Keep line height even if content empty.
|
||||||
|
|
||||||
|
// Display an existing paste
|
||||||
|
if ($('div#cipherdata').text().length > 1) {
|
||||||
|
// Missing decryption key in URL ?
|
||||||
|
if (window.location.hash.length == 0) {
|
||||||
|
showError('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL ?)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of messages to display
|
||||||
|
var messages = jQuery.parseJSON($('div#cipherdata').text());
|
||||||
|
|
||||||
|
// Show proper elements on screen.
|
||||||
|
stateExistingPaste();
|
||||||
|
|
||||||
|
displayMessages(pageKey(), messages);
|
||||||
|
}
|
||||||
|
// Display error message from php code.
|
||||||
|
else if ($('div#errormessage').text().length>1) {
|
||||||
|
showError($('div#errormessage').text());
|
||||||
|
}
|
||||||
|
// Create a new paste.
|
||||||
|
else {
|
||||||
|
newPaste();
|
||||||
|
}
|
||||||
|
});
|
1043
sources/lib/rain.tpl.class.php
Normal file
1043
sources/lib/rain.tpl.class.php
Normal file
File diff suppressed because it is too large
Load diff
29
sources/lib/serversalt.php
Normal file
29
sources/lib/serversalt.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Generate a large random hexadecimal salt.
|
||||||
|
function generateRandomSalt()
|
||||||
|
{
|
||||||
|
$randomSalt='';
|
||||||
|
for($i=0;$i<16;$i++) { $randomSalt.=base_convert(mt_rand(),10,16); }
|
||||||
|
return $randomSalt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return this ZeroBin server salt.
|
||||||
|
This is a random string which is unique to each ZeroBin installation.
|
||||||
|
It is automatically created if not present.
|
||||||
|
|
||||||
|
Salt is used:
|
||||||
|
- to generate unique VizHash in discussions (which are not reproductible across ZeroBin servers)
|
||||||
|
- to generate unique deletion token (which are not re-usable across ZeroBin servers)
|
||||||
|
*/
|
||||||
|
function getServerSalt()
|
||||||
|
{
|
||||||
|
$saltfile = 'data/salt.php';
|
||||||
|
if (!is_file($saltfile))
|
||||||
|
file_put_contents($saltfile,'<?php /* |'.generateRandomSalt().'| */ ?>');
|
||||||
|
$items=explode('|',file_get_contents($saltfile));
|
||||||
|
return $items[1];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
151
sources/lib/vizhash_gd_zero.php
Normal file
151
sources/lib/vizhash_gd_zero.php
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
// VizHash_GD 0.0.4 beta ZeroBin 0.19
|
||||||
|
// Visual Hash implementation in php4+GD, stripped down and modified version for ZeroBin
|
||||||
|
// See: http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd
|
||||||
|
// This is free software under the zlib/libpng licence
|
||||||
|
// http://www.opensource.org/licenses/zlib-license.php
|
||||||
|
/* Example:
|
||||||
|
$vz = new vizhash16x16();
|
||||||
|
$data = $vz->generate('hello');
|
||||||
|
header('Content-type: image/png');
|
||||||
|
echo $data;
|
||||||
|
exit;
|
||||||
|
*/
|
||||||
|
require_once "serversalt.php";
|
||||||
|
class vizhash16x16
|
||||||
|
{
|
||||||
|
private $VALUES;
|
||||||
|
private $VALUES_INDEX;
|
||||||
|
private $width;
|
||||||
|
private $height;
|
||||||
|
private $salt;
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->width=16;
|
||||||
|
$this->height=16;
|
||||||
|
$this->salt = getServerSalt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a 16x16 png corresponding to $text.
|
||||||
|
// Input: $text (string)
|
||||||
|
// Output: PNG data. Or empty string if GD is not available.
|
||||||
|
function generate($text)
|
||||||
|
{
|
||||||
|
if (!function_exists('gd_info')) return '';
|
||||||
|
|
||||||
|
// We hash the input string.
|
||||||
|
$hash=hash('sha1',$text.$this->salt).hash('md5',$text.$this->salt);
|
||||||
|
$hash=$hash.strrev($hash); # more data to make graphics
|
||||||
|
|
||||||
|
// We convert the hash into an array of integers.
|
||||||
|
$this->VALUES=array();
|
||||||
|
for($i=0; $i<strlen($hash); $i=$i+2){ array_push($this->VALUES,hexdec(substr($hash,$i,2))); }
|
||||||
|
$this->VALUES_INDEX=0; // to walk the array.
|
||||||
|
|
||||||
|
// Then use these integers to drive the creation of an image.
|
||||||
|
$image = imagecreatetruecolor($this->width,$this->height);
|
||||||
|
|
||||||
|
$r0 = $this->getInt();$r=$r0;
|
||||||
|
$g0 = $this->getInt();$g=$g0;
|
||||||
|
$b0 = $this->getInt();$b=$b0;
|
||||||
|
|
||||||
|
// First, create an image with a specific gradient background.
|
||||||
|
$op='v'; if (($this->getInt()%2)==0) { $op='h'; };
|
||||||
|
$image = $this->degrade($image,$op,array($r0,$g0,$b0),array(0,0,0));
|
||||||
|
|
||||||
|
for($i=0; $i<7; $i=$i+1)
|
||||||
|
{
|
||||||
|
$action=$this->getInt();
|
||||||
|
$color = imagecolorallocate($image, $r,$g,$b);
|
||||||
|
$r = ($r0 + $this->getInt()/25)%256;
|
||||||
|
$g = ($g0 + $this->getInt()/25)%256;
|
||||||
|
$b = ($b0 + $this->getInt()/25)%256;
|
||||||
|
$r0=$r; $g0=$g; $b0=$b;
|
||||||
|
$this->drawshape($image,$action,$color);
|
||||||
|
}
|
||||||
|
|
||||||
|
$color = imagecolorallocate($image,$this->getInt(),$this->getInt(),$this->getInt());
|
||||||
|
$this->drawshape($image,$this->getInt(),$color);
|
||||||
|
ob_start();
|
||||||
|
imagepng($image);
|
||||||
|
$imagedata = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
imagedestroy($image);
|
||||||
|
|
||||||
|
return $imagedata;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a large random hexadecimal salt.
|
||||||
|
private function randomSalt()
|
||||||
|
{
|
||||||
|
$randomSalt='';
|
||||||
|
for($i=0;$i<6;$i++) { $randomSalt.=base_convert(mt_rand(),10,16); }
|
||||||
|
return $randomSalt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function getInt() // Returns a single integer from the $VALUES array (0...255)
|
||||||
|
{
|
||||||
|
$v= $this->VALUES[$this->VALUES_INDEX];
|
||||||
|
$this->VALUES_INDEX++;
|
||||||
|
$this->VALUES_INDEX %= count($this->VALUES); // Warp around the array
|
||||||
|
return $v;
|
||||||
|
}
|
||||||
|
private function getX() // Returns a single integer from the array (roughly mapped to image width)
|
||||||
|
{
|
||||||
|
return $this->width*$this->getInt()/256;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getY() // Returns a single integer from the array (roughly mapped to image height)
|
||||||
|
{
|
||||||
|
return $this->height*$this->getInt()/256;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gradient function taken from:
|
||||||
|
# http://www.supportduweb.com/scripts_tutoriaux-code-source-41-gd-faire-un-degrade-en-php-gd-fonction-degrade-imagerie.html
|
||||||
|
private function degrade($img,$direction,$color1,$color2)
|
||||||
|
{
|
||||||
|
if($direction=='h') { $size = imagesx($img); $sizeinv = imagesy($img); }
|
||||||
|
else { $size = imagesy($img); $sizeinv = imagesx($img);}
|
||||||
|
$diffs = array(
|
||||||
|
(($color2[0]-$color1[0])/$size),
|
||||||
|
(($color2[1]-$color1[1])/$size),
|
||||||
|
(($color2[2]-$color1[2])/$size)
|
||||||
|
);
|
||||||
|
for($i=0;$i<$size;$i++)
|
||||||
|
{
|
||||||
|
$r = $color1[0]+($diffs[0]*$i);
|
||||||
|
$g = $color1[1]+($diffs[1]*$i);
|
||||||
|
$b = $color1[2]+($diffs[2]*$i);
|
||||||
|
if($direction=='h') { imageline($img,$i,0,$i,$sizeinv,imagecolorallocate($img,$r,$g,$b)); }
|
||||||
|
else { imageline($img,0,$i,$sizeinv,$i,imagecolorallocate($img,$r,$g,$b)); }
|
||||||
|
}
|
||||||
|
return $img;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawshape($image,$action,$color)
|
||||||
|
{
|
||||||
|
switch($action%7)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
ImageFilledRectangle ($image,$this->getX(),$this->getY(),$this->getX(),$this->getY(),$color);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
ImageFilledEllipse ($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(), $color);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$points = array($this->getX(), $this->getY(), $this->getX(), $this->getY(), $this->getX(), $this->getY(),$this->getX(), $this->getY());
|
||||||
|
ImageFilledPolygon ($image, $points, 4, $color);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
$start=$this->getInt()*360/256; $end=$start+$this->getInt()*180/256;
|
||||||
|
ImageFilledArc ($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(),$start,$end,$color,IMG_ARC_PIE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
4
sources/robots.txt
Normal file
4
sources/robots.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Might as well keep robots away for performance and privacy reasons.
|
||||||
|
User-agent: *
|
||||||
|
Allow: /index.php
|
||||||
|
Disallow: /
|
88
sources/tpl/page.html
Normal file
88
sources/tpl/page.html
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>ZeroBin</title>
|
||||||
|
<meta name="robots" content="noindex" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="css/zerobin.css?{$VERSION|rawurlencode}#" />
|
||||||
|
<script src="js/jquery.js?{$VERSION|rawurlencode}#"></script>
|
||||||
|
<script src="js/sjcl.js?{$VERSION|rawurlencode}#"></script>
|
||||||
|
<script src="js/base64.js?{$VERSION|rawurlencode}#"></script>
|
||||||
|
<script src="js/rawdeflate.js?{$VERSION|rawurlencode}#"></script>
|
||||||
|
<script src="js/rawinflate.js?{$VERSION|rawurlencode}#"></script>
|
||||||
|
<script src="js/zerobin.js?{$VERSION|rawurlencode}#"></script>
|
||||||
|
<link type="text/css" rel="stylesheet" href="js/highlight.styles/monokai.css?{$VERSION|rawurlencode}#">
|
||||||
|
<script src="js/highlight.pack.js?{$VERSION|rawurlencode}#"></script>
|
||||||
|
|
||||||
|
<!--[if lt IE 10]>
|
||||||
|
<style> body {padding-left:60px;padding-right:60px;} div#ienotice {display:block;} </style>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<!--[if lt IE 10]>
|
||||||
|
<style> div#ienotice {display:block; } div#oldienotice {display:block; } </style>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="aboutbox">
|
||||||
|
ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data.
|
||||||
|
Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES.
|
||||||
|
More information on the <a href="http://sebsauvage.net/wiki/doku.php?id=php:zerobin">project page</a>.<br />
|
||||||
|
<span style="text-decoration:blink;font-size:10pt;color:#a4b3c4;">▶</span> Note: This is a test service:
|
||||||
|
Data may be deleted anytime. Kittens will die if you abuse this service.
|
||||||
|
</div>
|
||||||
|
<h1 title="ZeroBin" onclick="window.location.href=scriptLocation();return false;">ZeroBin</h1><br>
|
||||||
|
<h2>Because ignorance is bliss</h2><br>
|
||||||
|
<h3>{$VERSION}</h3>
|
||||||
|
<noscript><div class="nonworking">Javascript is required for ZeroBin to work.<br>Sorry for the inconvenience.</div></noscript>
|
||||||
|
<div id="oldienotice" class="nonworking">ZeroBin requires a modern browser to work.</div>
|
||||||
|
<div id="ienotice">Still using Internet Explorer ? Do yourself a favor, switch to a modern browser:
|
||||||
|
<a href="http://www.mozilla.org/firefox/">Firefox</a>,
|
||||||
|
<a href="http://www.opera.com/">Opera</a>,
|
||||||
|
<a href="http://www.google.com/chrome">Chrome</a>,
|
||||||
|
<a href="http://www.apple.com/safari">Safari</a>...
|
||||||
|
</div>
|
||||||
|
<div id="status">{$STATUS}</div>
|
||||||
|
<div id="errormessage" style="display:none">{$ERRORMESSAGE|htmlspecialchars}</div>
|
||||||
|
<div id="toolbar">
|
||||||
|
<button id="newbutton" onclick="window.location.href=scriptLocation();return false;" style="display:none;"><img src="img/icon_new.png#" width="11" height="15" />New</button>
|
||||||
|
<button id="sendbutton" onclick="send_data();return false;" style="display:none;"><img src="img/icon_send.png#" width="18" height="15" />Send</button>
|
||||||
|
<button id="clonebutton" onclick="clonePaste();return false;" style="display:none;"><img src="img/icon_clone.png#" width="15" height="17" />Clone</button>
|
||||||
|
<button id="rawtextbutton" onclick="rawText();return false;" style="display:none; "><img src="img/icon_raw.png#" width="15" height="15" style="padding:1px 0px 1px 0px;"/>Raw text</button>
|
||||||
|
<div id="expiration" style="display:none;">Expires:
|
||||||
|
<select id="pasteExpiration" name="pasteExpiration">
|
||||||
|
<option value="5min">5 minutes</option>
|
||||||
|
<option value="10min">10 minutes</option>
|
||||||
|
<option value="1hour">1 hour</option>
|
||||||
|
<option value="1day">1 day</option>
|
||||||
|
<option value="1week">1 week</option>
|
||||||
|
<option value="1month" selected="selected">1 month</option>
|
||||||
|
<option value="1year">1 year</option>
|
||||||
|
<option value="never">Never</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="remainingtime" style="display:none;"></div>
|
||||||
|
<div id="burnafterreadingoption" class="button" style="display:none;">
|
||||||
|
<input type="checkbox" id="burnafterreading" name="burnafterreading" />
|
||||||
|
<label for="burnafterreading">Burn after reading</label>
|
||||||
|
</div>
|
||||||
|
<div id="opendisc" class="button" style="display:none;">
|
||||||
|
<input type="checkbox" id="opendiscussion" name="opendiscussion" />
|
||||||
|
<label for="opendiscussion">Open discussion</label>
|
||||||
|
</div>
|
||||||
|
<div id="syntaxcoloringoption" class="button" style="display:none;">
|
||||||
|
<input type="checkbox" id="syntaxcoloring" name="syntaxcoloring" />
|
||||||
|
<label for="syntaxcoloring">Syntax coloring</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="pasteresult" style="display:none;">
|
||||||
|
<div id="deletelink"></div>
|
||||||
|
<div id="pastelink"></div>
|
||||||
|
</div>
|
||||||
|
<div id="cleartext" style="display:none;"></div>
|
||||||
|
<textarea id="message" name="message" cols="80" rows="25" style="display:none;"></textarea>
|
||||||
|
<div id="discussion" style="display:none;">
|
||||||
|
<h4>Discussion</h4>
|
||||||
|
<div id="comments"></div>
|
||||||
|
</div>
|
||||||
|
<div id="cipherdata" style="display:none;">{$CIPHERDATA}</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue