├── www ├── css │ └── .placeholder ├── favicon.ico ├── img │ ├── init.gif │ ├── loading.gif │ ├── padlock.png │ ├── toolbar.png │ ├── uploading.gif │ ├── decrypting.gif │ ├── downloading.gif │ ├── encrypting.gif │ ├── bitcoin-button.png │ ├── flattr-ezcrypt.png │ ├── how-it-works.png │ └── gradient.svg ├── about.php ├── tpl │ ├── default │ │ ├── nonexistant.tpl │ │ ├── includes │ │ │ ├── footer.tpl │ │ │ ├── theme-select.tpl │ │ │ ├── syntax-select.tpl │ │ │ ├── new.tpl │ │ │ └── header.tpl │ │ ├── paste.tpl │ │ ├── about.tpl │ │ └── index.tpl │ └── mobile │ │ ├── includes │ │ ├── new.tpl │ │ └── header.tpl │ │ └── index.tpl ├── index.php ├── inc │ ├── db.inc.php │ ├── db-pgsql.inc.php │ ├── db-mysql.inc.php │ ├── paste.class.php │ ├── templates.class.php │ ├── controller.class.php │ ├── functions.inc.php │ ├── config.inc.php │ ├── ncrypt.rb │ └── mobile.class.php └── jslibs │ ├── jquery.textchange.min.js │ ├── jquery.textchange.js │ ├── head-1.0.2-1.min.js │ └── head-1.0.2-1.min.js.map ├── resources ├── ncrypt-pgsql.sql ├── ncrypt-mysql.sql ├── rewriterules.txt └── cipher-modes.txt ├── .gitignore ├── source ├── codemirror.css ├── mobile.css ├── cookies.js ├── sjcl-md5 │ └── sjcl-md5.js ├── sjcl.backend.js ├── default.css ├── common.css ├── mimetypes.js └── core.js ├── .gitmodules ├── cron └── prune.php ├── develop ├── codemirror-combine-modes.sh ├── js-preprocess.sh ├── make-css-tpl.sh ├── make-javascripts-tpl.sh ├── css-sha1-versioned.sh └── js-sha1-versioned.sh ├── Makefile └── README.md /www/css/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /www/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/favicon.ico -------------------------------------------------------------------------------- /www/img/init.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/init.gif -------------------------------------------------------------------------------- /www/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/loading.gif -------------------------------------------------------------------------------- /www/img/padlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/padlock.png -------------------------------------------------------------------------------- /www/img/toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/toolbar.png -------------------------------------------------------------------------------- /www/img/uploading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/uploading.gif -------------------------------------------------------------------------------- /www/img/decrypting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/decrypting.gif -------------------------------------------------------------------------------- /www/img/downloading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/downloading.gif -------------------------------------------------------------------------------- /www/img/encrypting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/encrypting.gif -------------------------------------------------------------------------------- /www/img/bitcoin-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/bitcoin-button.png -------------------------------------------------------------------------------- /www/img/flattr-ezcrypt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/flattr-ezcrypt.png -------------------------------------------------------------------------------- /www/img/how-it-works.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luggs-co/ncrypt/HEAD/www/img/how-it-works.png -------------------------------------------------------------------------------- /www/about.php: -------------------------------------------------------------------------------- 1 | about(); 6 | -------------------------------------------------------------------------------- /www/tpl/default/nonexistant.tpl: -------------------------------------------------------------------------------- 1 | incl('includes/header.tpl'); 3 | ?> 4 | Paste does not exist.
5 | incl('includes/footer.tpl'); 7 | -------------------------------------------------------------------------------- /www/tpl/default/includes/footer.tpl: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ncrypt-pgsql.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE pastes ( 3 | id BIGSERIAL PRIMARY KEY, 4 | password character varying(100), 5 | data text NOT NULL, 6 | syntax character varying(64) NOT NULL, 7 | crypto character varying(20) NOT NULL, 8 | added integer NOT NULL, 9 | ttl integer NOT NULL 10 | ); 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.buildpath 3 | /.project 4 | /www/inc/config-local.inc.php 5 | /www/jslibs/codemirror-* 6 | /www/jslibs/main-* 7 | /www/jslibs/sjcl-* 8 | /www/css/*.css 9 | /www/tpl/mobile/includes/css.tpl 10 | /www/tpl/mobile/includes/javascripts.tpl 11 | /www/tpl/default/includes/css.tpl 12 | /www/tpl/default/includes/javascripts.tpl 13 | -------------------------------------------------------------------------------- /source/codemirror.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | background-color: #fff; 3 | border: 1px SOLID #e0e0e0; 4 | padding: 0px; 5 | line-height: 14.5px; 6 | width: 100%; 7 | height: auto; 8 | } 9 | 10 | /* overwrite some codemirror themes using dotted lines */ 11 | .cm-trailingspace { 12 | text-decoration: none !important; 13 | } 14 | .cm-tab { 15 | text-decoration: none !important; 16 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sjcl"] 2 | path = source/sjcl 3 | url = https://github.com/bitwiseshiftleft/sjcl.git 4 | [submodule "Blob.js"] 5 | path = source/Blob.js 6 | url = https://github.com/eligrey/Blob.js.git 7 | [submodule "FileSaver.js"] 8 | path = source/FileSaver.js 9 | url = https://github.com/eligrey/FileSaver.js 10 | [submodule "CodeMirror"] 11 | path = source/CodeMirror 12 | url = https://github.com/codemirror/CodeMirror.git 13 | tag = 5.3.0 14 | -------------------------------------------------------------------------------- /cron/prune.php: -------------------------------------------------------------------------------- 1 | "${tmpdir}/all.js" 20 | 21 | "$(dirname "$(readlink -f "$0")")"/js-sha1-versioned.sh "$1" - < "${tmpdir}/all.js" 22 | -------------------------------------------------------------------------------- /www/img/gradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /develop/js-preprocess.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | preprocess() { 6 | local IFS='' 7 | local line file 8 | while read -r line || [ "${line}" != "" ]; do 9 | case "${line}" in 10 | "#INCLUDE "*) 11 | file="${line#\#INCLUDE }" 12 | printf "%s\n" "/* ${file} */" 13 | preprocess < "${file}" 14 | ;; 15 | *) printf "%s\n" "${line}" 16 | esac 17 | done 18 | } 19 | 20 | tmpdir=$(mktemp --tmpdir -d js-preprocess-XXXXXXX) 21 | trap 'rm -rf "${tmpdir}"' EXIT 22 | 23 | (cd "$(dirname "$1")"; preprocess ) < "$1" > "${tmpdir}/all.js" 24 | 25 | "$(dirname "$(readlink -f "$0")")"/js-sha1-versioned.sh "$2" - < "${tmpdir}/all.js" 26 | -------------------------------------------------------------------------------- /resources/ncrypt-mysql.sql: -------------------------------------------------------------------------------- 1 | -- SQL Dump 2 | 3 | SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; 4 | SET time_zone = "+00:00"; 5 | 6 | -- 7 | -- Database: `ncrypt` 8 | -- 9 | 10 | -- -------------------------------------------------------- 11 | 12 | -- 13 | -- Table structure for table `pastes` 14 | -- 15 | 16 | DROP TABLE IF EXISTS `pastes`; 17 | CREATE TABLE IF NOT EXISTS `pastes` ( 18 | `id` int(100) NOT NULL AUTO_INCREMENT, 19 | `password` varchar(100) DEFAULT NULL, 20 | `data` longblob NOT NULL, 21 | `syntax` varchar(64) NOT NULL DEFAULT 'text/plain', 22 | `crypto` varchar(20) NOT NULL DEFAULT 'AES-256-OFB', 23 | `added` int(15) DEFAULT NULL, 24 | `ttl` int(10) DEFAULT NULL, 25 | PRIMARY KEY (`id`) 26 | ) ENGINE=InnoDB DEFAULT CHARSET=utf32; 27 | -------------------------------------------------------------------------------- /www/tpl/default/includes/theme-select.tpl: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /resources/rewriterules.txt: -------------------------------------------------------------------------------- 1 | ~~~~ Lighttpd 1.4 ~~~~ 2 | 3 | url.rewrite-once = ( 4 | # Static files 5 | "^/css/.*" => "$0", 6 | "^/jslibs/.*" => "$0", 7 | "^/favicon.ico$" => "$0", 8 | 9 | # Everything else: 10 | ".*" => "/index.php$0" 11 | ) 12 | 13 | ~~~~ Lighttpd 2.0 ~~~~ 14 | 15 | docroot "/docroot/"; # alias "/paste" => "/srv/ncrypt/"; 16 | if !physical.is_file { 17 | docroot "/docroot/index.php"; # alias "/paste" => "/srv/ncrypt/index.php"; 18 | pathinfo; 19 | } else { 20 | expire "access 2 weeks"; 21 | } 22 | 23 | # some php handler. has to trigger on phys.path =$ ".php", not req.path! 24 | php; 25 | 26 | ~~~~ nginx ~~~~ 27 | 28 | if (!-e $request_filename) { 29 | rewrite ^.*$ /index.php$0 break; 30 | } 31 | 32 | ~~~~ Apache ~~~~ 33 | 34 | RewriteEngine On 35 | RewriteCond %{REQUEST_FILENAME} !-f 36 | RewriteCond %{REQUEST_FILENAME} !-d 37 | 38 | RewriteRule ^/.*$ /index.php/$0 [QSA,L] 39 | -------------------------------------------------------------------------------- /www/tpl/default/includes/syntax-select.tpl: -------------------------------------------------------------------------------- 1 | Formatting: 2 | 29 | -------------------------------------------------------------------------------- /resources/cipher-modes.txt: -------------------------------------------------------------------------------- 1 | 2 | Unless otherwise mentioned, output ciphertext is base64 encoded, input is either binary or UTF-8 string. 3 | 4 | * AES-128-CBC (alias PIDCRYPT) 5 | compatible with ``openssl enc -aes-128-cbc -a`` 6 | A random 8 byte salt is is prepended to the cipher text as "Salted__" + salt (adding 16 bytes). 7 | The key and IV for AES-128-CBC are derived from salt and password: 8 | (MD5 producing binary output) 9 | material[1] = MD5(password + salt) 10 | material[2] = MD5(material[1] + password + salt) 11 | material[3] = MD5(material[2] + password + salt) 12 | The input is padded with PKCS#5 to blocksize (padding of length n bytes uses "n" as value for all bytes. 01 or 02 02 or 03 03 03 ...). 13 | 14 | The pidCrypt developer didn't understand the padding and appended "\n" (0x0a) to the input before padding. 15 | 16 | * AES-256-OFB (alias CRYPTO_JS) 17 | uses PBKDF2 with the IV (random generated on encryption) as salt, 1 iteration and HMAC-SHA1 as PRF 18 | to generate the 256 bit AES key (and the password ofc). 19 | The IV is prepended to the ciphertext, which is generated by encrypting the plaintext with AES-256-OFB. 20 | -------------------------------------------------------------------------------- /develop/make-css-tpl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | tmpdir=$(mktemp --tmpdir -d make-css-tpl-XXXXXXX) 4 | trap 'rm -rf "${tmpdir}"' EXIT 5 | 6 | findcss2() { 7 | local base="$1" 8 | local ext="$2" 9 | 10 | local f version 11 | 12 | for f in "${base}"-*"${ext}"; do 13 | if [ ! -e "${f}" ]; then continue; fi 14 | 15 | version="${f:${#base}+1:-${#ext}}" 16 | if [ 40 -ne "${#version}" ]; then 17 | # not SHA1 hash. 18 | if [[ ! "${version}" =~ ^[0-9\.\-]+$ ]]; then continue; fi 19 | fi 20 | 21 | printf "%s" "${f}" 22 | return 0 23 | done 24 | 25 | if [ -e "${base}${ext}" ]; then 26 | printf "%s" "${base}${ext}" 27 | return 0 28 | fi 29 | 30 | return 1 31 | } 32 | 33 | findcss() { 34 | findcss2 "$1" ".min.css" || findcss2 "$1" ".css" || ( 35 | echo "Couldn't find stylesheet for '$1'" >&2 36 | exit 10 37 | ) 38 | } 39 | 40 | mode="$1" 41 | 42 | if [ ! -d "www/tpl/${mode}/includes" ]; then 43 | echo "invalid mode: '${mode}'" >&2 44 | exit 1 45 | fi 46 | 47 | ( 48 | cd 'www' 49 | 50 | printf '\n' 53 | ) > "${tmpdir}/css.tpl" 54 | 55 | if ! diff "www/tpl/${mode}/includes/css.tpl" "${tmpdir}/css.tpl" >/dev/null; then 56 | echo Updating "${mode}" css.tpl 57 | cp "${tmpdir}/css.tpl" "www/tpl/${mode}/includes/css.tpl" 58 | fi 59 | -------------------------------------------------------------------------------- /www/index.php: -------------------------------------------------------------------------------- 1 | show( $_GET['id'], $password ); 9 | } 10 | elseif( !empty( $_SERVER['PATH_INFO'] ) ) 11 | { 12 | $parts = array_values( array_filter( explode( '/', $_SERVER['PATH_INFO'] ) ) ); 13 | if( count( $parts ) >= 1 ) 14 | { 15 | switch( $parts[0] ) 16 | { 17 | case 'about': 18 | $controller->about(); 19 | break; 20 | case 'ncrypt': 21 | $controller->ncrypt_script(); 22 | break; 23 | case 'p': // "safe" urls working with all paste ids 24 | $controller->show( count( $parts ) >= 2 ? $parts[1] : '', $password ); 25 | break; 26 | default: // paste ids that are not "special" 27 | $controller->show( $parts[0], $password ); 28 | break; 29 | } 30 | } 31 | elseif( !empty( $_POST ) ) 32 | { 33 | // new post submission 34 | $controller->post( POST('data'), POST('syn'), POST('ttl'), POST('p'), POST('cipher', 'AES-256-OFB') ); 35 | } 36 | else 37 | { 38 | $controller->index(); 39 | } 40 | } 41 | elseif( !empty( $_POST ) ) 42 | { 43 | // new post submission 44 | $controller->post( POST('data'), POST('syn'), POST('ttl'), POST('p'), POST('cipher', 'AES-256-OFB') ); 45 | } 46 | else 47 | { 48 | $controller->index(); 49 | } 50 | -------------------------------------------------------------------------------- /www/inc/db.inc.php: -------------------------------------------------------------------------------- 1 | time() - added 35 | // doesn't need to include data,syntax,crypto 36 | function db_get($id) 37 | { 38 | $db = db_connection(); 39 | 40 | return db_backend_get($db, (int) $id); 41 | } 42 | 43 | // returns actual data: data,syntax,crypto and ttl 44 | function db_read($id) 45 | { 46 | $db = db_connection(); 47 | 48 | return db_backend_read($db, (int) $id); 49 | } 50 | 51 | function db_add($data, $syntax, $ttl, $password, $cipher) 52 | { 53 | $db = db_connection(); 54 | 55 | return db_backend_add($db, $data, $syntax, $ttl, $password, $cipher); 56 | } 57 | 58 | function db_delete($id) 59 | { 60 | $db = db_connection(); 61 | 62 | return db_backend_delete($db, intval($id)); 63 | } 64 | -------------------------------------------------------------------------------- /source/mobile.css: -------------------------------------------------------------------------------- 1 | html, body 2 | { 3 | width: auto; 4 | } 5 | 6 | #header 7 | { 8 | height: 25px; 9 | padding-top: 5px; 10 | } 11 | 12 | nav 13 | { 14 | position: relative; 15 | text-align: center; 16 | padding: 7px 0px; 17 | border-bottom: 1px SOLID #f2f2f2; 18 | margin-bottom: 10px; 19 | } 20 | 21 | nav a 22 | { 23 | background-color: #d7e5f5; 24 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #d7e5f5), color-stop(100%, #cbe0f5)); 25 | background-image: -webkit-linear-gradient(top, #d7e5f5, #cbe0f5); 26 | background-image: -moz-linear-gradient(top, #d7e5f5, #cbe0f5); 27 | background-image: -ms-linear-gradient(top, #d7e5f5, #cbe0f5); 28 | background-image: -o-linear-gradient(top, #d7e5f5, #cbe0f5); 29 | background-image: linear-gradient(top, #d7e5f5, #cbe0f5); 30 | border-top: 1px solid #abbbcc; 31 | border-left: 1px solid #a7b6c7; 32 | border-bottom: 1px solid #a1afbf; 33 | border-right: 1px solid #a7b6c7; 34 | -webkit-border-radius: 3px; 35 | -moz-border-radius: 3px; 36 | border-radius: 3px; 37 | -webkit-box-shadow: inset 0 1px 0 0 white; 38 | -moz-box-shadow: inset 0 1px 0 0 white; 39 | box-shadow: inset 0 1px 0 0 white; 40 | color: #1a3e66; 41 | font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; 42 | line-height: 1; 43 | padding: 3px 10px 4px 10px; 44 | margin-left: 2px; 45 | margin-right: 2px; 46 | text-align: center; 47 | text-shadow: 0 1px 1px #fff; 48 | } 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CRYPTO_BACKENDS=js-sjcl 3 | JAVASCRIPTS=$(CRYPTO_BACKENDS) js-main js-codemirror js-codemirror-modes 4 | STYLESHEETS=css-default css-mobile 5 | 6 | all: $(CRYPTO_BACKENDS) js-tpl $(STYLESHEETS) css-tpl 7 | 8 | .PHONY: all $(JAVASCRIPTS) js-tpl $(STYLESHEETS) css-tpl 9 | 10 | js-sjcl: 11 | ./develop/js-preprocess.sh source/sjcl.backend.js www/jslibs/sjcl 12 | 13 | js-main: 14 | cd source; ../develop/js-sha1-versioned.sh ../www/jslibs/main Blob.js/Blob.js FileSaver.js/FileSaver.js mimetypes.js cookies.js core.js 15 | 16 | js-codemirror-modes: 17 | ./develop/codemirror-combine-modes.sh www/jslibs/codemirror-modes 18 | 19 | js-codemirror: 20 | ./develop/js-sha1-versioned.sh www/jslibs/codemirror - < source/CodeMirror/lib/codemirror.js 21 | ./develop/js-sha1-versioned.sh www/jslibs/codemirror-simple - < source/CodeMirror/addon/mode/simple.js 22 | ./develop/js-sha1-versioned.sh www/jslibs/codemirror-overlay - < source/CodeMirror/addon/mode/overlay.js 23 | 24 | js-tpl: $(JAVASCRIPTS) 25 | ./develop/make-javascripts-tpl.sh 26 | 27 | 28 | css-default: 29 | ./develop/css-sha1-versioned.sh www/css/default source/CodeMirror/lib/codemirror.css source/CodeMirror/theme/*.css source/codemirror.css source/common.css source/default.css 30 | 31 | css-mobile: 32 | ./develop/css-sha1-versioned.sh www/css/mobile source/CodeMirror/lib/codemirror.css source/CodeMirror/theme/*.css source/codemirror.css source/common.css source/mobile.css 33 | 34 | css-tpl: $(STYLESHEETS) 35 | ./develop/make-css-tpl.sh default 36 | ./develop/make-css-tpl.sh mobile 37 | -------------------------------------------------------------------------------- /www/jslibs/jquery.textchange.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | jQuery TextChange Plugin 3 | http://www.zurb.com/playground/jquery-text-change-custom-event 4 | 5 | Copyright 2010, ZURB 6 | Released under the MIT License 7 | */ 8 | (function($){$.event.special.textchange={setup:function(data,namespaces){$(this).data('lastValue',this.contentEditable==='true'?$(this).html():$(this).val());$(this).bind('keyup.textchange',$.event.special.textchange.handler);$(this).bind('cut.textchange paste.textchange input.textchange',$.event.special.textchange.delayedHandler);},teardown:function(namespaces){$(this).unbind('.textchange');},handler:function(event){$.event.special.textchange.triggerIfChanged($(this));},delayedHandler:function(event){var element=$(this);setTimeout(function(){$.event.special.textchange.triggerIfChanged(element);},25);},triggerIfChanged:function(element){var current=element[0].contentEditable==='true'?element.html():element.val();if(current!==element.data('lastValue')){element.trigger('textchange',[element.data('lastValue')]);element.data('lastValue',current);}}};$.event.special.hastext={setup:function(data,namespaces){$(this).bind('textchange',$.event.special.hastext.handler);},teardown:function(namespaces){$(this).unbind('textchange',$.event.special.hastext.handler);},handler:function(event,lastValue){if((lastValue==='')&&lastValue!==$(this).val()){$(this).trigger('hastext');}}};$.event.special.notext={setup:function(data,namespaces){$(this).bind('textchange',$.event.special.notext.handler);},teardown:function(namespaces){$(this).unbind('textchange',$.event.special.notext.handler);},handler:function(event,lastValue){if($(this).val()===''&&$(this).val()!==lastValue){$(this).trigger('notext');}}};})(jQuery); -------------------------------------------------------------------------------- /www/tpl/mobile/includes/new.tpl: -------------------------------------------------------------------------------- 1 |
2 | incl('includes/syntax-select.tpl'); ?> 3 |
Please note, that pasting a large amount of text may cause your mobile to hang while encryption/decryption occurs.
4 |
5 | 6 |
7 | 8 | 9 |
10 |
11 | 12 |
13 | Expire in 14 | 24 |  |  25 |   26 | 27 | 28 | 29 | 30 | 31 |
32 | -------------------------------------------------------------------------------- /www/tpl/mobile/includes/header.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?=$meta_title?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | incl('includes/css.tpl'); ?> 17 | 18 | incl('includes/javascripts.tpl'); ?> 19 | 20 | 21 |
22 | 26 | 31 |
32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NCrypt 2 | 3 | Home of ncrypt. 4 | 5 | This readme will get updated when a more polished version of the code is completed. 6 | 7 | 8 | TODO: https://gist.github.com/luggs-co/1a0dc5f161f18e5861e3 (Comments welcome) 9 | 10 | ## Dependencies 11 | 12 | * Webserver 13 | * PHP 14 | * Database: MySQL (or compatible) or PostgreSQL 15 | 16 | ## Dev Dependencies 17 | 18 | * cssmin 19 | * uglifyjs 20 | * make 21 | 22 | ## Deploy 23 | 24 | www is the "public" base directory for your webserver. 25 | 26 | All requests that don't target a static file should be handled through index.php; the path after 27 | the base url should be given as PATH_INFO (append as path to index.php), see resources/rewriterules.txt 28 | 29 | Configure database access and other customizations in www/inc/config-local.inc.php (you have to create it) 30 | 31 | 2 | incl('includes/syntax-select.tpl'); ?> 3 |
Please note, that pasting a large amount of text may cause your browser to hang while encryption/decryption occurs.
4 |
5 | 6 |
7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 |
15 | Expire in 16 | 26 |  |  27 |   28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | -------------------------------------------------------------------------------- /www/tpl/default/paste.tpl: -------------------------------------------------------------------------------- 1 | incl('includes/header.tpl'); 3 | ?> 4 |
5 | Enter password:  6 |   7 | 8 |
9 |
10 | Enter key to decrypt:  11 |   12 | 13 |
14 | 15 |
16 | incl('includes/new.tpl'); ?> 17 |
18 | 19 |
20 | New 21 | Clone 22 | Save as 23 | Show hex 24 | 25 | id="tool-numbers" class="tool-button" /> 26 | 27 | 28 | id="tool-wrap" class="tool-button" /> 29 | 30 | 31 | id="tool-fullscreen" class="tool-button" /> 32 | 33 | 34 | incl('includes/theme-select.tpl'); ?> 35 |
36 |
37 |
38 |
39 |
40 | 41 |
42 |
43 | 44 | 52 | incl('includes/footer.tpl'); 54 | -------------------------------------------------------------------------------- /www/tpl/default/about.tpl: -------------------------------------------------------------------------------- 1 | incl('includes/header.tpl'); 3 | ?> 4 |

About

5 |
6 | We created because we wanted to give you the power to protect your information from everyone,
7 | including us! In a typical private pastebin, you upload your data and you trust the security of the
8 | server and the integrity of it's operators. With , you get a Javascript file which encrypts your
9 | paste using state of the art AES-256-OFB cipher before it ever leaves your computer!
10 | The link to your paste contains the key to decrypt it which you can share with whomever you want.
11 |
12 | The decryption key never touches our server and that means if something bad were to happen,
13 | all of your pastes would still be safe.
14 |
15 |
16 | How it works:
17 |
18 |
19 |
20 | When you upload a paste, Javascript generates a random key which is used to encrypt your data.
21 | You can see what your paste will look like to our server by holding the mouse over the "Submit" button.
22 |
23 | Q: How can the key be in the link and the server still not see it?
24 |
25 | A: The key is stored in the Anchor Hash, the part after the "#" which is not uploaded to the server but
26 | is available to Javascript on your computer.
27 |
28 | Q: Can this prevent my internet service provider from reading my paste?
29 |
30 | A: Yes, the packets which are sent over the wire are in the scrambled form. However, if an attacker
31 | *modified* the Javascript file while it was being sent to you, they could add a security vulnerability.
32 | These so-called "Man in The Middle" attacks are much less frequent than passive listening and unlike
33 | passive listening, they are detectable.
34 |
35 |
36 | incl('includes/footer.tpl'); 38 | -------------------------------------------------------------------------------- /www/jslibs/jquery.textchange.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery TextChange Plugin 3 | * http://www.zurb.com/playground/jquery-text-change-custom-event 4 | * 5 | * Copyright 2010, ZURB 6 | * Released under the MIT License 7 | */ 8 | (function($) { 9 | 10 | $.event.special.textchange = { 11 | 12 | setup : function(data, namespaces) { 13 | $(this).data( 14 | 'lastValue', 15 | this.contentEditable === 'true' ? $(this).html() : $(this) 16 | .val()); 17 | $(this) 18 | .bind('keyup.textchange', 19 | $.event.special.textchange.handler); 20 | $(this).bind('cut.textchange paste.textchange input.textchange', 21 | $.event.special.textchange.delayedHandler); 22 | }, 23 | 24 | teardown : function(namespaces) { 25 | $(this).unbind('.textchange'); 26 | }, 27 | 28 | handler : function(event) { 29 | $.event.special.textchange.triggerIfChanged($(this)); 30 | }, 31 | 32 | delayedHandler : function(event) { 33 | var element = $(this); 34 | setTimeout(function() { 35 | $.event.special.textchange.triggerIfChanged(element); 36 | }, 25); 37 | }, 38 | 39 | triggerIfChanged : function(element) { 40 | var current = element[0].contentEditable === 'true' ? element 41 | .html() : element.val(); 42 | if (current !== element.data('lastValue')) { 43 | element.trigger('textchange', [ element.data('lastValue') ]); 44 | element.data('lastValue', current); 45 | } 46 | } 47 | }; 48 | 49 | $.event.special.hastext = { 50 | 51 | setup : function(data, namespaces) { 52 | $(this).bind('textchange', $.event.special.hastext.handler); 53 | }, 54 | 55 | teardown : function(namespaces) { 56 | $(this).unbind('textchange', $.event.special.hastext.handler); 57 | }, 58 | 59 | handler : function(event, lastValue) { 60 | if ((lastValue === '') && lastValue !== $(this).val()) { 61 | $(this).trigger('hastext'); 62 | } 63 | } 64 | }; 65 | 66 | $.event.special.notext = { 67 | 68 | setup : function(data, namespaces) { 69 | $(this).bind('textchange', $.event.special.notext.handler); 70 | }, 71 | 72 | teardown : function(namespaces) { 73 | $(this).unbind('textchange', $.event.special.notext.handler); 74 | }, 75 | 76 | handler : function(event, lastValue) { 77 | if ($(this).val() === '' && $(this).val() !== lastValue) { 78 | $(this).trigger('notext'); 79 | } 80 | } 81 | }; 82 | 83 | })(jQuery); -------------------------------------------------------------------------------- /develop/make-javascripts-tpl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | tmpdir=$(mktemp --tmpdir -d make-javascripts-tpl-XXXXXXX) 4 | trap 'rm -rf "${tmpdir}"' EXIT 5 | 6 | findjs2() { 7 | local base="$1" 8 | local ext="$2" 9 | 10 | local f version 11 | 12 | for f in "${base}"-*"${ext}"; do 13 | if [ ! -e "${f}" ]; then continue; fi 14 | 15 | version="${f:${#base}+1:-${#ext}}" 16 | if [ 40 -ne "${#version}" ]; then 17 | # not SHA1 hash. 18 | if [[ ! "${version}" =~ ^[0-9\.\-]+$ ]]; then continue; fi 19 | fi 20 | 21 | printf "%s" "${f}" 22 | return 0 23 | done 24 | 25 | if [ -e "${base}${ext}" ]; then 26 | printf "%s" "${base}${ext}" 27 | return 0 28 | fi 29 | 30 | return 1 31 | } 32 | 33 | findjs() { 34 | findjs2 "$1" ".min.js" || findjs2 "$1" ".js" || ( 35 | echo "Couldn't find javascript for '$1'" >&2 36 | exit 10 37 | ) 38 | } 39 | 40 | ( 41 | cd 'www' 42 | 43 | printf '\n' 46 | printf '\t\t\n' 87 | ) > "${tmpdir}/javascripts.tpl" 88 | 89 | if ! diff 'www/tpl/default/includes/javascripts.tpl' "${tmpdir}/javascripts.tpl" >/dev/null; then 90 | echo Updating javascripts.tpl 91 | cp "${tmpdir}/javascripts.tpl" 'www/tpl/default/includes/javascripts.tpl' 92 | fi 93 | -------------------------------------------------------------------------------- /www/inc/db-pgsql.inc.php: -------------------------------------------------------------------------------- 1 | ttl 28 | '; 29 | 30 | $res = pg_query($db, $sql) or die("Failed to execute query '$sql'"); 31 | 32 | return pg_affected_rows($res); 33 | } 34 | 35 | function db_backend_get($db, $id) 36 | { 37 | // grab paste from database 38 | $sql = ' 39 | SELECT 40 | "id", "added", "ttl", "password", (EXTRACT(EPOCH FROM now()) - added ) as age 41 | FROM 42 | pastes 43 | WHERE 44 | id = $1 45 | LIMIT 46 | 1 47 | '; 48 | 49 | $res = pg_query_params($db, $sql, array($id)) or die("Failed to execute query '$sql'"); 50 | 51 | return pg_fetch_assoc($res); 52 | } 53 | 54 | function db_backend_read($db, $id) 55 | { 56 | // grab paste from database 57 | $sql = ' 58 | SELECT 59 | "data", "syntax", "ttl", "crypto" 60 | FROM 61 | pastes 62 | WHERE 63 | id = $1 64 | LIMIT 65 | 1 66 | '; 67 | 68 | $res = pg_query_params($db, $sql, array($id)) or die("Failed to execute query '$sql'"); 69 | 70 | return pg_fetch_assoc($res); 71 | } 72 | 73 | function db_backend_add($db, $data, $syntax, $ttl, $password, $cipher) 74 | { 75 | $sql = ' 76 | INSERT INTO 77 | pastes 78 | ( "data", "syntax", "added", "ttl", "password", "crypto" ) 79 | VALUES 80 | ( $1, $2, EXTRACT(EPOCH FROM now()), $3, $4, $5 ) 81 | RETURNING id 82 | '; 83 | 84 | $res = pg_query_params($db, $sql, array($data, $syntax, $ttl, $password, $cipher)) or die("Failed to execute query '$sql'"); 85 | 86 | $row = pg_fetch_row($res); 87 | return $row[0]; 88 | } 89 | 90 | function db_backend_delete($db, $id) 91 | { 92 | $sql = ' 93 | DELETE FROM 94 | pastes 95 | WHERE 96 | id = $1 97 | '; 98 | 99 | $res = pg_query_params($db, $sql, array($id)) or die("Failed to execute query '$sql'"); 100 | } 101 | -------------------------------------------------------------------------------- /www/inc/db-mysql.inc.php: -------------------------------------------------------------------------------- 1 | query( 'SET NAMES utf8mb4;' ); 16 | 17 | return $db; 18 | } 19 | 20 | function db_backend_prune($db) 21 | { 22 | // delete all pastes from database that have expired 23 | $sql = ' 24 | DELETE FROM 25 | pastes 26 | WHERE 27 | ttl != -1 AND 28 | ttl != -100 AND 29 | ( UNIX_TIMESTAMP() - added ) > ttl 30 | '; 31 | 32 | $stmt = $db->prepare($sql); 33 | $stmt->execute(); 34 | 35 | return $db->affected_rows; 36 | } 37 | 38 | function db_backend_get($db, $id) 39 | { 40 | // grab paste from database 41 | $sql = ' 42 | SELECT 43 | `id`, `added`, `ttl`, `password`, ( UNIX_TIMESTAMP() - added ) as age 44 | FROM 45 | pastes 46 | WHERE 47 | id = ? 48 | LIMIT 49 | 1 50 | '; 51 | 52 | $stmt = $db->prepare($sql); 53 | $stmt->bind_param('i', $id); 54 | $stmt->execute(); 55 | 56 | $res = $stmt->get_result(); 57 | 58 | return $res->fetch_array(MYSQLI_ASSOC); 59 | } 60 | 61 | function db_backend_read($db, $id) 62 | { 63 | // grab paste from database 64 | $sql = ' 65 | SELECT 66 | `data`, `syntax`, `ttl`, `crypto` 67 | FROM 68 | pastes 69 | WHERE 70 | id = ? 71 | LIMIT 72 | 1 73 | '; 74 | 75 | $stmt = $db->prepare($sql); 76 | $stmt->bind_param('i', $id); 77 | $stmt->execute(); 78 | 79 | $res = $stmt->get_result(); 80 | 81 | return $res->fetch_array(MYSQLI_ASSOC); 82 | } 83 | 84 | function db_backend_add($db, $data, $syntax, $ttl, $password, $cipher) 85 | { 86 | $sql = ' 87 | INSERT INTO 88 | pastes 89 | ( `data`, `syntax`, `added`, `ttl`, `password`, `crypto` ) 90 | VALUES 91 | ( ?, ?, UNIX_TIMESTAMP(), ?, ?, ? ) 92 | '; 93 | 94 | $stmt = $db->prepare($sql); 95 | $stmt->bind_param('ssiss', $data, $syntax, $ttl, $password, $cipher); 96 | $stmt->execute(); 97 | $stmt->close(); 98 | 99 | return $db->insert_id; 100 | } 101 | 102 | function db_backend_delete($db, $id) 103 | { 104 | $sql = ' 105 | DELETE FROM 106 | pastes 107 | WHERE 108 | id = ? 109 | '; 110 | 111 | $stmt = $db->prepare($sql); 112 | $stmt->bind_param( 'i', $id ); 113 | $stmt->execute(); 114 | } 115 | -------------------------------------------------------------------------------- /develop/css-sha1-versioned.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SHA1SUM=${SHA1SUM:-sha1sum} 6 | CSSMIN=${CSSMIN:-cssmin} 7 | 8 | function usage() { 9 | echo "Usage: $0 outputname [sources..]" >&2 10 | echo " Calculates SHA1 hash from input, generates outputname-HASH.css and .min.css" >&2 11 | exit 1 12 | } 13 | 14 | tmpdir=$(mktemp --tmpdir -d css-sha1-versioned-XXXXXXX) 15 | trap 'rm -rf "${tmpdir}"' EXIT 16 | 17 | while [ $# -gt 0 ]; do 18 | case "$1" in 19 | -h) 20 | usage 21 | ;; 22 | *) 23 | break 24 | ;; 25 | esac 26 | done 27 | 28 | target=$1 29 | base=$(basename "${target}") 30 | tdir=$(dirname "${target}") 31 | shift 32 | sources=("$@") 33 | 34 | if [ "" = "${base}" -o 0 -eq $# ]; then 35 | usage 36 | fi 37 | 38 | if [ 1 -eq $# -a "$1" = "-" ]; then 39 | # use STDIN 40 | sources=( ) 41 | fi 42 | 43 | if ! which "${SHA1SUM}" >/dev/null; then 44 | echo "Couldn't find program for sha1sum (${SHA1SUM}), set SHA1SUM environment variable" >&2 45 | exit 2 46 | fi 47 | 48 | if ! which "${CSSMIN}" >/dev/null; then 49 | echo "Couldn't find program for cssmin (${CSSMIN}), set CSSMIN environment variable" >&2 50 | exit 2 51 | fi 52 | 53 | ( 54 | if [ "${#sources[@]}" -eq 0 ]; then 55 | cat 56 | else 57 | for f in "${sources[@]}"; do 58 | echo 59 | printf "/* #source 1 1 %s */\n" "${f}" 60 | cat < "${f}" 61 | done 62 | fi 63 | ) > "${tmpdir}/source.css" 64 | 65 | hash=$("${SHA1SUM}" < "${tmpdir}/source.css") 66 | hash=${hash%% *} 67 | 68 | if [ "" = "${hash}" ]; then 69 | echo "Failed to get sha1 hash" >&2 70 | exit 3 71 | fi 72 | 73 | fname="${target}-${hash}.css" 74 | minname="${target}-${hash}.min.css" 75 | 76 | tmpfname="${base}-${hash}.css" 77 | tmpminname="${base}-${hash}.min.css" 78 | 79 | if [ -e "${fname}" -a -e "${minname}" ]; then 80 | echo "Target file already exists, no changes" 81 | exit 0 82 | fi 83 | 84 | mv "${tmpdir}/source.css" "${tmpdir}/${tmpfname}" 85 | 86 | cssmin < "${tmpdir}/${tmpfname}" > "${tmpdir}/${tmpminname}" 87 | 88 | ( 89 | declare -a files 90 | files=( "${target}"-*.{css,min.css} ) 91 | if [ 0 -eq "${#files[@]}" ]; then exit 0; fi 92 | 93 | for f in "${target}"-*.{css,min.css}; do 94 | if [ ! -e "${f}" ]; then continue; fi 95 | 96 | fh="${f:${#target}+1}" 97 | fh="${fh%.min.css}" 98 | fh="${fh%.css}" 99 | if [ 40 -ne "${#fh}" ]; then continue; fi # not SHA1 hash 100 | 101 | echo "Removing old file: $f" 102 | rm "${f}" 103 | done 104 | ) 105 | 106 | echo "Moving new files into place:" 107 | echo "${fname}" 108 | mv "${tmpdir}/${tmpfname}" "${fname}" 109 | echo "${minname}" 110 | mv "${tmpdir}/${tmpminname}" "${minname}" 111 | -------------------------------------------------------------------------------- /www/tpl/mobile/index.tpl: -------------------------------------------------------------------------------- 1 | incl('includes/header.tpl'); 3 | ?> 4 | 5 | 33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 | incl('includes/new.tpl'); ?> 44 | 45 | 53 | incl('includes/footer.tpl'); 55 | -------------------------------------------------------------------------------- /www/tpl/default/includes/header.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?=$meta_title?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | incl('includes/css.tpl'); ?> 15 | 16 | 17 | 22 | 23 | incl('includes/javascripts.tpl'); ?> 24 | 25 | 26 |
27 | 35 |
36 | 37 |
38 |
39 |
40 |
41 | 44 | 57 |
58 | -------------------------------------------------------------------------------- /www/tpl/default/index.tpl: -------------------------------------------------------------------------------- 1 | incl('includes/header.tpl'); 3 | ?> 4 | 5 | 33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 | incl('includes/new.tpl'); ?> 44 | 45 | 53 | incl('includes/footer.tpl'); 55 | -------------------------------------------------------------------------------- /develop/js-sha1-versioned.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SHA1SUM=${SHA1SUM:-sha1sum} 6 | UGLIFYJS=${UGLIFYJS:-uglifyjs} 7 | UGLIFYJS_OPTIONS="${UGLIFYJS_OPTIONS:--c -m}" 8 | 9 | function usage() { 10 | echo "Usage: $0 [uglifyjs options] outputname [sources..]" >&2 11 | echo " Calculates SHA1 hash from input, generates outputname-HASH.js, .min.js and .min.map" >&2 12 | exit 1 13 | } 14 | 15 | tmpdir=$(mktemp --tmpdir -d js-sha1-versioned-XXXXXXX) 16 | trap 'rm -rf "${tmpdir}"' EXIT 17 | 18 | while [ $# -gt 0 ]; do 19 | case "$1" in 20 | -h) 21 | usage 22 | ;; 23 | '-'*) 24 | # forward all options to uglifyfs 25 | UGLIFYJS_OPTIONS="${UGLIFYJS_OPTIONS} $1" 26 | shift 27 | ;; 28 | *) 29 | break 30 | ;; 31 | esac 32 | done 33 | 34 | target=$1 35 | base=$(basename "${target}") 36 | tdir=$(dirname "${target}") 37 | shift 38 | sources=("$@") 39 | 40 | if [ "" = "${base}" -o 0 -eq $# ]; then 41 | usage 42 | fi 43 | 44 | if [ 1 -eq $# -a "$1" = "-" ]; then 45 | # use STDIN 46 | sources=( ) 47 | fi 48 | 49 | if ! which "${SHA1SUM}" >/dev/null; then 50 | echo "Couldn't find program for sha1sum (${SHA1SUM}), set SHA1SUM environment variable" >&2 51 | exit 2 52 | fi 53 | 54 | if ! which "${UGLIFYJS}" >/dev/null; then 55 | echo "Couldn't find program for uglifyjs (${UGLIFYJS}), set UGLIFYJS environment variable" >&2 56 | exit 2 57 | fi 58 | 59 | ( 60 | printf "/// uglifyjs options: %s\n" "${UGLIFYJS_OPTIONS}" 61 | if [ "${#sources[@]}" -eq 0 ]; then 62 | cat 63 | else 64 | for f in "${sources[@]}"; do 65 | echo 66 | printf "///#source 1 1 %s\n" "${f}" 67 | cat < "${f}" 68 | done 69 | fi 70 | ) > "${tmpdir}/source.js" 71 | 72 | hash=$("${SHA1SUM}" < "${tmpdir}/source.js") 73 | hash=${hash%% *} 74 | 75 | if [ "" = "${hash}" ]; then 76 | echo "Failed to get sha1 hash" >&2 77 | exit 3 78 | fi 79 | 80 | fname="${target}-${hash}.js" 81 | minname="${target}-${hash}.min.js" 82 | mapname="${target}-${hash}.min.map" 83 | 84 | tmpfname="${base}-${hash}.js" 85 | tmpminname="${base}-${hash}.min.js" 86 | tmpmapname="${base}-${hash}.min.map" 87 | 88 | if [ -e "${fname}" -a -e "${minname}" -a -e "${mapname}" ]; then 89 | echo "Target file already exists, no changes" 90 | exit 0 91 | fi 92 | 93 | mv "${tmpdir}/source.js" "${tmpdir}/${tmpfname}" 94 | 95 | # options controlling output should be included in hash 96 | if [ "${#sources[@]}" -eq 0 ]; then 97 | # used STDIN to generate ${tmpfname} 98 | ( 99 | cd "${tmpdir}"; 100 | uglifyjs ${UGLIFYJS_OPTIONS} --lint --source-map "${tmpmapname}" -o "${tmpminname}" "${tmpfname}" 101 | ) 102 | else 103 | uglifyjs ${UGLIFYJS_OPTIONS} --lint --source-map-url "${tmpmapname}" --source-map "${tmpdir}/${tmpmapname}" -o "${tmpdir}/${tmpminname}" "${sources[@]}" 104 | fi 105 | 106 | ( 107 | declare -a files 108 | files=( "${target}"-*.{js,min.js,min.map} ) 109 | if [ 0 -eq "${#files[@]}" ]; then exit 0; fi 110 | 111 | for f in "${target}"-*.{js,min.js,min.map}; do 112 | if [ ! -e "${f}" ]; then continue; fi 113 | 114 | fh="${f:${#target}+1}" 115 | fh="${fh%.min.js}" 116 | fh="${fh%.js}" 117 | fh="${fh%.min.map}" 118 | if [ 40 -ne "${#fh}" ]; then continue; fi # not SHA1 hash 119 | 120 | echo "Removing old file: $f" 121 | rm "${f}" 122 | done 123 | ) 124 | 125 | echo "Moving new files into place:" 126 | echo "${fname}" 127 | mv "${tmpdir}/${tmpfname}" "${fname}" 128 | echo "${minname}" 129 | mv "${tmpdir}/${tmpminname}" "${minname}" 130 | echo "${mapname}" 131 | mv "${tmpdir}/${tmpmapname}" "${mapname}" 132 | -------------------------------------------------------------------------------- /www/inc/paste.class.php: -------------------------------------------------------------------------------- 1 | has_expired( $paste ); 34 | if( $expired === false ) return $paste; 35 | 36 | return $expired; 37 | } 38 | 39 | return NCRYPT_DOES_NOT_EXIST; 40 | } 41 | 42 | function read( $paste_meta ) 43 | { 44 | if (false !== ( $paste = db_read( $paste_meta['id'] ) )) 45 | { 46 | switch ($paste['crypto']) { 47 | case 'PIDCRYPT': 48 | $cipher = 'AES-128-CBC'; 49 | break; 50 | case 'CRYPTO_JS': 51 | $cipher = 'AES-256-OFB'; 52 | break; 53 | default: 54 | $cipher = $paste['crypto']; 55 | } 56 | 57 | if ( -100 == $paste['ttl'] ) 58 | { 59 | // one-time only paste, delete it now 60 | db_delete( $paste_meta['id'] ); 61 | } 62 | 63 | return array( 64 | 'data' => $paste['data'], 65 | 'syntax' => $paste['syntax'], 66 | 'cipher' => $cipher, 67 | ); 68 | } 69 | } 70 | 71 | function add( $data, $syntax, $ttl, $password, $cipher ) 72 | { 73 | if( !empty( $password ) ) 74 | { 75 | // create a salt that ensures crypt creates an md5 hash 76 | $base64_alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 77 | $salt = '$5$'; 78 | for( $i = 0; $i < 16; $i++ ) $salt .= $base64_alphabet[rand( 0, 63 )]; 79 | $salt .= '$'; 80 | $password = crypt( $password, $salt ); 81 | } 82 | 83 | // submit new paste to server 84 | return db_add( $data, $syntax, $ttl, $password, $cipher ); 85 | } 86 | 87 | function validate_password( $paste, $password ) 88 | { 89 | // if we haven't gotten a paste yet. 90 | if( empty( $paste ) ) return NCRYPT_MISSING_DATA; 91 | 92 | if( !empty( $paste['password'] ) ) 93 | { 94 | if (empty($password)) 95 | { 96 | // prompt user for password 97 | return NCRYPT_PASSWORD_REQUIRED; 98 | } 99 | 100 | if( strlen( $paste['password'] ) == 40 && '$' != $paste['password'][0] ) 101 | { 102 | // old style, salted with id 103 | $password = sha1( $paste['id'] . $password ); 104 | } 105 | else 106 | { 107 | // crypted 108 | $password = crypt( $password, $paste['password'] ); 109 | } 110 | 111 | if( 0 == strcmp( $password, $paste['password'] ) ) 112 | { 113 | // correct, send user the required data 114 | return NCRYPT_PASSWORD_SUCCESS; 115 | } 116 | 117 | // incorrect, give the json response an error 118 | return NCRYPT_PASSWORD_FAILED; 119 | } 120 | 121 | return NCRYPT_NO_PASSWORD; 122 | } 123 | 124 | function has_expired( $paste ) 125 | { 126 | // if we haven't gotten a paste yet. 127 | if( empty( $paste ) ) return NCRYPT_MISSING_DATA; 128 | 129 | // determine if the paste has expired. 130 | // if ttl is set to -1 that means it a perm paste 131 | // if ttl is set to -100 that means this is a one-time only paste 132 | // otherwise test to see if the ttl duration has been met 133 | if ( -100 == $paste['ttl'] ) 134 | { 135 | // one-time only paste, delete on read (not now) 136 | return false; 137 | } 138 | else if( $paste['ttl'] != -1 && $paste['age'] > $paste['ttl'] ) 139 | { 140 | // this paste is flagged as expired, time to clean up 141 | db_delete( $paste['id'] ); 142 | return NCRYPT_HAS_EXPIRED; 143 | } 144 | 145 | return false; 146 | } 147 | } -------------------------------------------------------------------------------- /www/inc/templates.class.php: -------------------------------------------------------------------------------- 1 | $value) { 29 | $this->assign('site_' . $key, $value); 30 | } 31 | $this->assign('cookie', $_COOKIE); 32 | } 33 | 34 | // assign template version 35 | public function assign( $name, $value ) 36 | { 37 | $this->template_vars[$name] = $value; 38 | } 39 | 40 | public function theme( $theme ) 41 | { 42 | if( is_dir( __DIR__ . '/../tpl/' . $theme ) ) 43 | { 44 | $this->theme = $theme; 45 | } 46 | } 47 | 48 | public function format( $format = null ) 49 | { 50 | if (null !== $format) $this->format = $format; 51 | return $this->format; 52 | } 53 | 54 | public function status_text( $code ) 55 | { 56 | switch ( $code ) 57 | { 58 | case 200: return 'OK'; 59 | case 403: return 'Forbidden'; 60 | case 404: return 'Not found'; 61 | default: throw new Exception( 'Unknown status code' ); 62 | } 63 | } 64 | 65 | // for status 200: raw rendering needs $object['data'], json outputs $object 66 | public function render( $status_code, $template_name = null, $object = array() ) 67 | { 68 | header ('HTTP/1.1 ' . $status_code . $this->status_text( $status_code ) ); 69 | 70 | if ($status_code !== 200 && $this->format != 'html') { 71 | header( 'Content-Type: text/plain' ); 72 | echo $this->status_text( $status_code ); 73 | return; 74 | } 75 | 76 | switch ( $this->format ) 77 | { 78 | case 'html': 79 | $this->template_vars = array_merge( $this->template_vars, $object ); 80 | $this->incl( $template_name ); 81 | break; 82 | case 'json': 83 | header( 'Content-Type: application/json' ); 84 | $json = json_encode( $object ); 85 | 86 | if( stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false ) 87 | { 88 | // if client accepts gzip, compress the data and send to client 89 | // this is so we can generate a progress report on pastes 90 | ob_start(); 91 | ob_start( 'ob_gzhandler' ); 92 | header( 'Content-Encoding: gzip' ); 93 | echo $json; 94 | ob_end_flush(); 95 | header( 'Content-Length: ' . ob_get_length() ); 96 | ob_end_flush(); 97 | } 98 | else 99 | { 100 | header( 'Content-Length: ' . mb_strlen( $json ) ); 101 | echo $json; 102 | } 103 | 104 | break; 105 | case 'raw': 106 | header( 'Content-Type: application/octet-stream' ); 107 | if (!empty( $object['syntax'] )) header( 'X-Syntax: ' . strip_unsafe_header( $object['syntax'] ) ); 108 | if (!empty( $object['cipher'] )) header( 'X-Cipher: ' . strip_unsafe_header( $object['cipher'] ) ); 109 | echo $object['data']; 110 | break; 111 | } 112 | } 113 | 114 | public function incl_find_file( $template_name ) 115 | { 116 | if( $template_name === null ) throw new Exception( 'Missing template to render' ); 117 | 118 | $basedir = __DIR__ . '/../tpl/'; 119 | 120 | if ( is_file ( $basedir . $this->theme . '/' . $template_name ) ) { 121 | return $basedir . $this->theme . '/' . $template_name; 122 | } else if ( $this->theme !== 'default' && is_file ( $basedir . 'default/' . $template_name ) ) { 123 | return $basedir . 'default/' . $template_name; 124 | } else { 125 | throw new Exception( 'Template file ('.$__template_name.') does not exist'); 126 | } 127 | } 128 | 129 | public function incl( $__template_name ) { 130 | // extract template values and skip already existing variables (to stop possible injections) 131 | extract( $this->template_vars, EXTR_SKIP ); 132 | 133 | // include our template 134 | require $this->incl_find_file( $__template_name ); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /www/inc/controller.class.php: -------------------------------------------------------------------------------- 1 | template = new Template(); 14 | $this->template->assign( 'meta_title', $conf['site']['name'] ); 15 | $this->template->assign( 'bitcoin_address', $conf['bitcoin-addresses'][mt_rand( 0, count( $conf['bitcoin-addresses'] ) - 1 )] ); 16 | 17 | if( isset( $_GET['raw'] ) ) 18 | { 19 | $this->template->format( 'raw' ); 20 | } 21 | elseif( 'POST' == $_SERVER['REQUEST_METHOD'] || isset( $_GET['json'] ) ) 22 | { 23 | $this->template->format( 'json' ); 24 | } 25 | 26 | // detect what device is viewing the page 27 | // require_once __DIR__ . '/mobile.class.php'; 28 | // $detect = new Mobile(); 29 | 30 | //if( $detect->isMobile() ) 31 | // $template->theme( 'mobile' ); 32 | //elseif( $detect->isTablet() ) 33 | // $template->theme( 'tablet' ); 34 | } 35 | 36 | function show( $id, $password ) 37 | { 38 | $conf = get_config(); 39 | $template = $this->template; 40 | if( !empty( $_COOKIE['theme'] ) ) $template->assign( 'theme', $_COOKIE['theme'] ); 41 | //print_r( $_COOKIE ); 42 | 43 | // This may be required if a user is dealing with a file that is so large that is takes more than 30 seconds 44 | set_time_limit( 0 ); 45 | 46 | require_once __DIR__ . '/paste.class.php'; 47 | 48 | $pastes = new Paste(); 49 | 50 | $paste = $pastes->get( $id ); 51 | 52 | // detect if any errors came through 53 | switch( $paste ) 54 | { 55 | case NCRYPT_DOES_NOT_EXIST: 56 | case NCRYPT_HAS_EXPIRED: 57 | case NCRYPT_MISSING_DATA: 58 | $template->assign( 'meta_title', $conf['site']['name'] . ' - Paste does not exist' ); 59 | $template->render( 404, 'nonexistant.tpl' ); 60 | return; 61 | } 62 | 63 | // validate paste, check if password has been set 64 | $validated = $pastes->validate_password( $paste, $password ); 65 | 66 | switch( $validated ) 67 | { 68 | case NCRYPT_PASSWORD_FAILED: 69 | // incorrect, give the json response an error 70 | case NCRYPT_PASSWORD_REQUIRED: 71 | // prompt user for password 72 | 73 | $template->assign( 'meta_title', $conf['site']['name'] . ' - Paste requires password' ); 74 | $template->render( 403, 'paste.tpl' ); 75 | break; 76 | 77 | case NCRYPT_PASSWORD_SUCCESS: 78 | // correct, send user the required data 79 | case NCRYPT_NO_PASSWORD: 80 | // no password, show paste 81 | 82 | if ( 'html' !== $template->format() ) { 83 | $output = $pastes->read( $paste ); 84 | } else { 85 | // read paste via javascript to make basic page loading faster 86 | $output = array(); 87 | } 88 | 89 | $template->assign( 'meta_title', $conf['site']['name'] . ' - Paste' ); 90 | $template->render( 200, 'paste.tpl', $output ); 91 | break; 92 | 93 | default: 94 | throw new Exception( 'Internal error' ); 95 | } 96 | } 97 | 98 | function index() 99 | { 100 | $template = $this->template; 101 | 102 | require_once __DIR__ . '/paste.class.php'; 103 | 104 | // new paste 105 | $template->assign( 'norobots', false ); 106 | $template->render( 200, 'index.tpl' ); 107 | } 108 | 109 | /* only works with JSON format */ 110 | function post( $data, $syntax, $ttl, $password, $cipher ) 111 | { 112 | $template = $this->template; 113 | 114 | // This may be required if a user is dealing with a file that is so large that it takes more than 30 seconds 115 | set_time_limit( 0 ); 116 | 117 | require_once __DIR__ . '/paste.class.php'; 118 | 119 | $pastes = new Paste(); 120 | 121 | // new post submission 122 | $paste = $pastes->add( $data, $syntax, $ttl, $password, $cipher ); 123 | 124 | // return our new ID to the user 125 | $output = array( 126 | 'id' => alphaID( $paste, false ), 127 | ); 128 | 129 | $template->render( 200, null, $output ); 130 | } 131 | 132 | function about() 133 | { 134 | $conf = get_config(); 135 | $template = $this->template; 136 | $template->assign( 'meta_title', $conf['site']['name'] . ' - About' ); 137 | 138 | // About Page 139 | $template->render( 200, 'about.tpl' ); 140 | } 141 | 142 | function ncrypt_script() 143 | { 144 | header( 'Content-Type: text/plain; charset=utf-8' ); 145 | header( 'Content-Disposition: inline; filename=ncrypt' ); 146 | 147 | $conf = get_config(); 148 | 149 | $default_url = 'https://ncry.pt'; // the url used in the script file 150 | echo str_replace( $default_url, $conf['scripturl'], file_get_contents( __DIR__ . '/ncrypt.rb' ) ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /source/sjcl-md5/sjcl-md5.js: -------------------------------------------------------------------------------- 1 | /** @fileOverview Javascript MD5 implementation. 2 | * 3 | * Based on the implementation in RFC 1321, and on the SJCL 4 | * SHA-1 implementation. 5 | * 6 | * @author Brandon Smith 7 | */ 8 | 9 | /** 10 | * Context for a MD5 operation in progress. 11 | * @constructor 12 | * @class MD5, 128 bits. 13 | */ 14 | sjcl.hash.md5 = function (hash) { 15 | if (!this._T[0]) { this._precompute(); } 16 | if (hash) { 17 | this._h = hash._h.slice(0); 18 | this._buffer = hash._buffer.slice(0); 19 | this._length = hash._length; 20 | } else { 21 | this.reset(); 22 | } 23 | }; 24 | 25 | /** 26 | * Hash a string or an array of words. 27 | * @static 28 | * @param {bitArray|String} data the data to hash. 29 | * @return {bitArray} The hash value, an array of 5 big-endian words. 30 | */ 31 | sjcl.hash.md5.hash = function (data) { 32 | return (new sjcl.hash.md5()).update(data).finalize(); 33 | }; 34 | 35 | sjcl.hash.md5.prototype = { 36 | /** 37 | * The hash's block size, in bits. 38 | * @constant 39 | */ 40 | blockSize: 512, 41 | 42 | /** 43 | * Reset the hash state. 44 | * @return this 45 | */ 46 | reset:function () { 47 | this._h = this._init.slice(0); 48 | this._buffer = []; 49 | this._length = 0; 50 | return this; 51 | }, 52 | 53 | /** 54 | * Input several words to the hash. 55 | * @param {bitArray|String} data the data to hash. 56 | * @return this 57 | */ 58 | update: function (data) { 59 | if (typeof data === "string") { 60 | data = sjcl.codec.utf8String.toBits(data); 61 | } 62 | var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data), 63 | ol = this._length, 64 | nl = this._length = ol + sjcl.bitArray.bitLength(data); 65 | for (i = this.blockSize+ol & -this.blockSize; i <= nl; 66 | i+= this.blockSize) { 67 | this._block(b.splice(0,16), true); 68 | } 69 | return this; 70 | }, 71 | 72 | /** 73 | * Complete hashing and output the hash value. 74 | * @return {bitArray} The hash value, an array of 4 big-endian words. 75 | */ 76 | finalize:function () { 77 | var i, b = this._buffer, h = this._h; 78 | 79 | // Round out and push the buffer 80 | b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1,1)]); 81 | // Round out the buffer to a multiple of 16 words, less the 2 length words. 82 | for (i = b.length + 2; i & 15; i++) { 83 | b.push(0); 84 | } 85 | 86 | // append the length 87 | b.push(this._length | 0); 88 | b.push((this._length / 0x100000000)|0); 89 | 90 | while (b.length) { 91 | // b.length is passed to avoid swapping and reswapping length bytes 92 | this._block(b.splice(0,16), b.length); 93 | } 94 | 95 | this.reset(); 96 | this._BS(h, 4); 97 | return h; 98 | }, 99 | 100 | /** 101 | * The MD5 initialization vector. 102 | * @private 103 | */ 104 | _init:[0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476], 105 | 106 | /** 107 | * Byte swap 108 | * @private 109 | */ 110 | _BS:function(w, n) { 111 | var i, x; 112 | for (i=0; i>>24) | (x>>8&0xff00) | ((x&0xff00)<<8) | ((x&0xff)<<24); 115 | } 116 | }, 117 | 118 | /* Will be precomputed */ 119 | _T:[], 120 | /* 121 | * 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 122 | * 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 123 | * 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 124 | * 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 125 | * 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 126 | * 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 127 | * 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 128 | * 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 129 | * 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 130 | * 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 131 | * 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 132 | * 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 133 | * 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 134 | * 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 135 | * 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 136 | * 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 137 | * @private 138 | */ 139 | _precompute:function() { 140 | var i; 141 | for (i=0; i<64; i++) { 142 | this._T[i] = ((0xffffffff+1) * Math.abs(Math.sin(i+1)))|0; 143 | } 144 | }, 145 | 146 | /** 147 | * Perform one cycle of MD5. 148 | * @param {bitArray} words one block of words. 149 | * @private 150 | */ 151 | _block:function (words, notlast) { 152 | var i, a, b, c, d, 153 | w = words.slice(0), 154 | h = this._h, 155 | T = this._T; 156 | 157 | a = h[0]; b = h[1]; c = h[2]; d = h[3]; 158 | 159 | this._BS(w, notlast?16:14); 160 | for (i=0; i<64; i++) { 161 | var f, x, s, t; 162 | if (i < 32) { 163 | if (i < 16) { 164 | f = (b & c) | ((~b) & d); 165 | x = i; 166 | s = [7, 12, 17, 22]; 167 | } else { 168 | f = (d & b) | ((~d) & c); 169 | x = (5 * i + 1) % 16; 170 | s = [5, 9, 14, 20]; 171 | } 172 | } else { 173 | if (i < 48) { 174 | f = b ^ c ^ d; 175 | x = (3 * i + 5) % 16; 176 | s = [4, 11, 16, 23]; 177 | } else { 178 | f = c ^ (b | (~d)); 179 | x = (7 * i) % 16; 180 | s = [6, 10, 15, 21]; 181 | } 182 | } 183 | t = a + f + w[x] + T[i]; 184 | a = d; 185 | d = c; 186 | c = b; 187 | b = (((t << s[i%4]) | (t >>> 32-s[i%4])) + b)|0; 188 | } 189 | 190 | h[0] += a; 191 | h[1] += b; 192 | h[2] += c; 193 | h[3] += d; 194 | } 195 | }; 196 | -------------------------------------------------------------------------------- /www/inc/functions.inc.php: -------------------------------------------------------------------------------- 1 | PpQXn7COf 9 | * 10 | * specifiying the second argument true, it will 11 | * translate back e.g.: 12 | * PpQXn7COf --> 9007199254740989 13 | * 14 | * this function is based on any2dec && dec2any by 15 | * fragmer[at]mail[dot]ru 16 | * see: http://nl3.php.net/manual/en/function.base-convert.php#52450 17 | * 18 | * If you want the alphaID to be at least 3 letter long, use the 19 | * $pad_up = 3 argument 20 | * 21 | * In most cases this is better than totally random ID generators 22 | * because this can easily avoid duplicate ID's. 23 | * For example if you correlate the alpha ID to an auto incrementing ID 24 | * in your database, you're done. 25 | * 26 | * The reverse is done because it makes it slightly more cryptic, 27 | * but it also makes it easier to spread lots of IDs in different 28 | * directories on your filesystem. Example: 29 | * $part1 = substr($alpha_id,0,1); 30 | * $part2 = substr($alpha_id,1,1); 31 | * $part3 = substr($alpha_id,2,strlen($alpha_id)); 32 | * $destindir = "/".$part1."/".$part2."/".$part3; 33 | * // by reversing, directories are more evenly spread out. The 34 | * // first 26 directories already occupy 26 main levels 35 | * 36 | * more info on limitation: 37 | * - http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/165372 38 | * 39 | * if you really need this for bigger numbers you probably have to look 40 | * at things like: http://theserverpages.com/php/manual/en/ref.bc.php 41 | * or: http://theserverpages.com/php/manual/en/ref.gmp.php 42 | * but I haven't really dugg into this. If you have more info on those 43 | * matters feel free to leave a comment. 44 | * 45 | * @author Kevin van Zonneveld 46 | * @author Simon Franz 47 | * @author Deadfish 48 | * @copyright 2008 Kevin van Zonneveld (http://kevin.vanzonneveld.net) 49 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence 50 | * @version SVN: Release: $Id: alphaID.inc.php 344 2009-06-10 17:43:59Z kevin $ 51 | * @link http://kevin.vanzonneveld.net/ 52 | * 53 | * @param mixed $in String or long input to translate 54 | * @param boolean $to_num Reverses translation when true 55 | * @param mixed $pad_up Number or boolean padds the result up to a specified length 56 | * 57 | * @return mixed string or long 58 | */ 59 | function alphaID( $in, $to_num = false, $pad_up = false ) 60 | { 61 | $__conf = get_config(); 62 | $index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 63 | $i = array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ); 64 | 65 | if( !empty( $__conf['paste']['secret'] ) ) 66 | { 67 | // Although this function's purpose is to just make the 68 | // ID short - and not so much secure, 69 | // with this patch by Simon Franz (http://blog.snaky.org/) 70 | // you can optionally supply a password to make it harder 71 | // to calculate the corresponding numeric ID 72 | for( $n = 0; $n < strlen( $index ); $n++ ) 73 | { 74 | $i[] = substr( $index, $n, 1 ); 75 | } 76 | 77 | $passhash = hash( 'sha256', $__conf['paste']['secret'] ); 78 | $passhash = ( strlen( $passhash ) < strlen( $index ) ) ? hash( 'sha512', $__conf['paste']['secret'] ) : $passhash; 79 | 80 | for( $n = 0; $n < strlen( $index ); $n++ ) 81 | { 82 | $p[] = substr( $passhash, $n, 1 ); 83 | } 84 | 85 | array_multisort( $p, SORT_DESC, $i ); 86 | $index = implode( $i ); 87 | } 88 | 89 | $base = strlen( $index ); 90 | 91 | if( $to_num ) 92 | { 93 | // Digital number <<-- alphabet letter code 94 | $in = strrev( $in ); 95 | $out = 0; 96 | $len = strlen( $in ) - 1; 97 | for( $t = 0; $t <= $len; $t++ ) 98 | { 99 | $bcpow = bcpow( $base, $len - $t ); 100 | $out += strpos( $index, $in[$t] ) * $bcpow; 101 | } 102 | 103 | if( is_numeric( $pad_up ) ) 104 | { 105 | $pad_up--; 106 | if( $pad_up > 0 ) 107 | { 108 | $out -= pow( $base, $pad_up ); 109 | } 110 | } 111 | } 112 | else 113 | { 114 | // Digital number -->> alphabet letter code 115 | if( is_numeric( $pad_up ) ) 116 | { 117 | $pad_up--; 118 | if( $pad_up > 0 ) 119 | { 120 | $in += pow( $base, $pad_up ); 121 | } 122 | } 123 | 124 | $out = ''; 125 | for( $t = floor( log10( $in ) / log10( $base ) ); $t >= 0; $t-- ) 126 | { 127 | $bcp = bcpow( $base, $t ); 128 | $a = (int) floor( $in / $bcp ); 129 | $out .= $index[$a]; 130 | $in -= $a * $bcp; 131 | } 132 | $out = strrev( $out ); // reverse 133 | } 134 | 135 | return $out; 136 | } 137 | 138 | function is_cli() 139 | { 140 | if( defined( 'STDIN' ) ) return true; 141 | if( php_sapi_name() === 'cli' ) return true; 142 | if( array_key_exists( 'SHELL', $_ENV ) ) return true; 143 | if( empty( $_SERVER['REMOTE_ADDR'] ) && !isset( $_SERVER['HTTP_USER_AGENT'] ) && count( @$_SERVER['argv'] ) > 0 ) return true; 144 | if( !array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) return true; 145 | return false; 146 | } 147 | 148 | function POST($key, $default = '') { 149 | if (isset($_POST[$key])) return $_POST[$key]; 150 | return $default; 151 | } 152 | 153 | function GET($key, $default = '') { 154 | if (isset($_GET[$key])) return $_GET[$key]; 155 | return $default; 156 | } 157 | 158 | function REQUEST($key, $default = '') { 159 | if (isset($_REQUEST[$key])) return $_REQUEST[$key]; 160 | return $default; 161 | } 162 | 163 | function SERVER($key, $default = '') { 164 | if (isset($_SERVER[$key])) return $_SERVER[$key]; 165 | return $default; 166 | } 167 | -------------------------------------------------------------------------------- /www/inc/config.inc.php: -------------------------------------------------------------------------------- 1 | (is_cli()) ? 'cli' : $_SERVER['SERVER_NAME'], 7 | 'ip' => (is_cli()) ? '127.0.0.1' : $_SERVER['SERVER_ADDR'], 8 | 9 | 'database' => [ 10 | 'host' => 'localhost', 11 | 'username' => 'username', 12 | 'password' => 'password', 13 | 'db' => 'ncrypt', 14 | ], 15 | 16 | 'paste' => [ 17 | 'secret' => '', // set this if you want unidentifiable alphaIds (not really secure) 18 | 'attachments' => [ 19 | 'max_size' => ( 2 * 1024 * 1024 ), // 2MB largest file size (not used yet) 20 | ], 21 | ], 22 | 23 | 'site' => [ 24 | 'url' => '/', 25 | 'name' => 'NCrypt', 26 | 'version' => 'v0.7.61', 27 | 'source' => 'https://github.com/luggs-co/ncrypt', 28 | 'contact' => 'mailto:contact@ncry.pt', 29 | 'attachments' => [ 30 | 'enabled' => true, 31 | ], 32 | ], 33 | 34 | //'scripturl' => 'https://ncry.pt', 35 | 'scripturl' => (is_cli()) ? 'cli' : ( isset( $_SERVER['HTTPS'] ) ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'], 36 | ]; 37 | 38 | $year = gmdate('Y'); 39 | 40 | $__config['site']['footer'] = << ♦  43 | Contact 44 | EOD; 45 | 46 | $__config['mime-types'] = [ 47 | 'Common Formats', 48 | [ 'text/plain' , 'Plain Text' ], 49 | [ 'application/x-aspx' , 'ASP.NET' ], 50 | [ 'text/x-bash' , 'Bash' ], 51 | [ 'text/x-csrc' , 'C' ], 52 | [ 'text/x-c++src' , 'C++' ], 53 | [ 'text/x-csharp' , 'C#' ], 54 | [ 'text/css' , 'CSS' ], 55 | [ 'text/x-dockerfile' , 'DockerFile' ], 56 | [ 'text/x-less' , 'LESS' ], 57 | [ 'text/x-scss' , 'SCSS' ], 58 | [ 'text/x-erlang' , 'Erlang' ], 59 | [ 'text/x-go' , 'Go' ], 60 | [ 'htmlmixed' , 'HTML mixed-mode' ], 61 | [ 'text/x-java' , 'Java' ], 62 | [ 'text/javascript' , 'JavaScript' ], 63 | [ 'text/x-perl' , 'Perl' ], 64 | [ 'application/x-httpd-php' , 'PHP' ], 65 | [ 'text/x-python' , 'Python' ], 66 | [ 'text/x-ruby' , 'Ruby' ], 67 | [ 'text/x-sql' , 'SQL' ], 68 | [ 'text/x-tcl' , 'TCL' ], 69 | [ 'application/xml' , 'XML' ], 70 | 'Other Formats', 71 | [ 'text/apl' , 'APL' ], 72 | [ 'text/x-asterisk' , 'Asterisk' ], 73 | [ 'text/x-brainfuck' , 'BrainFuck' ], 74 | [ 'text/x-cassandra' , 'Cassandra' ], 75 | [ 'text/x-clojure' , 'Clojure' ], 76 | [ 'text/x-cmake' , 'CMake' ], 77 | [ 'text/x-coffeescript' , 'CoffeeScript' ], 78 | [ 'text/x-cobol' , 'Cobol' ], 79 | [ 'text/x-diff' , 'Diff' ], 80 | [ 'text/x-django' , 'Django' ], 81 | [ 'text/x-eiffel' , 'Eiffel' ], 82 | [ 'text/x-gfm' , 'GitHub Markdown' ], 83 | [ 'text/x-groovy' , 'Groovy' ], 84 | [ 'text/x-haskell' , 'Haskell' ], 85 | [ 'text/html' , 'HTML embedded scripts' ], 86 | [ 'application/x-jsp' , 'JavaServer Pages' ], 87 | [ 'application/json' , 'JSON' ], 88 | [ 'jinja2' , 'Jinja2' ], 89 | [ 'text/x-lua' , 'Lua' ], 90 | [ 'text/x-mariadb' , 'MariaDB' ], 91 | [ 'text/x-markdown' , 'Markdown' ], 92 | [ 'application/mbox' , 'mbox' ], 93 | [ 'text/mirc' , 'mIRC' ], 94 | [ 'text/x-mssql' , 'MSSQL' ], 95 | [ 'text/x-mysql' , 'MySQL' ], 96 | [ 'text/x-nginx-conf' , 'Nginx Config' ], 97 | [ 'text/n-triples' , 'NTriples' ], 98 | [ 'text/x-pascal' , 'Pascal' ], 99 | [ 'application/pgp' , 'PGP' ], 100 | [ 'text/x-pgsql' , 'PGSQL' ], 101 | [ 'application/x-powershell' , 'PowerShell' ], 102 | [ 'text/x-rsc' , 'R' ], 103 | [ 'text/x-rst' , 'reStructuredText' ], 104 | [ 'text/x-russrct' , 'Rust' ], 105 | [ 'text/x-sass' , 'SASS' ], 106 | [ 'text/x-scheme' , 'Scheme' ], 107 | [ 'application/sieve' , 'Sieve' ], 108 | [ 'text/x-stsrc' , 'Smalltalk' ], 109 | [ 'text/x-smarty' , 'Smarty' ], 110 | [ 'application/sparql' , 'SPARQL' ], 111 | [ 'text/x-stex' , 'sTeX, LaTeX' ], 112 | [ 'text/x-tiddlywiki' , 'Tiddlywiki' ], 113 | [ 'text/velocity' , 'Velocity' ], 114 | [ 'text/x-verilog' , 'Verilog' ], 115 | [ 'text/x-vb' , 'Visual Basic' ], 116 | [ 'text/vbscript' , 'VB Script' ], 117 | [ 'text/x-yaml' , 'YAML' ], 118 | ]; 119 | 120 | $__config['code-themes'] = [ 121 | "default", 122 | "3024-day", 123 | "3024-night", 124 | "ambiance", 125 | "base16-dark", 126 | "base16-light", 127 | "blackboard", 128 | "cobalt", 129 | "colorforth", 130 | "eclipse", 131 | "elegant", 132 | "erlang-dark", 133 | "lesser-dark", 134 | "liquibyte", 135 | "mbo", 136 | "mdn-like", 137 | "midnight", 138 | "monokai", 139 | "neat", 140 | "neo", 141 | "night", 142 | "paraiso-dark", 143 | "paraiso-light", 144 | "pastel-on-dark", 145 | "rubyblue", 146 | "solarized dark", 147 | "solarized light", 148 | "the-matrix", 149 | "tomorrow-night-bright", 150 | "tomorrow-night-eighties", 151 | "ttcn", 152 | "twilight", 153 | "vibrant-ink", 154 | "xq-dark", 155 | "xq-light", 156 | "zenburn", 157 | ]; 158 | 159 | $__config['bitcoin-addresses'] = [ 160 | '1NCrypt1VFFWxa2ambbQqbeyaBfcjKQQKc', 161 | '1NCrypts6Jr31whfqEyhX9ChGGDfocv9mi', 162 | '1NCryptQxNy3M4ywT34DR3vvLw1YKwXzWT', 163 | '1NCryptq6jW9fAekLv1Dz8zKKd2K5S89S1', 164 | '1NCryptMEHo3bpXjC8sY211Meiw4j5CLs8', 165 | '1NCryptE8uh44NnYTErxPsE7mndt2Qt4Uu', 166 | '1NCryptE3MRaZsPU2ataHoQRknWQK6Ee9E', 167 | '1NCryptHxe6kikartjSvrU3DSxsb5AFXgX', 168 | '1NCryptPDkey2BRfuU72BmbAR7WysaKuSW', 169 | '1NCryptEqjsCrBcV77VuAhL8zj43Pvxogk', 170 | '1NCryptNq4dKQmgU1RUqBhvVy7NtXgbY4v', 171 | '1NCryptMjfzc7CmMe219NuAy5gw4C2n6mN', 172 | '1NCryptTtRXmrWh6qWC9ho8q4p7EXcaz8Q', 173 | '1NCrypt5zbxwBmyyvMrVZr4x4n2NBBPNr8', 174 | ]; 175 | 176 | if( file_exists( __DIR__ . '/config-local.inc.php' ) ) 177 | { 178 | require_once __DIR__ . '/config-local.inc.php'; 179 | } 180 | 181 | function get_config() { global $__config; return $__config; } -------------------------------------------------------------------------------- /source/sjcl.backend.js: -------------------------------------------------------------------------------- 1 | 2 | var global = typeof window !== "undefined" && window !== null ? window : this; 3 | 4 | (function() { 5 | var window = global; 6 | 7 | var console = ('undefined' === typeof global.console) ? { } : global.console; 8 | if ('undefined' === typeof console.log) console.log = function(){}; 9 | 10 | #INCLUDE sjcl/core/sjcl.js 11 | #INCLUDE sjcl/core/aes.js 12 | #INCLUDE sjcl/core/cbc.js 13 | #INCLUDE sjcl/core/bitArray.js 14 | #INCLUDE sjcl/core/codecBase64.js 15 | #INCLUDE sjcl/core/codecBytes.js 16 | #INCLUDE sjcl/core/codecHex.js 17 | #INCLUDE sjcl/core/codecString.js 18 | #INCLUDE sjcl/core/hmac.js 19 | #INCLUDE sjcl/core/pbkdf2.js 20 | #INCLUDE sjcl-md5/sjcl-md5.js 21 | #INCLUDE sjcl/core/sha1.js 22 | #INCLUDE sjcl/core/sha256.js 23 | #INCLUDE sjcl/core/random.js 24 | 25 | sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."](); 26 | 27 | if (typeof global.sjcl === 'undefined') global.sjcl = sjcl; 28 | 29 | var bitArray = sjcl.bitArray; 30 | 31 | function encode_input(block, options) { 32 | var binary = options && options.binary; 33 | if (binary) return sjcl.codec.bytes.toBits(block); 34 | return sjcl.codec.utf8String.toBits(block); 35 | } 36 | 37 | function decode_output(block, options) { 38 | var binary = options && options.binary; 39 | if (!binary) { 40 | try { 41 | return sjcl.codec.utf8String.fromBits(block); 42 | } catch (e) { 43 | console.log("Couldn't decode to UTF-8, trying binary instead:"); 44 | console.log(e); 45 | } 46 | } 47 | return sjcl.codec.bytes.fromBits(block); 48 | } 49 | 50 | /**************** 51 | * AES-256-OFB * 52 | ****************/ 53 | 54 | function aes_256_ofb_derive_key(key, iv) { 55 | return sjcl.misc.pbkdf2(key, iv, 1, 256, function (password) { return new sjcl.misc.hmac(password, sjcl.hash.sha1); }); 56 | } 57 | 58 | /* en- and decrypt. modifies data. */ 59 | function ofb(prp, data, iv) { 60 | if (bitArray.bitLength(iv) !== 128) { 61 | throw new sjcl.exception.invalid("ofb iv must be 128 bits"); 62 | } 63 | var i, 64 | bl = bitArray.bitLength(data), 65 | bp = 0, 66 | output = data; // .slice(); // duplicate if data shouldn't get modified 67 | 68 | for (i=0; bp <= bl; i+=4, bp+=128) { 69 | iv = prp.encrypt(iv); 70 | output[i] ^= iv[0]; 71 | output[i+1] ^= iv[1]; 72 | output[i+2] ^= iv[2]; 73 | output[i+3] ^= iv[3]; 74 | } 75 | /* clamp output */ 76 | output.splice((bl+31) >> 5); 77 | bl = bl % 32; 78 | if (bl > 0) { 79 | output[output.length-1] = bitArray.partial(bl, output[output.length-1] & 0x80000000 >> (bl-1), 1); 80 | } 81 | 82 | return output; 83 | } 84 | 85 | // modifies block 86 | function aes_256_ofb_decrypt(key, block) { 87 | var iv = block.splice(0, 4); /* extract 4 32-bit ints = 128 bits */ 88 | var aes = new sjcl.cipher.aes(aes_256_ofb_derive_key(key, iv)); 89 | return ofb(aes, block, iv); 90 | } 91 | 92 | // modifies block 93 | function aes_256_ofb_encrypt(key, block) { 94 | var iv = sjcl.random.randomWords(4, 0); /* should have enough entropy after we got the key */ 95 | var aes = new sjcl.cipher.aes(aes_256_ofb_derive_key(key, iv)); 96 | var cipher = ofb(aes, block, iv); 97 | cipher.splice(0,0,iv[0],iv[1],iv[2],iv[3]); 98 | return cipher; 99 | } 100 | 101 | /**************** 102 | * AES-128-CBC * 103 | ****************/ 104 | 105 | // returns [aes key, iv] 106 | function aes_128_cbc_key_derive(key, salt) { 107 | var t = bitArray.concat(sjcl.codec.utf8String.toBits(key), salt); 108 | var md5 = sjcl.hash.md5.hash; // returns 4*32 = 128 bits 109 | var m1 = md5(t); 110 | var m2 = md5(m1.concat(t)); 111 | // var m3 = md5(m2.concat(t)); 112 | return [m1, m2]; 113 | } 114 | 115 | // modifies block 116 | function aes_128_cbc_decrypt(key, block) { 117 | var magic = block.splice(0, 2); 118 | if (sjcl.codec.utf8String.fromBits(magic) !== "Salted__") throw "AES-128-CBC: unexpected input, missing 'Salted__' prefix"; 119 | var salt = block.splice(0, 2); 120 | var kiv = aes_128_cbc_key_derive(key, salt); 121 | var aes = new sjcl.cipher.aes(kiv[0]); 122 | return sjcl.mode.cbc.decrypt(aes, block, kiv[1], []); 123 | } 124 | 125 | // modifies block 126 | function aes_128_cbc_encrypt(key, block) { 127 | var magic = sjcl.codec.utf8String.toBits("Salted__"); 128 | var salt = sjcl.random.randomWords(2, 0); /* should have enough entropy after we got the key */ 129 | var kiv = aes_128_cbc_key_derive(key, salt); 130 | var aes = new sjcl.cipher.aes(kiv[0]); 131 | var cipher = sjcl.mode.cbc.encrypt(aes, block, kiv[1], []); 132 | return magic.concat(salt, cipher); 133 | } 134 | 135 | var b = { 136 | decrypt: function(key, block, cipher, options) { 137 | block = sjcl.codec.base64.toBits(block); 138 | switch (cipher) { 139 | case 'AES-256-OFB': 140 | return decode_output(aes_256_ofb_decrypt(key, block), options); 141 | break; 142 | case 'AES-128-CBC': 143 | return decode_output(aes_128_cbc_decrypt(key, block), options).slice(0, -1); // slice removes trailing newline 144 | break; 145 | default: 146 | throw ('cipher "' + cipher + '" not supported'); 147 | } 148 | }, 149 | 150 | encrypt: function(key, block, cipher, options) { 151 | switch (cipher) { 152 | case 'AES-256-OFB': 153 | block = aes_256_ofb_encrypt(key, encode_input(block, options)); 154 | break; 155 | case 'AES-128-CBC': 156 | var trailing_nl = sjcl.codec.bytes.toBits([0x0a]); 157 | block = aes_128_cbc_encrypt(key, bitArray.concat(encode_input(block, options), trailing_nl)); 158 | break; 159 | default: 160 | throw ('cipher "' + cipher + '" not supported'); 161 | } 162 | return sjcl.codec.base64.fromBits(block); 163 | }, 164 | 165 | sha: function(text) { 166 | return sjcl.codec.hex.fromBits(sjcl.hash.sha1.hash(text)); 167 | }, 168 | 169 | randomKey: function(callback) { 170 | var col_started = false; 171 | if (!sjcl.random.isReady()) { 172 | if (!sjcl.random._collectorsStarted) { 173 | sjcl.random.startCollectors(); 174 | col_started = true; 175 | } 176 | setTimeout(function () { 177 | b.randomKey(callback); 178 | if (col_started) sjcl.random.stopCollectors(); 179 | }, 200); 180 | return; 181 | } 182 | var key = sjcl.codec.base64url.fromBits(sjcl.random.randomWords(8)); 183 | callback(key); 184 | }, 185 | 186 | _add_entropy: function(rnd, entropy, source) { 187 | sjcl.random.addEntropy(rnd, entropy, source); 188 | } 189 | }; 190 | 191 | if ('undefined' === typeof global.crypto_backends) global.crypto_backends = {}; 192 | global.crypto_backends['sjcl'] = b; 193 | if ('undefined' === typeof global.ncrypt_backend) global.ncrypt_backend = b; 194 | }) (); 195 | 196 | if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) { 197 | self.onmessage = function (event) { 198 | var data = event.data; 199 | try { 200 | self.postMessage({id: data.id, func: data.func, result: global.ncrypt_backend[data.func].apply(this, data.arguments) }); 201 | } catch (e) { 202 | console.log("exception in crypto worker:") 203 | console.log(e); 204 | self.postMessage({id: data.id, func: data.func, error: e.toString() }); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /www/inc/ncrypt.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'openssl' 4 | require 'base64' 5 | require 'securerandom' 6 | 7 | require 'net/http' 8 | require 'net/https' 9 | 10 | def pbkdf2(password, salt, keylen, opts = {}) 11 | hash = opts[:hash] || 'sha1' 12 | iterations = (opts[:iterations] || 1) - 1 13 | def bigendian(val, len) 14 | len.times.map { val, r = val.divmod 256; r }.reverse.pack('C*') 15 | end 16 | key = '' 17 | blockindex = 1 18 | while key.length < keylen 19 | block = OpenSSL::HMAC.digest(hash, password, salt + bigendian(blockindex, 4)) 20 | u = block 21 | iterations.times do 22 | u = OpenSSL::HMAC.digest(hash, password, u) 23 | block.length.times.each do |j| 24 | block[j] ^= u[j] 25 | end 26 | end 27 | key += block 28 | blockindex += 1 29 | end 30 | key.slice(0, keylen) 31 | end 32 | 33 | def aes_decrypt(text, key, opts = {}) 34 | text = text.dup 35 | iv = text.slice!(0, 16) # aes has fixed blocksize of 128 bits 36 | key = pbkdf2(key, iv, (opts[:aeskeysize] || 256) / 8, opts) 37 | 38 | cipher = OpenSSL::Cipher::AES.new(8 * key.length, opts[:mode] || :OFB).decrypt 39 | cipher.key = key 40 | cipher.iv = iv 41 | cipher.update(text) + cipher.final 42 | end 43 | 44 | def aes_encrypt(text, opts = {}) 45 | key = opts[:key] || generateKey(opts[:keylen] || 24) 46 | cipher = OpenSSL::Cipher::AES.new(opts[:aeskeysize] || 256, opts[:mode] || :OFB).encrypt 47 | cipher.iv = iv = opts[:iv] || cipher.random_iv 48 | cipher.key = pbkdf2(key, iv, (opts[:aeskeysize] || 256) / 8, opts) 49 | text = cipher.update(text) + cipher.final 50 | [ iv + text, key ] 51 | end 52 | 53 | def generateKey(len = 24) 54 | chars = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' 55 | len.times.map { chars[SecureRandom.random_number(chars.length)].ord }.pack('c*') 56 | end 57 | 58 | def decrypt(text, key, opts = {}) 59 | text = Base64.decode64(text) 60 | aes_decrypt(text, key, opts) 61 | end 62 | 63 | def encrypt(text, opts = {}) 64 | text, key = aes_encrypt(text, opts) 65 | [ Base64.encode64(text).gsub(/\s+/, ""), key ] 66 | end 67 | 68 | def fixipv6host(host) 69 | if m = /^\[([0-9a-fA-F:]+)\]$/.match(host) 70 | return m[1] 71 | end 72 | host 73 | end 74 | 75 | def http_get(uri) 76 | request = Net::HTTP::Get.new uri.request_uri 77 | request['Host'] = uri.host 78 | http = Net::HTTP.new(fixipv6host(uri.host), uri.port) 79 | http.use_ssl = uri.scheme == 'https' 80 | # http.verify_mode = OpenSSL::SSL::VERIFY_PEER 81 | # http.verify_depth = 5 82 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 83 | http.start { |http| http.request request } 84 | end 85 | 86 | def http_post(uri, args) 87 | request = Net::HTTP::Post.new(uri.request_uri) 88 | request['Host'] = uri.host 89 | request.set_form_data(args) 90 | http = Net::HTTP.new(fixipv6host(uri.host), uri.port) 91 | http.use_ssl = uri.scheme == 'https' 92 | # http.verify_mode = OpenSSL::SSL::VERIFY_PEER 93 | # http.verify_depth = 5 94 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 95 | http.start { |http| http.request request } 96 | end 97 | 98 | def hashpw(password) 99 | return nil if password.nil? 100 | return OpenSSL::Digest::SHA1.hexdigest(password) 101 | end 102 | 103 | require 'optparse' 104 | opts = {} 105 | OptionParser.new do |o| 106 | o.on('-u', '--url URL', "Retrieve paste from url (conflicts with the posting options)") { |url| opts[:url] = URI(url) } 107 | o.on('-f', '--file FILENAME', "Upload file") { |fn| opts[:fn] = fn } 108 | o.on('-m', '--mime MIMETYPE', "Specify mime type for paste") { |mime| opts[:mime] = mime } 109 | o.on('-t', '--ttl TTL', "Specify Time-To-Live for paste in seconds, default one week (-1 for indefinately, -100 for one time only)") { |ttl| opts[:ttl] = ttl } 110 | o.on('-p', '--password[PASSWORD]', "Use password protection on server side (no additional encryption)") { |password| 111 | opts[:pass] = true 112 | opts[:password] = password unless password.nil? 113 | } 114 | o.on('-s', '--site SITE', "Post upload to another ncrypt pastebin (default: https://ncry.pt)") { |s| opts[:site] = s } 115 | o.separator "" 116 | o.separator " If neither url nor filename was given, a final parameter can be used to specify it. Urls are autodetected." 117 | o.separator "" 118 | o.on('-h', '--help', "Show this help") { STDERR.puts o; exit } 119 | o.parse! 120 | if ARGV.length == 1 121 | if opts[:url] || opts[:fn] 122 | STDERR.puts o 123 | exit 1 124 | end 125 | begin 126 | url = URI(ARGV[0]) 127 | if ("http" == url.scheme || "https" == url.scheme) && url.host 128 | opts[:url] = url 129 | else 130 | opts[:fn] = ARGV[0] 131 | end 132 | rescue 133 | opts[:fn] = ARGV[0] 134 | end 135 | end 136 | if ARGV.length > 1 or (opts[:url] and (opts[:fn] || opts[:ttl] || opts[:mime] || opts[:site] || opts[:pass])) or (!opts[:url] and !opts[:fn]) 137 | STDERR.puts o 138 | exit 1 139 | end 140 | end 141 | 142 | if opts[:pass] and opts[:password].nil? 143 | if opts[:fn] == '-' 144 | STDERR.puts "Can't read post data from stdin and prompt for password" 145 | exit 1 146 | end 147 | STDERR.write "Enter password: " 148 | STDERR.flush 149 | opts[:password] = STDIN.readline.chomp 150 | end 151 | 152 | if opts[:url] 153 | uri = opts[:url] 154 | if !uri.fragment 155 | STDERR.puts "Specified url has no fragment, cannot decode paste" 156 | exit 1 157 | end 158 | 159 | password = nil 160 | while 161 | if password.nil? 162 | resp = http_get(uri) 163 | else 164 | resp = http_post(uri, :p => hashpw(password)) 165 | end 166 | 167 | if 200 != resp.code.to_i 168 | STDERR.puts "Got HTTP/#{resp.http_version} #{resp.code} #{resp.message}" 169 | STDERR.puts "Location: #{resp['Location']}" if resp['Location'] 170 | exit 1 171 | end 172 | html = resp.body 173 | 174 | if m = (// and password.nil? 179 | STDERR.write "Paste is password protected. Enter password: " 180 | STDERR.flush 181 | password = STDIN.readline.chomp 182 | else 183 | STDERR.puts "Can't parse response." 184 | exit 1 185 | end 186 | end 187 | 188 | else 189 | uri = URI(opts[:site] || 'https://ncry.pt') 190 | if opts[:fn] != '-' 191 | if opts[:mime].nil? 192 | begin 193 | opts[:mime] = IO.popen("file --brief --mime-type '#{opts[:fn]}'", "r").read.chomp 194 | rescue 195 | # use default mime type text/plain 196 | end 197 | end 198 | text = File.open(opts[:fn], "rb").read 199 | else 200 | text = STDIN.read 201 | end 202 | 203 | cipher, key = encrypt(text) 204 | resp = http_post(uri, :data => cipher, :syn => opts[:mime] || 'text/plain', :ttl => opts[:ttl] || (7*86400), :p => hashpw(opts[:password])) 205 | if 200 != resp.code.to_i 206 | STDERR.puts "Key is #{key}" 207 | STDERR.puts "Got HTTP/#{resp.http_version} #{resp.code} #{resp.message}" 208 | resp.each_header { |k,v| STDERR.puts "#{k}: #{v}" } 209 | STDERR.puts resp.body 210 | exit 1 211 | end 212 | html = resp.body 213 | if html =~ /^\{"id":".*"\}\s*$/m then 214 | id = html.gsub(/^\{"id":"/m, "").gsub(/"\}\s*$/m, "") 215 | uri.path += '/' unless ?/ == uri.path[-1] 216 | uri.path += id 217 | uri.fragment = key 218 | puts uri 219 | else 220 | STDERR.puts "Key is #{key}" 221 | STDERR.puts "Can't parse response #{resp.body}" 222 | exit 1 223 | end 224 | end 225 | -------------------------------------------------------------------------------- /source/default.css: -------------------------------------------------------------------------------- 1 | input[type="checkbox"], input[type="radio"] 2 | { 3 | border: none; 4 | background: none; 5 | vertical-align: middle; 6 | margin: 3px 2px; 7 | } 8 | 9 | input[type="submit"], input[type="button"] 10 | { 11 | -moz-box-shadow: INSET 1px 1px 0px #f5f5f5, INSET -1px -1px 0px #c9c9c9; 12 | -webkit-box-shadow: INSET 1px 1px 0px #f5f5f5, INSET -1px -1px 0px #c9c9c9; 13 | box-shadow: INSET 1px 1px 0px #f5f5f5, INSET -1px -1px 0px #c9c9c9; 14 | 15 | background: -webkit-gradient( linear, left top, left bottom, color-stop( 0.05, #ededed ), color-stop( 1, #dfdfdf ) ); 16 | background: -moz-linear-gradient( center top, #ededed 5%, #dfdfdf 100% ); 17 | background: -o-linear-gradient( rgb( 237, 237, 237 ), rgb( 223, 223, 223 ) ); 18 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ededed', endColorstr='#dfdfdf' ); 19 | background-color: #ededed; 20 | 21 | border-width: 1px; 22 | border-style: SOLID; 23 | border-left-color: #dedede; 24 | border-top-color: #dedede; 25 | border-right-color: #777777; 26 | border-bottom-color: #777777; 27 | 28 | color: #4d4d4d; 29 | font-family: inherit; 30 | font-size: 115%; 31 | text-decoration: none; 32 | text-shadow: 1px 1px 0px #ffffff; 33 | 34 | height: 29px; 35 | padding-left: 8px; 36 | padding-right: 8px; 37 | } 38 | 39 | input[type="submit"]:hover, input[type="button"]:hover 40 | { 41 | background: -webkit-gradient( linear, left top, left bottom, color-stop(0.05, #e4e4e4), color-stop(1, #dbdbdb) ); 42 | background: -moz-linear-gradient( center top, #e4e4e4 5%, #dbdbdb 100% ); 43 | background: -o-linear-gradient( rgb( 228, 228, 228 ), rgb( 219, 219, 219 ) ); 44 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e4e4e4', endColorstr='#dbdbdb'); 45 | background-color: #e4e4e4; 46 | 47 | color: #282828; 48 | } 49 | 50 | input[type="submit"]:active, input[type="button"]:active 51 | { 52 | position: relative; 53 | top: 1px; 54 | left: 1px; 55 | 56 | -moz-box-shadow: INSET 1px 1px 0px #c9c9c9, INSET -1px -1px 0px #e8e8e8; 57 | -webkit-box-shadow: INSET 1px 1px 0px #c9c9c9, INSET -1px -1px 0px #e8e8e8; 58 | box-shadow: INSET 1px 1px 0px #c9c9c9, INSET -1px -1px 0px #e8e8e8; 59 | 60 | border-width: 1px; 61 | border-style: SOLID; 62 | border-left-color: #777777; 63 | border-top-color: #777777; 64 | border-right-color: #dedede; 65 | border-bottom-color: #dedede; 66 | } 67 | 68 | input[type="text"], input[type="password"] 69 | { 70 | background: -webkit-gradient( linear, left top, left bottom, color-stop(0.05, #e1e1e1), color-stop(1, #f6f6f6) ); 71 | background: -moz-linear-gradient( center top, #e1e1e1 5%, #f6f6f6 100% ); 72 | background: -o-linear-gradient( top, rgb( 225, 225, 225 ), rgb( 246, 246, 246 ) ); 73 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e1e1e1', endColorstr='#f6f6f6'); 74 | background-color: #f6f6f6; 75 | 76 | border: 1px SOLID #d1c7ac; 77 | color: #333333; 78 | padding: 2px; 79 | margin: 0px; 80 | } 81 | 82 | textarea 83 | { 84 | background: -webkit-gradient( linear, left top, left bottom, color-stop(0.05, #e1e1e1), color-stop(1, #f6f6f6) ); 85 | background: -moz-linear-gradient( center top, #e1e1e1 5%, #f6f6f6 100% ); 86 | background: -o-linear-gradient( top, rgb( 225, 225, 225 ), rgb( 246, 246, 246 ) ); 87 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e1e1e1', endColorstr='#f6f6f6'); 88 | background-color: #f6f6f6; 89 | 90 | border: 1px SOLID #d1c7ac; 91 | color: #333333; 92 | } 93 | 94 | #holder 95 | { 96 | width: 920px; 97 | } 98 | 99 | #header 100 | { 101 | height: 35px; 102 | padding-top: 15px; 103 | } 104 | 105 | #menu 106 | { 107 | position: relative; 108 | text-align: center; 109 | padding: 7px 0px; 110 | border-bottom: 1px SOLID #f2f2f2; 111 | margin-bottom: 10px; 112 | } 113 | 114 | #menu a:not(:last-child) 115 | { 116 | background-color: #d7e5f5; 117 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #d7e5f5), color-stop(100%, #cbe0f5)); 118 | background-image: -webkit-linear-gradient(top, #d7e5f5, #cbe0f5); 119 | background-image: -moz-linear-gradient(top, #d7e5f5, #cbe0f5); 120 | background-image: -ms-linear-gradient(top, #d7e5f5, #cbe0f5); 121 | background-image: -o-linear-gradient(top, #d7e5f5, #cbe0f5); 122 | background-image: linear-gradient(top, #d7e5f5, #cbe0f5); 123 | border-top: 1px solid #abbbcc; 124 | border-left: 1px solid #a7b6c7; 125 | border-bottom: 1px solid #a1afbf; 126 | border-right: 1px solid #a7b6c7; 127 | -webkit-border-radius: 3px; 128 | -moz-border-radius: 3px; 129 | border-radius: 3px; 130 | -webkit-box-shadow: inset 0 1px 0 0 white; 131 | -moz-box-shadow: inset 0 1px 0 0 white; 132 | box-shadow: inset 0 1px 0 0 white; 133 | color: #1a3e66; 134 | font: normal 11px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; 135 | line-height: 1; 136 | padding: 1px 5px; 137 | text-align: center; 138 | text-shadow: 0 1px 1px #fff; 139 | width: 150px; 140 | } 141 | 142 | #menu a:not(:last-child):hover 143 | { 144 | background-color: #ccd9e8; 145 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ccd9e8), color-stop(100%, #c1d4e8)); 146 | background-image: -webkit-linear-gradient(top, #ccd9e8, #c1d4e8); 147 | background-image: -moz-linear-gradient(top, #ccd9e8, #c1d4e8); 148 | background-image: -ms-linear-gradient(top, #ccd9e8, #c1d4e8); 149 | background-image: -o-linear-gradient(top, #ccd9e8, #c1d4e8); 150 | background-image: linear-gradient(top, #ccd9e8, #c1d4e8); 151 | border-top: 1px solid #a1afbf; 152 | border-left: 1px solid #9caaba; 153 | border-bottom: 1px solid #96a3b3; 154 | border-right: 1px solid #9caaba; 155 | -webkit-box-shadow: inset 0 1px 0 0 #f2f2f2; 156 | -moz-box-shadow: inset 0 1px 0 0 #f2f2f2; 157 | box-shadow: inset 0 1px 0 0 #f2f2f2; 158 | color: #163659; 159 | cursor: pointer; 160 | } 161 | 162 | #menu a:not(:last-child):active 163 | { 164 | border: 1px solid #8c98a7; 165 | -webkit-box-shadow: inset 0 0 4px 2px #abbccf, 0 0 1px 0 #eeeeee; 166 | -moz-box-shadow: inset 0 0 4px 2px #abbccf, 0 0 1px 0 #eeeeee; 167 | box-shadow: inset 0 0 4px 2px #abbccf, 0 0 1px 0 #eeeeee; 168 | } 169 | 170 | #options 171 | { 172 | margin-top: 5px; 173 | } 174 | 175 | #decrypting 176 | { 177 | width: 875px; 178 | } 179 | 180 | #new_result 181 | { 182 | width: 910px; 183 | } 184 | 185 | 186 | /* #overlay/#popup */ 187 | #overlay 188 | { 189 | display: none; 190 | position: fixed; 191 | left: 0px; 192 | top: 0px; 193 | width: 100%; 194 | height: 100%; 195 | background: rgb(0, 0, 0); 196 | background: rgba(0, 0, 0, 0.6); 197 | background: transparent !ie; /* clear solid background for ie */ 198 | zoom: 1; /* required for the filters */ 199 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#33000000, endColorstr=#33000000); 200 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#33000000, endColorstr=#33000000)"; 201 | z-index: 99; 202 | } 203 | 204 | #popup 205 | { 206 | font-family: Verdana, Geneva, Arial, sans-serif; 207 | background-color: #FFF; 208 | color: #000; 209 | margin-left: -260px; 210 | margin-top: -70px; 211 | position: absolute; 212 | top: 50%; 213 | left: 50%; 214 | width: 550px; 215 | height: 120px; 216 | visibility: visible; 217 | z-index: 999; 218 | 219 | padding: 10px; 220 | 221 | -moz-box-shadow: 0 0 10px #000; 222 | -webkit-box-shadow: 0 0 10px #000; 223 | box-shadow: 0 0 10px #000; 224 | 225 | -webkit-border-radius: 3px; 226 | -moz-border-radius: 3px; 227 | border-radius: 3px; 228 | } 229 | 230 | #popup .success 231 | { 232 | font-weight: bold; 233 | font-size: 18px; 234 | text-align: center; 235 | } 236 | 237 | #popup .url 238 | { 239 | width: 538px; 240 | padding: 5px; 241 | font-size: 16px; 242 | } 243 | 244 | #popup .close 245 | { 246 | color: #777; 247 | font: 14px/100% arial, sans-serif; 248 | position: absolute; 249 | right: 5px; 250 | text-decoration: none; 251 | text-shadow: 0 1px 0 #fff; 252 | top: 5px; 253 | } 254 | 255 | #popup .close:after 256 | { 257 | content: '✖'; /* UTF-8 symbol */ 258 | } 259 | -------------------------------------------------------------------------------- /source/common.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | 3 | html, body 4 | { 5 | margin: 0px; 6 | padding: 0px; 7 | height: 100%; 8 | } 9 | 10 | body 11 | { 12 | background-color: #636363; 13 | font-family: 'Trebuchet MS', Helvetica, sans-serif; 14 | font-size: 13px; 15 | line-height: 20px; 16 | height: 100%; 17 | min-height: 100%; 18 | margin: 0px; 19 | position: relative; 20 | } 21 | 22 | a 23 | { 24 | text-decoration: none; 25 | color: #27377d; 26 | } 27 | 28 | a:hover 29 | { 30 | text-decoration: underline; 31 | } 32 | 33 | #holder 34 | { 35 | position: relative; 36 | margin: 0px auto -35px auto; 37 | min-height: 100%; 38 | height: auto !important; 39 | height: 100%; 40 | 41 | /* image preloader */ 42 | background: url(../img/padlock.png) no-repeat -9999px -9999px; 43 | /* end image preloader */ 44 | 45 | background-color: #b7b7b7; 46 | -moz-box-shadow: 0 0 3px 3px #b7b7b7; 47 | -webkit-box-shadow: 0 0 3px 3px #b7b7b7; 48 | box-shadow: 0 0 3px 3px #b7b7b7; 49 | } 50 | 51 | #header, #footer 52 | { 53 | background: #b7b7b7; /* Old browsers */ 54 | /* IE9 SVG, needs conditional override of 'filter' to 'none' */ 55 | background: url(../img/gradient.svg); 56 | background: -moz-linear-gradient(left, #b7b7b7 0%, #dcdcdc 10%, #dcdcdc 90%, #b7b7b7 100%); /* FF3.6+ */ 57 | background: -webkit-gradient(linear, left top, right top, color-stop(0%,#b7b7b7), color-stop(10%,#dcdcdc), color-stop(90%,#dcdcdc), color-stop(100%,#b7b7b7)); /* Chrome,Safari4+ */ 58 | background: -webkit-linear-gradient(left, #b7b7b7 0%,#dcdcdc 10%,#dcdcdc 90%,#b7b7b7 100%); /* Chrome10+,Safari5.1+ */ 59 | background: -o-linear-gradient(left, #b7b7b7 0%,#dcdcdc 10%,#dcdcdc 90%,#b7b7b7 100%); /* Opera 11.10+ */ 60 | background: -ms-linear-gradient(left, #b7b7b7 0%,#dcdcdc 10%,#dcdcdc 90%,#b7b7b7 100%); /* IE10+ */ 61 | background: linear-gradient(left, #b7b7b7 0%,#dcdcdc 10%,#dcdcdc 90%,#b7b7b7 100%); /* W3C */ 62 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b7b7b7', endColorstr='#b7b7b7',GradientType=1 ); /* IE6-8 */ 63 | } 64 | 65 | #header 66 | { 67 | font-size: 18px; 68 | text-align: center; 69 | color: #000; 70 | } 71 | 72 | .small 73 | { 74 | font-size: 8px; 75 | color: #666; 76 | } 77 | 78 | #main 79 | { 80 | padding: 5px; 81 | } 82 | 83 | #new_preview 84 | { 85 | display: none; 86 | position: absolute; 87 | top: 0px; 88 | left: 0px; 89 | border: 1px SOLID #c1e9c3 !important; 90 | -moz-box-shadow: inset 0 0 10px 10px #d6f4d8; 91 | -webkit-box-shadow: inset 0 0 10px 10px #d6f4d8; 92 | box-shadow: inset 0 0 10px 10px #d6f4d8; 93 | background: #f9fff9 url(../img/padlock.png) 100% 2px no-repeat; 94 | color: #909090; 95 | overflow: hidden; 96 | } 97 | 98 | #askpassword 99 | { 100 | display: none; 101 | padding-top: 25px; 102 | margin: 0px auto; 103 | text-align: center; 104 | } 105 | 106 | #insertkey 107 | { 108 | display: none; 109 | padding-top: 25px; 110 | margin: 0px auto; 111 | text-align: center; 112 | } 113 | 114 | #newpaste 115 | { 116 | display: none; 117 | padding-bottom: 20px; 118 | } 119 | 120 | #wrapholder 121 | { 122 | height: 25px; 123 | display: none; 124 | } 125 | 126 | #wrapholder a 127 | { 128 | background-color: #7fbf4d; 129 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #7fbf4d), color-stop(100%, #63a62f)); 130 | background-image: -webkit-linear-gradient(top, #7fbf4d, #63a62f); 131 | background-image: -moz-linear-gradient(top, #7fbf4d, #63a62f); 132 | background-image: -ms-linear-gradient(top, #7fbf4d, #63a62f); 133 | background-image: -o-linear-gradient(top, #7fbf4d, #63a62f); 134 | background-image: linear-gradient(top, #7fbf4d, #63a62f); 135 | border: 1px solid #63a62f; 136 | border-bottom: 1px solid #5b992b; 137 | -webkit-border-radius: 3px; 138 | -moz-border-radius: 3px; 139 | border-radius: 3px; 140 | -webkit-box-shadow: inset 0 1px 0 0 #96ca6d; 141 | -moz-box-shadow: inset 0 1px 0 0 #96ca6d; 142 | box-shadow: inset 0 1px 0 0 #96ca6d; 143 | color: #FFF; 144 | font: normal 11px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; 145 | line-height: 1; 146 | padding: 1px 5px; 147 | text-align: center; 148 | text-shadow: 0 1px 1px #4c9021; 149 | width: 150px; 150 | } 151 | 152 | #wrapholder a:hover 153 | { 154 | background-color: #76b347; 155 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #76b347), color-stop(100%, #5e9e2e)); 156 | background-image: -webkit-linear-gradient(top, #76b347, #5e9e2e); 157 | background-image: -moz-linear-gradient(top, #76b347, #5e9e2e); 158 | background-image: -ms-linear-gradient(top, #76b347, #5e9e2e); 159 | background-image: -o-linear-gradient(top, #76b347, #5e9e2e); 160 | background-image: linear-gradient(top, #76b347, #5e9e2e); 161 | -webkit-box-shadow: inset 0 1px 0 0 #8dbf67; 162 | -moz-box-shadow: inset 0 1px 0 0 #8dbf67; 163 | box-shadow: inset 0 1px 0 0 #8dbf67; 164 | cursor: pointer; 165 | } 166 | 167 | #wrapholder a:active 168 | { 169 | border: 1px solid #5b992b; 170 | border-bottom: 1px solid #538c27; 171 | -webkit-box-shadow: inset 0 0 4px 2px #548c29, 0 0 1px 0 #eeeeee; 172 | -moz-box-shadow: inset 0 0 4px 2px #548c29, 0 0 1px 0 #eeeeee; 173 | box-shadow: inset 0 0 4px 2px #548c29, 0 0 1px 0 #eeeeee; 174 | } 175 | 176 | select#theme-select 177 | { 178 | float: right; 179 | } 180 | 181 | input.tool-button 182 | { 183 | display: none; 184 | } 185 | 186 | input.tool-button + label 187 | { 188 | display: block; 189 | float: right; 190 | width: 20px; 191 | height: 18px; 192 | margin-top: 2px; 193 | margin-left: 2px; 194 | border-width: 1px; 195 | border-style: SOLID; 196 | border-left-color: #dedede; 197 | border-top-color: #dedede; 198 | border-right-color: #777777; 199 | border-bottom-color: #777777; 200 | 201 | background-image: url(../img/toolbar.png); 202 | background-color: #FFF; 203 | background-repeat: no-repeat; 204 | } 205 | 206 | input.tool-button:checked + label 207 | { 208 | border-left-color: #777777 !important; 209 | border-top-color: #777777 !important; 210 | border-right-color: #dedede !important; 211 | border-bottom-color: #dedede !important; 212 | } 213 | 214 | #tool-numbers + label 215 | { 216 | background-position: -20px -0px; 217 | } 218 | #tool-numbers:checked + label 219 | { 220 | background-position: -20px -18px; 221 | } 222 | 223 | #tool-wrap + label 224 | { 225 | background-position: -40px -0px; 226 | } 227 | #tool-wrap:checked + label 228 | { 229 | background-position: -40px -18px; 230 | } 231 | 232 | #tool-fullscreen + label 233 | { 234 | background-position: -0px -0px; 235 | } 236 | #tool-fullscreen:checked + label 237 | { 238 | background-position: -0px -18px; 239 | } 240 | 241 | #new_encrypttime 242 | { 243 | display: none; 244 | position: absolute; 245 | right: 10px; 246 | bottom: 10px; 247 | height: 15px; 248 | font-size: 10px; 249 | color: #CECECE; 250 | } 251 | 252 | #new_encrypting 253 | { 254 | display: none; 255 | position: absolute; 256 | right: 10px; 257 | bottom: 5px; 258 | 259 | font-size: 1em; 260 | 261 | line-height: 1; 262 | padding: 0.125em 0 0.175em 0.55em; 263 | width: 2.75em; 264 | } 265 | 266 | #new_encrypting span 267 | { 268 | background: transparent; 269 | border-radius: 50%; 270 | box-shadow: inset 0 0 1px rgba( 0, 0, 0, 0.75 ); 271 | display: inline-block; 272 | height: 0.6em; 273 | width: 0.6em; 274 | margin-left: 2px; 275 | 276 | -webkit-animation: loading_dots 0.8s linear infinite; 277 | -moz-animation: loading_dots 0.8s linear infinite; 278 | -ms-animation: loading_dots 0.8s linear infinite; 279 | animation: loading_dots 0.8s linear infinite; 280 | } 281 | 282 | #new_encrypting span:nth-child(2) 283 | { 284 | -webkit-animation-delay: 0.2s; 285 | -moz-animation-delay: 0.2s; 286 | -ms-animation-delay: 0.2s; 287 | animation-delay: 0.2s; 288 | } 289 | #new_encrypting span:nth-child(1) 290 | { 291 | -webkit-animation-delay: 0.4s; 292 | -moz-animation-delay: 0.4s; 293 | -ms-animation-delay: 0.4s; 294 | animation-delay: 0.4s; 295 | } 296 | 297 | @-webkit-keyframes loading_dots 298 | { 299 | 0% { 300 | background: transparent; 301 | } 302 | 50% { 303 | background: #bdbdbd; 304 | } 305 | 100% { 306 | background: transparent; 307 | } 308 | } 309 | 310 | @-moz-keyframes loading_dots 311 | { 312 | 0% { 313 | background: transparent; 314 | } 315 | 50% { 316 | background: #bdbdbd; 317 | } 318 | 100% { 319 | background: transparent; 320 | } 321 | } 322 | @-ms-keyframes loading_dots 323 | { 324 | 0% { 325 | background: transparent; 326 | } 327 | 50% { 328 | background: #bdbdbd; 329 | } 330 | 100% { 331 | background: transparent; 332 | } 333 | } 334 | @keyframes loading_dots 335 | { 336 | 0% { 337 | background: transparent; 338 | } 339 | 50% { 340 | background: #bdbdbd; 341 | } 342 | 100% { 343 | background: transparent; 344 | } 345 | } 346 | 347 | #speed 348 | { 349 | float: right; 350 | height: 15px; 351 | font-size: 10px; 352 | color: #888; 353 | } 354 | 355 | #speed #execute, #speed #coloring, #speed #totaltime 356 | { 357 | float: right; 358 | padding-left: 5px; 359 | } 360 | 361 | #push 362 | { 363 | height: 40px; 364 | clear: both; 365 | } 366 | 367 | #footer 368 | { 369 | position: absolute; 370 | height: 25px; 371 | width: 100%; 372 | margin: 0px auto; 373 | padding-top: 10px; 374 | text-align: center; 375 | color: #000; 376 | bottom: 0px; 377 | } 378 | 379 | #syntax>.header 380 | { 381 | text-align: center; 382 | background-color: #94cff5; 383 | color: #000; 384 | } 385 | 386 | #decrypting 387 | { 388 | position: absolute; 389 | left: 40px; 390 | margin-top: 5px; 391 | z-index: 100; 392 | height: 50px; 393 | background: url(../img/init.gif) top left no-repeat; 394 | } 395 | 396 | #decrypting #download-progress 397 | { 398 | display: none; 399 | margin-top: 25px; 400 | } 401 | 402 | #upload-paste 403 | { 404 | display: none; 405 | margin: 0; 406 | position: absolute; 407 | padding: 10px; 408 | border-radius: 5px; 409 | top: 50%; 410 | left: 50%; 411 | text-align: center; 412 | transform: translate(-50%, -50%); 413 | z-index: 150; 414 | height: 70px; 415 | background: #FFFFFF url(../img/uploading.gif) top center no-repeat; 416 | } 417 | 418 | #upload-paste #upload-progress 419 | { 420 | margin-top: 25px; 421 | } 422 | 423 | textarea 424 | { 425 | width: 100%; 426 | height: 400px; 427 | border: 1px SOLID #e0e0e0; 428 | padding: 0px; 429 | resize: none; 430 | outline: none; 431 | -webkit-appearance: none; 432 | } 433 | -------------------------------------------------------------------------------- /www/inc/mobile.class.php: -------------------------------------------------------------------------------- 1 | isMobile() or $detect->isTablet() 9 | * 10 | * For more specific usage see the documentation navigate to: 11 | * http://code.google.com/p/php-mobile-detect/wiki/Mobile_Detect 12 | * 13 | * @license http://www.opensource.org/licenses/mit-license.php The MIT License 14 | */ 15 | class Mobile 16 | { 17 | protected $detectionRules; 18 | protected $userAgent = null; 19 | protected $accept = null; 20 | 21 | // Assume the visitor has a desktop environment. 22 | protected $isMobile = false; 23 | protected $isTablet = false; 24 | protected $phoneDeviceName = null; 25 | protected $tabletDevicename = null; 26 | protected $operatingSystemName = null; 27 | protected $userAgentName = null; 28 | 29 | // List of mobile devices (phones) 30 | protected $phoneDevices = array( 31 | 'iPhone' => '(iPhone.*Mobile|iPod|iTunes)', 32 | 'BlackBerry' => 'BlackBerry|rim[0-9]+', 33 | 'HTC' => 'HTC|HTC.*(6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090', 34 | 'Nexus' => 'Nexus One|Nexus S', 35 | 'DellStreak' => 'Dell Streak', 36 | 'Motorola' => '\bDroid\b.*Build|HRI39|MOT\-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT909|XT910|XT912|XT928', 37 | 'Samsung' => 'Samsung|GT-I9100|GT-I9000|GT-I9020|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-LC11|SCH-N150|SCH-N300|SCH-R300|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-C207|SGH-C225|SGH-C417|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D807|SGH-E105|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E635|SGH-E715|SGH-I577|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I717|SGH-I727|SGH-I777|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-N105|SGH-N625|SGH-P107|SGH-P207|SGH-P735|SGH-P777|SGH-Q105|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T919|SGH-T929|SGH-T939|SGH-T939|SGH-T959|SGH-T989|SGH-V205|SGH-V206|SGH-X105|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-ZX10|SGH-ZX20|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500I|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100', 38 | 'Sony' => 'E10i|SonyEricsson|SonyEricssonLT15iv', 39 | 'Asus' => 'Asus.*Galaxy', 40 | 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino 41 | 'GenericPhone' => '(mmp|pocket|psp|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|wap|nokia|Series40|Series60|S60|SonyEricsson|N900|PPC;|MAUI.*WAP.*Browser|LG-P500)' 42 | ); 43 | 44 | // List of tablet devices. 45 | protected $tabletDevices = array( 46 | 'BlackBerryTablet' => 'PlayBook|RIM Tablet', 47 | 'iPad' => 'iPad|iPad.*Mobile', // @todo: check for mobile friendly emails topic. 48 | 'Kindle' => 'Kindle|Silk.*Accelerated', 49 | 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|GT-P1000|GT-P1010|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I777|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SGH-T989|SPH-D710|SPH-P100', 50 | 'HTCtablet' => 'HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200', 51 | 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617', 52 | 'AsusTablet' => 'Transformer|TF101', 53 | 'NookTablet' => 'NookColor|nook browser|BNTV250A|LogicPD Zoom2', 54 | 'AcerTablet' => 'Android.*(A100|A101|A200|A500|A501|A510|W500|W500P|W501|W501P)', 55 | 'YarvikTablet' => 'Android.*(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468)', 56 | 'GenericTablet' => 'Tablet(?!.*PC)|ViewPad7|LG-V909|MID7015|BNTV250A|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b' 57 | ); 58 | 59 | // List of mobile Operating Systems. 60 | protected $operatingSystems = array( 61 | 'AndroidOS' => '(android.*mobile|android(?!.*mobile))', 62 | 'BlackBerryOS' => '(blackberry|rim tablet os)', 63 | 'PalmOS' => '(avantgo|blazer|elaine|hiptop|palm|plucker|xiino)', 64 | 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|\bS60\b', 65 | 'WindowsMobileOS' => 'IEMobile|Windows Phone|Windows CE.*(PPC|Smartphone)|MSIEMobile|Window Mobile|XBLWP7', 66 | 'iOS' => '(iphone|ipod|ipad)', 67 | 'FlashLiteOS' => '', 68 | 'JavaOS' => '', 69 | 'NokiaOS' => '', 70 | 'webOS' => '', 71 | 'badaOS' => '\bBada\b', 72 | 'BREWOS' => '' 73 | ); 74 | 75 | // List of mobile User Agents. 76 | protected $userAgents = array( 77 | 'Chrome' => '\bCrMo\b|Chrome\/[.0-9]* Mobile', 78 | 'Dolfin' => '\bDolfin\b', 79 | 'Opera' => 'Opera.*Mini|Opera.*Mobi', 80 | 'Skyfire' => 'skyfire', 81 | 'IE' => 'IEMobile', 82 | 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile', 83 | 'Bolt' => 'bolt', 84 | 'TeaShark' => 'teashark', 85 | 'Blazer' => 'Blazer', 86 | 'Safari' => 'Mobile.*Safari|Safari.*Mobile', 87 | 'Midori' => 'midori', 88 | 'GenericBrowser' => 'NokiaBrowser|OviBrowser|SEMC.*Browser' 89 | ); 90 | 91 | function __construct( ) 92 | { 93 | // Merge all rules together. 94 | $this->detectionRules = array_merge( $this->phoneDevices, $this->tabletDevices, $this->operatingSystems, $this->userAgents ); 95 | $this->userAgent = SERVER('HTTP_USER_AGENT'); 96 | $this->accept = SERVER('HTTP_ACCEPT'); 97 | if( 98 | isset( $_SERVER['HTTP_X_WAP_PROFILE'] ) || 99 | isset( $_SERVER['HTTP_X_WAP_CLIENTID'] ) || 100 | isset( $_SERVER['HTTP_WAP_CONNECTION'] ) || 101 | isset( $_SERVER['HTTP_PROFILE'] ) || 102 | isset( $_SERVER['HTTP_X_OPERAMINI_PHONE_UA'] ) || // Reported by Nokia devices (eg. C3) 103 | isset( $_SERVER['HTTP_X_NOKIA_IPADDRESS'] ) || 104 | isset( $_SERVER['HTTP_X_NOKIA_GATEWAY_ID'] ) || 105 | isset( $_SERVER['HTTP_X_ORANGE_ID'] ) || 106 | isset( $_SERVER['HTTP_X_VODAFONE_3GPDPCONTEXT'] ) || 107 | isset( $_SERVER['HTTP_X_HUAWEI_USERID'] ) || 108 | isset( $_SERVER['HTTP_UA_OS'] ) || // Reported by Windows Smartphones 109 | ( isset( $_SERVER['HTTP_UA_CPU'] ) && $_SERVER['HTTP_UA_CPU'] == 'ARM' ) ) // Seen this on a HTC 110 | { 111 | $this->isMobile = true; 112 | } 113 | elseif( ! empty( $this->accept ) && ( strpos( $this->accept, 'text/vnd.wap.wml' ) !== false || strpos( $this->accept, 'application/vnd.wap.xhtml+xml' ) !== false ) ) 114 | { 115 | $this->isMobile = true; 116 | } 117 | else 118 | { 119 | $this->_detect(); 120 | } 121 | } 122 | 123 | public function getRules( ) 124 | { 125 | return $this->detectionRules; 126 | } 127 | 128 | /** 129 | * Magic overloading method. 130 | * 131 | * @method boolean is[...]() 132 | * @param string $name 133 | * @param array $arguments 134 | * @return mixed 135 | */ 136 | public function __call( $name, $arguments ) 137 | { 138 | $key = substr( $name, 2 ); 139 | return $this->_detect( $key ); 140 | } 141 | 142 | /** 143 | * Private method that does the detection of the 144 | * mobile devices. 145 | * 146 | * @param type $key 147 | * @return boolean|null 148 | */ 149 | private function _detect( $key = '' ) 150 | { 151 | if( empty( $key ) ) 152 | { 153 | // Begin general search. 154 | foreach( $this->detectionRules as $_regex ) 155 | { 156 | if( empty( $_regex ) ) 157 | { 158 | continue; 159 | } 160 | if( preg_match( '/' . $_regex . '/is', $this->userAgent ) ) 161 | { 162 | $this->isMobile = true; 163 | return true; 164 | } 165 | } 166 | return false; 167 | } 168 | else 169 | { 170 | // Search for a certain key. 171 | // Make the keys lowecase so we can match: isIphone(), isiPhone(), isiphone(), etc. 172 | $key = strtolower( $key ); 173 | $_rules = array_change_key_case( $this->detectionRules ); 174 | if( array_key_exists( $key, $_rules ) ) 175 | { 176 | if( empty( $_rules[$key] ) ) 177 | { 178 | return null; 179 | } 180 | if( preg_match( '/' . $_rules[$key] . '/is', $this->userAgent ) ) 181 | { 182 | $this->isMobile = true; 183 | return true; 184 | } 185 | else 186 | { 187 | return false; 188 | } 189 | } 190 | else 191 | { 192 | trigger_error( "Method $key is not defined", E_USER_WARNING ); 193 | } 194 | return false; 195 | } 196 | } 197 | 198 | /** 199 | * Check if the device is mobile. 200 | * Returns true if any type of mobile device detected, including special ones 201 | * @return bool 202 | */ 203 | public function isMobile( ) 204 | { 205 | return $this->isMobile; 206 | } 207 | 208 | /** 209 | * Check if the device is a tablet. 210 | * Return true if any type of tablet device is detected. 211 | * @return boolean 212 | */ 213 | public function isTablet( ) 214 | { 215 | foreach( $this->tabletDevices as $_regex ) 216 | { 217 | if( preg_match( '/' . $_regex . '/is', $this->userAgent ) ) 218 | { 219 | $this->isTablet = true; 220 | return true; 221 | } 222 | } 223 | return false; 224 | } 225 | } -------------------------------------------------------------------------------- /www/jslibs/head-1.0.2-1.min.js: -------------------------------------------------------------------------------- 1 | (function(win,undefined){"use strict";var doc=win.document,nav=win.navigator,loc=win.location,html=doc.documentElement,klass=[],conf={screens:[240,320,480,640,768,800,1024,1280,1440,1680,1920],screensCss:{gt:true,gte:false,lt:true,lte:false,eq:false},browsers:[{ie:{min:6,max:11}}],browserCss:{gt:true,gte:false,lt:true,lte:false,eq:true},html5:true,page:"-page",section:"-section",head:"head"};if(win.head_conf){for(var item in win.head_conf){if(win.head_conf[item]!==undefined){conf[item]=win.head_conf[item]}}}function pushClass(name){klass[klass.length]=name}function removeClass(name){var re=new RegExp(" ?\\b"+name+"\\b");html.className=html.className.replace(re,"")}function each(arr,fn){for(var i=0,l=arr.length;iv){if(conf.browserCss.gt){pushClass("gt-"+key+v)}if(conf.browserCss.gte){pushClass("gte-"+key+v)}}else if(version2&&this[i+1]!==undefined){if(i){pushClass(this.slice(i,i+1).join("-").toLowerCase()+conf.section)}}else{var id=el||"index",index=id.indexOf(".");if(index>0){id=id.substring(0,index)}html.id=id.toLowerCase()+conf.page;if(!i){pushClass("root"+conf.section)}}});api.screen={height:win.screen.height,width:win.screen.width};function screenSize(){html.className=html.className.replace(/ (w-|eq-|gt-|gte-|lt-|lte-|portrait|no-portrait|landscape|no-landscape)\d+/g,"");var iw=win.innerWidth||html.clientWidth,ow=win.outerWidth||win.screen.width;api.screen.innerWidth=iw;api.screen.outerWidth=ow;pushClass("w-"+iw);each(conf.screens,function(width){if(iw>width){if(conf.screensCss.gt){pushClass("gt-"+width)}if(conf.screensCss.gte){pushClass("gte-"+width)}}else if(iwiw);api.feature("landscape",ih1},fontface:function(){var browser=api.browser.name,version=api.browser.version;switch(browser){case"ie":return version>=9;case"chrome":return version>=13;case"ff":return version>=6;case"ios":return version>=5;case"android":return false;case"webkit":return version>=5.1;case"opera":return version>=10;default:return false}}};for(var key in tests){if(tests[key]){api.feature(key,tests[key].call(),true)}}api.feature()})(window);(function(win,undefined){"use strict";var doc=win.document,domWaiters=[],queue=[],handlers={},assets={},isAsync="async"in doc.createElement("script")||"MozAppearance"in doc.documentElement.style||win.opera,isHeadReady,isDomReady,headVar=win.head_conf&&win.head_conf.head||"head",api=win[headVar]=win[headVar]||function(){api.ready.apply(null,arguments)},PRELOADING=1,PRELOADED=2,LOADING=3,LOADED=4;function noop(){}function each(arr,callback){if(!arr){return}if(typeof arr==="object"){arr=[].slice.call(arr)}for(var i=0,l=arr.length;i= thresh && u < units.length - 1 ); 129 | 130 | return bytes.toFixed( 2 ) + ' ' + units[u]; 131 | } 132 | 133 | function formatPercent( current, total ) 134 | { 135 | return ( current / total * 100 ).toFixed( 2 ); 136 | } 137 | 138 | // determine duration taken to render the syntax highlighter 139 | function onCodeChange() 140 | { 141 | if( null != timer_decrypted ) 142 | { 143 | var coloring = timer_decrypted.getDiff(); 144 | // display duration 145 | $( '#coloring' ).html( 'syntax: ' + coloring + 'ms,'); 146 | 147 | // if we have hit this point it means we have finished, display total time taken 148 | var total = ( time_decryption + coloring ); 149 | $( '#totaltime' ).html( 'total: ' + total + 'ms'); 150 | } 151 | } 152 | 153 | function display_binary_hex( bytes, syntax, limit ) 154 | { 155 | if( !limit ) limit = 100 * 1024; 156 | var offprefix = bytes.length.toString( 16 ).replace( /./g, '0' ) 157 | var BYTES_PER_LINE = 20; 158 | var lines = [], bline, offset = 0, line, i, j; 159 | 160 | lines.push( 'Binary file of type: ' + syntax + "\n" ); 161 | lines.push( "\n" ); 162 | 163 | while( offset < bytes.length ) 164 | { 165 | if( limit >= 0 && offset >= limit ) 166 | { 167 | lines.push( "\n" ); 168 | lines.push( "too much data, stopping now\n" ); 169 | break; 170 | } 171 | 172 | bline = bytes.slice( offset, offset + BYTES_PER_LINE ); 173 | line = ( offprefix + offset.toString( 16 ) ).slice( -offprefix.length ) + ':'; 174 | offset += bline.length; 175 | 176 | for( i = 0; i < BYTES_PER_LINE; ) 177 | { 178 | line += ' '; 179 | for( j = 0; j < 4; ++j, ++i ) 180 | { 181 | if( i < bline.length ) 182 | { 183 | line += ( '00' + bline[i].toString( 16 ) ).slice( -2 ) + ' '; 184 | } 185 | else 186 | { 187 | line += ' '; 188 | } 189 | } 190 | } 191 | 192 | line += '| '; 193 | 194 | // replace non printable characters (in unicode <= 0xff) with '.'; each byte is treated as a separate unicode codepoint 195 | line += String.fromCharCode.apply( String, bline ).replace( /[\0-\x1F\x7F-\x9F\xAD]/g, '.' ) 196 | line += "\n"; 197 | lines.push( line ); 198 | } 199 | return lines.join( '' ); 200 | } 201 | 202 | function select_theme( theme ) 203 | { 204 | if( 'undefined' === typeof theme ) 205 | { 206 | theme = document.getElementById("select").options[document.getElementById("select").selectedIndex].innerHTML; 207 | } 208 | 209 | window.editor.setOption( 'theme', theme ); 210 | _cookies.setItem( 'theme', theme, 31536e3, '/' ); 211 | } 212 | 213 | function set_new_syntax( syntax ) 214 | { 215 | var s = $( '#new_syntax' ); 216 | if( s.val() === syntax ) return; 217 | s.val( syntax ); 218 | if( s.val() === syntax ) return; 219 | if ( $( '#new_syntax .header.unknown' ).length == 0 ) 220 | { 221 | s.append( $( '' ) ); 222 | } 223 | var o = $( '