add debian building. add asset compile and embedding. other fixes.
This commit is contained in:
parent
8e6da6cd30
commit
db6d5afb09
5
Caddyfile
Normal file
5
Caddyfile
Normal file
|
@ -0,0 +1,5 @@
|
|||
localhost:2018
|
||||
|
||||
log / stderr "{common}"
|
||||
proxy /paste localhost:9010
|
||||
root assets/
|
39
Makefile
Normal file
39
Makefile
Normal file
|
@ -0,0 +1,39 @@
|
|||
ROUTE_ASSET=routes/bindata_assetfs.go
|
||||
ROUTE_FILES=public/index.html public/app.js public/paste.sh public/style.css
|
||||
|
||||
SOURCE=./*.go routes/*.go
|
||||
BINARY=paste
|
||||
|
||||
all: $(BINARY)
|
||||
|
||||
clean:
|
||||
rm -f $(BINARY) $(ROUTE_ASSET) $(ROUTE_FILES)
|
||||
|
||||
$(BINARY): $(SOURCE) $(ROUTE_ASSET)
|
||||
go build
|
||||
|
||||
$(ROUTE_ASSET): $(ROUTE_FILES)
|
||||
export PATH=$$GOPATH/bin:$$PATH; cd routes; go-bindata-assetfs -pkg routes -prefix ../ ../public/
|
||||
|
||||
public/index.html: assets/index.html assets/ui/*
|
||||
cd assets; \
|
||||
(sed '/INSERT_TEMPLATES/Q' index.html; \
|
||||
for f in ui/*.html; \
|
||||
do echo "<script id='$$f' type='text/ng-template'>"; \
|
||||
html-minifier --minify-css --collapse-whitespace $$f; \
|
||||
echo '</script>'; \
|
||||
done; \
|
||||
sed '1,/INSERT_TEMPLATES/d;/REMOVE_ASSET_START/,/REMOVE_ASSET_END/d' index.html) \
|
||||
| html-minifier --minify-css --collapse-whitespace > ../public/index.html
|
||||
public/app.js: assets/lib.js assets/app.js
|
||||
cat assets/lib.js assets/app.js | uglifyjs > public/app.js
|
||||
public/paste.sh: assets/paste.sh
|
||||
cp assets/paste.sh public/paste.sh
|
||||
public/style.css: assets/*.css
|
||||
cleancss assets/*.css > public/style.css
|
||||
|
||||
deploy:
|
||||
cd debian && make && make deploy
|
||||
|
||||
.PHONEY: clean deploy
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
var app = angular.module('souris-app', ['souris-common', 'hljs']).
|
||||
config(function ($routeProvider) {
|
||||
$routeProvider.
|
||||
when('/', {controller: CreateCtrl, templateUrl: 'create.html'}).
|
||||
when('/:id!:key', {controller: ViewCtrl, templateUrl: 'view.html'}).
|
||||
when('/:id', {controller: ViewCtrl, templateUrl: 'view.html'}).
|
||||
when('/', {controller: CreateCtrl, templateUrl: 'ui/create.html'}).
|
||||
when('/:id!:key', {controller: ViewCtrl, templateUrl: 'ui/view.html'}).
|
||||
when('/:id', {controller: ViewCtrl, templateUrl: 'ui/view.html'}).
|
||||
otherwise({redirectTo: '/'});
|
||||
});
|
||||
|
||||
|
@ -14,8 +14,7 @@
|
|||
$scope.reload = $route.reload;
|
||||
$scope.o = {text: TEXT};
|
||||
|
||||
var base_url = $location.absUrl();
|
||||
base_url = base_url.slice(0, base_url.indexOf('#'));
|
||||
var base_url = window.location.origin;
|
||||
$scope.$base_url = base_url;
|
||||
|
||||
// Add Randomness to RNG
|
||||
|
@ -53,7 +52,7 @@
|
|||
if (o.text.length > 512) {o.zip = true; $scope.o.zip = true; }
|
||||
var e = encrypt(o);
|
||||
|
||||
console.log("Sending:\n" + json(e));
|
||||
console.log("Sending:\n" + e.txt);
|
||||
|
||||
$remoteService('/paste')
|
||||
.post({}, e.txt)
|
||||
|
@ -70,8 +69,7 @@
|
|||
function ViewCtrl($scope, $params, $remoteService, $location) {
|
||||
"use strict";
|
||||
|
||||
var base_url = $location.absUrl();
|
||||
base_url = base_url.slice(0, base_url.indexOf('#'));
|
||||
var base_url = window.location.origin;
|
||||
$scope.$base_url = base_url;
|
||||
|
||||
var id = $params.id,
|
||||
|
@ -86,7 +84,7 @@
|
|||
$scope.copy = function(t) {
|
||||
TEXT = t;
|
||||
$location.path('/');
|
||||
}
|
||||
};
|
||||
$remoteService('/paste/:id')
|
||||
.get({id: id})
|
||||
.success(decrypt(key, store))
|
||||
|
@ -111,11 +109,11 @@
|
|||
enc: function (t, p) { return CryptoJS.AES.encrypt(t, p).toString(); },
|
||||
dec: function (c, p) { return CryptoJS.AES.decrypt(c, p); },
|
||||
b64: function (s) {
|
||||
if (s == undefined) return;
|
||||
if (s === undefined) return;
|
||||
return CryptoJS.enc.Base64.stringify(s).replace(/[=]+/, '').replace(/\//g, '_').replace(/\+/g, '-');
|
||||
},
|
||||
d64: function (s) {
|
||||
if (s == undefined) return;
|
||||
if (s === undefined) return;
|
||||
switch (s.length % 3) {
|
||||
case 2:
|
||||
s += '=';
|
||||
|
@ -148,16 +146,16 @@
|
|||
str8: function (ua) {
|
||||
var s = '';
|
||||
for (var i = 0; i < ua.byteLength; i++) {
|
||||
if ((ua[i]&0x80) == 0) s += String.fromCharCode(ua[i]);
|
||||
else if ((ua[i]&0xe0) == 0xc0 && (ua[i+1]&0xc0) == 0x80) {
|
||||
if ((ua[i]&0x80) === 0) s += String.fromCharCode(ua[i]);
|
||||
else if ((ua[i]&0xe0) === 0xc0 && (ua[i+1]&0xc0) === 0x80) {
|
||||
s += String.fromCharCode(((ua[i]&0x1f)<<6) + (ua[i+1]&0x3f));
|
||||
i += 1;
|
||||
}
|
||||
else if ((ua[i]&0xf0) == 0xe0 && (ua[i+1]&0xc0) == 0x80 && (ua[i+2]&0xc0) == 0x80){
|
||||
else if ((ua[i]&0xf0) === 0xe0 && (ua[i+1]&0xc0) === 0x80 && (ua[i+2]&0xc0) === 0x80){
|
||||
s += String.fromCharCode(((ua[i]&0x0f)<<12) + ((ua[i+1]&0x3f)<<6) + (ua[i+2]&0x3f));
|
||||
i += 2;
|
||||
}
|
||||
else if ((ua[i]&0xf8) == 0xf0 && (ua[i+1]&0xc0) == 0x80 && (ua[i+2]&0xc0) == 0x80 && (ua[i+3]&0xc0) == 0x80) {
|
||||
else if ((ua[i]&0xf8) === 0xf0 && (ua[i+1]&0xc0) === 0x80 && (ua[i+2]&0xc0) === 0x80 && (ua[i+3]&0xc0) === 0x80) {
|
||||
s += String.fromCharCode(((ua[i]&0x0f)<<18) + ((ua[i+1]&0x3f)<<12) + ((ua[i+2]&0x3f)<<6) + (ua[i+3]&0x3f));
|
||||
i += 3;
|
||||
}
|
||||
|
@ -184,7 +182,7 @@
|
|||
var i = 0;
|
||||
|
||||
while (true) {
|
||||
if (s[i] == "") break;
|
||||
if (s[i] === "") break;
|
||||
|
||||
var l = s[i].trim().split(':\t');
|
||||
d[l[0]] = l[1];
|
||||
|
@ -194,7 +192,7 @@
|
|||
d.tx = s.splice(i).join('');
|
||||
|
||||
if (key === undefined) tgt({err: "Missing Key", code: 'no_key'});
|
||||
else if (d.chk != fn.rmd(fn.d64(key))) tgt({err: "Invalid Key", code: "bad_key"});
|
||||
else if (d.chk !== fn.rmd(fn.d64(key))) tgt({err: "Invalid Key", code: "bad_key"});
|
||||
else {
|
||||
var tx;
|
||||
if (d.zip) {
|
||||
|
@ -207,7 +205,7 @@
|
|||
|
||||
var lang = 'text';
|
||||
for (i = 0; i < HighliteLang.length; i++)
|
||||
if (d.lang == HighliteLang[i][0])
|
||||
if (d.lang === HighliteLang[i][0])
|
||||
lang = HighliteLang[i][0];
|
||||
|
||||
tgt({code: 'ok', tx: tx, lang: lang, exp: d.exp, zip: d.zip});
|
||||
|
@ -264,7 +262,7 @@
|
|||
|
||||
m.filter('blength', function () {
|
||||
return function (o) {
|
||||
if (!(typeof o == 'string' || o instanceof String)) return;
|
||||
if (!(typeof o === 'string' || o instanceof String)) return;
|
||||
if (o === undefined || o.length === undefined) return;
|
||||
var utf8length = 0;
|
||||
for (var n = 0; n < o.length; n++) {
|
||||
|
|
5
assets/bootstrap.min.css
vendored
Normal file
5
assets/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/highlightjs.min.css
vendored
Normal file
3
assets/highlightjs.min.css
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/* highlight.js 8.4 */
|
||||
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#f0f0f0;-webkit-text-size-adjust:none}.hljs,.hljs-subst,.hljs-tag .hljs-title,.nginx .hljs-title{color:black}.hljs-string,.hljs-title,.hljs-constant,.hljs-parent,.hljs-tag .hljs-value,.hljs-rules .hljs-value,.hljs-preprocessor,.hljs-pragma,.haml .hljs-symbol,.ruby .hljs-symbol,.ruby .hljs-symbol .hljs-string,.hljs-template_tag,.django .hljs-variable,.smalltalk .hljs-class,.hljs-addition,.hljs-flow,.hljs-stream,.bash .hljs-variable,.apache .hljs-tag,.apache .hljs-cbracket,.tex .hljs-command,.tex .hljs-special,.erlang_repl .hljs-function_or_atom,.asciidoc .hljs-header,.markdown .hljs-header,.coffeescript .hljs-attribute{color:#800}.smartquote,.hljs-comment,.hljs-annotation,.diff .hljs-header,.hljs-chunk,.asciidoc .hljs-blockquote,.markdown .hljs-blockquote{color:#888}.hljs-number,.hljs-date,.hljs-regexp,.hljs-literal,.hljs-hexcolor,.smalltalk .hljs-symbol,.smalltalk .hljs-char,.go .hljs-constant,.hljs-change,.lasso .hljs-variable,.makefile .hljs-variable,.asciidoc .hljs-bullet,.markdown .hljs-bullet,.asciidoc .hljs-link_url,.markdown .hljs-link_url{color:#080}.hljs-label,.hljs-javadoc,.ruby .hljs-string,.hljs-decorator,.hljs-filter .hljs-argument,.hljs-localvars,.hljs-array,.hljs-attr_selector,.hljs-important,.hljs-pseudo,.hljs-pi,.haml .hljs-bullet,.hljs-doctype,.hljs-deletion,.hljs-envvar,.hljs-shebang,.apache .hljs-sqbracket,.nginx .hljs-built_in,.tex .hljs-formula,.erlang_repl .hljs-reserved,.hljs-prompt,.asciidoc .hljs-link_label,.markdown .hljs-link_label,.vhdl .hljs-attribute,.clojure .hljs-attribute,.asciidoc .hljs-attribute,.lasso .hljs-attribute,.coffeescript .hljs-property,.hljs-phony{color:#88f}.hljs-keyword,.hljs-id,.hljs-title,.hljs-built_in,.css .hljs-tag,.hljs-javadoctag,.hljs-phpdoc,.hljs-dartdoc,.hljs-yardoctag,.smalltalk .hljs-class,.hljs-winutils,.bash .hljs-variable,.apache .hljs-tag,.hljs-type,.hljs-typename,.tex .hljs-command,.asciidoc .hljs-strong,.markdown .hljs-strong,.hljs-request,.hljs-status{font-weight:bold}.asciidoc .hljs-emphasis,.markdown .hljs-emphasis{font-style:italic}.nginx .hljs-built_in{font-weight:normal}.coffeescript .javascript,.javascript .xml,.lasso .markup,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:0.5}
|
||||
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#ffffff}.hljs,.hljs-subst{color:#444}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-built_in,.hljs-literal,.hljs-bullet,.hljs-code,.hljs-addition{color:#1F811F}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-comment{color:#888888}.hljs-meta{color:#2B6EA1}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}
|
|
@ -1,28 +1,35 @@
|
|||
<?doctype html?>
|
||||
<html ng-app='souris-app'>
|
||||
<head>
|
||||
<base href="/" />
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PasteBox</title>
|
||||
<!--INSERT_TEMPLATES-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<div class="container-responsive">
|
||||
<article ng-view></article>
|
||||
|
||||
<a onclick='var elm = document.getElementById("debug"); elm.parentNode.style.display="block"; window.scrollTop = window.scrollHeight;' style="margin:3px;cursor:context-menu;font-family:monospace;position:fixed;bottom:0;right:0">π</a>
|
||||
<div class='panel panel-default' style='height:13em;margin-bottom:0;margin-top:2em;;display:none;position:relative;bottom:0'><b>Debug Log</b>
|
||||
<div style='float:right'>
|
||||
<a class="btn" onclick='document.getElementById("debug").parentNode.style.display="none";'><i class='glyphicon glyphicon-remove'></i></a><br/>
|
||||
<a class="btn" onclick='var elm=document.getElementById("debug");while (elm.firstChild) {elm.removeChild(elm.firstChild);}'><i class='glyphicon glyphicon-ban-circle'></i></a>
|
||||
<div class="container-responsive">
|
||||
<article ng-view></article>
|
||||
<a onclick='var elm = document.getElementById("debug"); elm.parentNode.style.display="block"; window.scrollTop = window.scrollHeight;' style="margin:3px;cursor:context-menu;font-family:monospace;position:fixed;bottom:0;right:0">π</a>
|
||||
<div class='panel panel-default' style='height:13em;margin-bottom:0;margin-top:2em;;display:none;position:relative;bottom:0'><b>Debug Log</b>
|
||||
<div style='float:right'>
|
||||
<a class="btn" onclick='document.getElementById("debug").parentNode.style.display="none";'><i class='glyphicon glyphicon-remove'></i></a><br/>
|
||||
<a class="btn" onclick='var elm=document.getElementById("debug");while (elm.firstChild) {elm.removeChild(elm.firstChild);}'><i class='glyphicon glyphicon-ban-circle'></i></a>
|
||||
</div>
|
||||
<pre id=debug style='height:12em; overflow:scroll;'></pre>
|
||||
<footer></footer></div>
|
||||
</div>
|
||||
<pre id=debug style='height:12em; overflow:x-scroll;'></pre>
|
||||
<footer></footer></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="style.css" integrity='sha384-2g6CT1TMuzCclIAqYC+AkSkfA21njEedIBVs+k3tcZ6gHhCU7s17aGJMLvYsF0fK'>
|
||||
<!--REMOVE_ASSET_START-->
|
||||
<link rel="stylesheet" href="bootstrap.min.css">
|
||||
<link rel="stylesheet" href="highlightjs.min.css">
|
||||
<script src='lib.js'></script>
|
||||
<!--REMOVE_ASSET_END-->
|
||||
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<script src='app.js'></script>
|
||||
<noscript>
|
||||
<div class=container-responsive>
|
||||
|
@ -53,5 +60,4 @@
|
|||
</div>
|
||||
</noscript>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
|
@ -455,4 +455,4 @@ angular.module("hljs",[]).provider("hljsService",function(){var a={};return{setO
|
|||
*/
|
||||
|
||||
var AppConfig = {aspect: null};
|
||||
!function(a,b){m=a.module("souris-common",["ngRoute","ngCookies"]),m.factory("$remoteService",["$http","$cookies","$location",function(a,c,d){var e=function(e,f,g,h){var i={cache:!1,headers:{"If-Modified-Since":"0"},withCredentials:!0};void 0!==c.get("XSRF-TOKEN-DEV")&&(i.headers={"X-XSRF-TOKEN-DEV":c.get("XSRF-TOKEN-DEV")}),i.method=e,void 0===g&&(g={}),void 0===g.aspect&&(g.aspect=d.absUrl().split("#")[0].replace(/\/$/,"").split(d.host()+"/",2),1==g.aspect.length?g.aspect="/":g.aspect=g.aspect[1].split("/",2)[0],void 0!==b.aspect&&(g.aspect=b.aspect));for(var j in g)if(g.hasOwnProperty(j)){var k=":"+j;f.search(k)>0&&(f=f.replace(k,g[j]),delete g[j])}return i.params=g,i.url=f,void 0!==h&&(i.data=h),a("DELETE"==e||"JSONP"==e?i:i)};return function(a){return{get:function(b){return e("GET",a,b)},put:function(b,c){return e("PUT",a,b,c)},post:function(b,c){return e("POST",a,b,c)},patch:function(b,c){return e("PATCH",a,b,c)},del:function(b){return e("DELETE",a,b)},delete:function(b){return e("DELETE",a,b)},jsonp:function(b){return e("JSONP",a,b)}}}}]),m.directive("selectOnClick",function(){"use strict";return{restrict:"A",link:function(a,b){b.on("click",function(){this.select()})}}}),m.filter("default",function(){return function(a,b){return void 0===a?b:a}})}(angular,AppConfig);
|
||||
!function(a,b){m=a.module("souris-common",["ngRoute","ngCookies"]),m.config(['$locationProvider', function(l){l.html5Mode(false);}]),m.factory("$remoteService",["$http","$cookies","$location",function(a,c,d){var e=function(e,f,g,h){var i={cache:!1,headers:{"If-Modified-Since":"0"},withCredentials:!0};void 0!==c.get("XSRF-TOKEN-DEV")&&(i.headers={"X-XSRF-TOKEN-DEV":c.get("XSRF-TOKEN-DEV")}),i.method=e,void 0===g&&(g={}),void 0===g.aspect&&(g.aspect=window.location.origin,1==g.aspect.length?g.aspect="/":g.aspect=g.aspect[1].split("/",2)[0],void 0!==b.aspect&&(g.aspect=b.aspect));for(var j in g)if(g.hasOwnProperty(j)){var k=":"+j;f.search(k)>0&&(f=f.replace(k,g[j]),delete g[j])}return i.params=g,i.url=f,void 0!==h&&(i.data=h),a("DELETE"==e||"JSONP"==e?i:i)};return function(a){return{get:function(b){return e("GET",a,b)},put:function(b,c){return e("PUT",a,b,c)},post:function(b,c){return e("POST",a,b,c)},patch:function(b,c){return e("PATCH",a,b,c)},del:function(b){return e("DELETE",a,b)},delete:function(b){return e("DELETE",a,b)},jsonp:function(b){return e("JSONP",a,b)}}}}]),m.directive("selectOnClick",function(){"use strict";return{restrict:"A",link:function(a,b){b.on("click",function(){this.select()})}}}),m.filter("default",function(){return function(a,b){return void 0===a?b:a}})}(angular,AppConfig);
|
||||
|
|
|
@ -5,7 +5,7 @@ if [ "$1" = "-h" ]; then
|
|||
usage: echo /etc/passwd | ./paste.sh
|
||||
|
||||
env options:
|
||||
PASTE_URL - Set the url base for paste operations (default: HTTPS://sour.is/paste)
|
||||
PASTE_URL - Set the url base for paste operations (default: HTTPS://paste.dn42.us)
|
||||
PASTE_GZIP - 0 = No Compression, 1 = Use gzip compression (default: 0)
|
||||
PASTE_BURN - 0 = No Burn on Read, 1 = Burn on read (default: 0)
|
||||
PASTE_DATE - Value to be used when setting expire date. (default: next-week)
|
||||
|
@ -13,7 +13,7 @@ EOL
|
|||
exit
|
||||
fi
|
||||
|
||||
PASTE_URL=${PASTE_URL-"https://sour.is/paste"}
|
||||
PASTE_URL=${PASTE_URL-"https://paste.dn42.us"}
|
||||
PASTE_BURN=${PASTE_BURN-0}
|
||||
PASTE_DATE=${PASTE_DATE-"next-week"}
|
||||
PASTE_GZIP=${PASTE_GZIP-0}
|
||||
|
@ -29,7 +29,7 @@ HASH=$((echo -e "exp:\t$(date +%s -d ${PASTE_DATE})"; \
|
|||
[ "$PASTE_GZIP" -eq "1" ] && echo -e "zip:\ttrue"; \
|
||||
echo; \
|
||||
cat /dev/stdin | $GZBIN | openssl aes-256-cbc -e -a -k $PASS) | \
|
||||
curl -s -X POST ${PASTE_URL}/api/ --data-binary @-)
|
||||
curl -s -X POST ${PASTE_URL}/paste --data-binary @-)
|
||||
|
||||
HASH_OK=$(echo $HASH | cut -c1-2)
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,14 +4,14 @@
|
|||
<span class="input-group-btn">
|
||||
<a class="btn btn-default" ng-click='reload()' type="button">New</a>
|
||||
</span>
|
||||
<input type=text readonly class=form-control select-on-click value="{{$base_url}}#/{{result.id}}!{{result.key}}">
|
||||
<input type=text readonly class=form-control select-on-click value="{{$base_url}}/#/{{result.id}}!{{result.key}}">
|
||||
<span class="input-group-btn">
|
||||
<a class='btn btn-default' ng-href='#/{{result.id}}!{{result.key}}'>Open</a>
|
||||
<a class='btn btn-default' ng-href='/#/{{result.id}}!{{result.key}}'>Open</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre class=col-xs-12> # Command Line: curl -s {{$base_url}}api/get/{{result.id}} | sed "1,/^\$/d" | openssl aes-256-cbc -d -a -k {{result.key}} <span ng-if='o.zip == true''>| gzip -dc</span>
|
||||
<pre class=col-xs-12> # Command Line: curl -s {{$base_url}}/api/get/{{result.id}} | sed "1,/^\$/d" | openssl aes-256-cbc -d -a -k {{result.key}} <span ng-if='o.zip == true'>| gzip -dc</span>
|
||||
|
||||
{{result.text}}</pre>
|
||||
</div>
|
||||
|
@ -47,7 +47,7 @@
|
|||
$ echo /etc/passwd | ./paste.sh
|
||||
|
||||
env options:
|
||||
PASTE_URL - Set the url base for paste operations (default: HTTPS://sour.is/paste)
|
||||
PASTE_URL - Set the url base for paste operations (default: HTTPS://paste.dn42.us)
|
||||
PASTE_GZIP - 0 = No Compression, 1 = Use gzip compression (default: 0)
|
||||
PASTE_BURN - 0 = No Burn on Read, 1 = Burn on read (default: 0)
|
||||
PASTE_DATE - Value to be used when setting expire date. (default: next-week)
|
|
@ -2,9 +2,9 @@
|
|||
<div class=col-xs-12>
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<a class="btn btn-default" ng-href='#/' type="button">New</a>
|
||||
<a class="btn btn-default" ng-href='/#/' type="button">New</a>
|
||||
</span>
|
||||
<input type=text readonly class=form-control select-on-click value="{{$base_url}}#/{{id}}!{{key}}">
|
||||
<input type=text readonly class=form-control select-on-click value="{{$base_url}}/#/{{id}}!{{key}}">
|
||||
<span class="input-group-btn">
|
||||
<a class='btn btn-default' ng-click='copy(store.tx)'>Copy</a>
|
||||
</span>
|
||||
|
@ -16,6 +16,6 @@
|
|||
<b>Lang:</b> {{store.lang}}, <b>Expires:</b> <span ng-if='store.exp != "burn_on_read"'>{{store.exp*1000|date}}</span><span ng-if='store.exp == "burn_on_read"'>Burn on Read</span>
|
||||
</div>
|
||||
<div hljs language="{{store.lang}}" source="store.tx"></div>
|
||||
<pre class=col-xs-12> # Command Line: curl -s {{$base_url}}api/get/{{id}} | sed "1,/^\$/d" | openssl aes-256-cbc -d -a -k {{key}} <span ng-if='store.zip != undefined'>| gzip -dc</span>
|
||||
<pre class=col-xs-12> # Command Line: curl -s {{$base_url}}/api/get/{{id}} | sed "1,/^\$/d" | openssl aes-256-cbc -d -a -k {{key}} <span ng-if='store.zip != undefined'>| gzip -dc</span>
|
||||
</div>
|
||||
<div ng-if="store.err != undefined"><h3>Error: {{store.err}}</h3></div>
|
67
config.go
67
config.go
|
@ -3,28 +3,33 @@ package main
|
|||
import (
|
||||
"github.com/spf13/viper"
|
||||
"github.com/docopt/docopt.go"
|
||||
"log"
|
||||
"io/ioutil"
|
||||
"sour.is/x/log"
|
||||
"sour.is/x/httpsrv"
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
APP_VERSION string
|
||||
APP_BUILD string
|
||||
)
|
||||
var APP_NAME string = "Paste API"
|
||||
var APP_USAGE string = `Paste API
|
||||
|
||||
Usage:
|
||||
mercury [-v] serve [--listen=<ListenAddress>]
|
||||
paste version
|
||||
paste [ -v | -vv ] serve
|
||||
|
||||
Options:
|
||||
-v Log to console.
|
||||
-v Log info to console.
|
||||
-vv Log debug to console.
|
||||
-l <ListenAddress>, --listen=<ListenAddress> Address to listen on.
|
||||
-c <ConfigFile>, --conf=<ConfigFile> Config file name. [Default: config.toml]
|
||||
-c <ConfigDir>, --config=<ConfigDir> Set Config Directory.
|
||||
|
||||
Config:
|
||||
The config file is read from the following locations:
|
||||
- /etc/paste/
|
||||
- ~/.local/paste/
|
||||
- <ConfigDir>
|
||||
- /etc/opt/sour.is/paste/
|
||||
- Working Directory
|
||||
`
|
||||
|
||||
|
@ -37,25 +42,41 @@ func init() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if args["-v"] == false {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
if args["-v"].(int) == 1 {
|
||||
log.SetVerbose(log.Vinfo)
|
||||
}
|
||||
if args["-v"].(int) == 2 {
|
||||
log.SetVerbose(log.Vdebug)
|
||||
log.Debug("Debug Logging.")
|
||||
}
|
||||
|
||||
viper.SetConfigName("config")
|
||||
viper.AddConfigPath("/etc/reg42/")
|
||||
viper.AddConfigPath("$HOME/.local/reg42/")
|
||||
if args["--config"] != nil{
|
||||
viper.AddConfigPath(args["--config"].(string))
|
||||
}
|
||||
|
||||
viper.AddConfigPath("/etc/opt/sour.is/paste/")
|
||||
viper.AddConfigPath(".")
|
||||
|
||||
viper.SetConfigType("toml")
|
||||
viper.ReadConfig(bytes.NewBuffer(defaultConfig))
|
||||
viper.ReadConfig(bytes.NewBuffer([]byte(defaultConfig)))
|
||||
|
||||
err = viper.MergeInConfig()
|
||||
if err != nil { // Handle errors reading the config file
|
||||
panic(fmt.Errorf("Fatal error config file: %s \n", err))
|
||||
log.Fatalf("Fatal error config file: %s \n", err)
|
||||
}
|
||||
|
||||
log.Print("Read config from: ", viper.ConfigFileUsed())
|
||||
viper.Set("app.name", APP_NAME)
|
||||
|
||||
viper.SetDefault("app.version", "VERSION")
|
||||
if APP_VERSION != "" {
|
||||
viper.Set("app.version", APP_VERSION)
|
||||
}
|
||||
|
||||
viper.SetDefault("app.build", "SNAPSHOT")
|
||||
if APP_BUILD != "" {
|
||||
viper.Set("app.build", APP_BUILD)
|
||||
}
|
||||
|
||||
if args["serve"] == true {
|
||||
|
||||
|
@ -63,13 +84,27 @@ func init() {
|
|||
viper.Set("listen", args["--listen"].(string))
|
||||
}
|
||||
|
||||
httpsrv.Config()
|
||||
log.Noticef("Startup: %s (%s %s)",
|
||||
viper.GetString("app.name"),
|
||||
viper.GetString("app.version"),
|
||||
viper.GetString("app.build"))
|
||||
|
||||
log.Notice("Read config from: ", viper.ConfigFileUsed())
|
||||
|
||||
if viper.IsSet("http") {
|
||||
httpsrv.Config()
|
||||
}
|
||||
} else if args["version"] == true {
|
||||
fmt.Printf("Version: %s (%s %s)\n",
|
||||
viper.GetString("app.name"),
|
||||
viper.GetString("app.version"),
|
||||
viper.GetString("app.build"))
|
||||
}
|
||||
}
|
||||
|
||||
var defaultConfig []byte = []byte(`
|
||||
[http]
|
||||
listen = ":9010"
|
||||
fileserver = "/public/:/"
|
||||
|
||||
[module.paste]
|
||||
random = "4096"
|
||||
|
|
11
config.toml
11
config.toml
|
@ -1,11 +0,0 @@
|
|||
listen = ":9010"
|
||||
identity = "mock"
|
||||
fileserver = "/:public/"
|
||||
|
||||
[idm.mock]
|
||||
identity = "anon"
|
||||
display_name = "Default User"
|
||||
|
||||
[module.paste]
|
||||
random = "4096"
|
||||
store = "data/"
|
2
debian/BUILD/.gitignore
vendored
Normal file
2
debian/BUILD/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.git*
|
39
debian/Makefile
vendored
Normal file
39
debian/Makefile
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
NAME=sour.is-paste
|
||||
VERSION=$(shell cat VERSION)
|
||||
RELEASE=$(shell cat RELEASE)
|
||||
|
||||
REPO_IP="172.22.141.171"
|
||||
REPO_PATH="/opt/web/pub/sour.is/debian/"
|
||||
|
||||
all: release build copy
|
||||
|
||||
clean:
|
||||
rm -r BUILD/*
|
||||
|
||||
release:
|
||||
export RELEASE=`cat RELEASE`; \
|
||||
echo `expr $${RELEASE} + 1` > RELEASE
|
||||
|
||||
build:
|
||||
export VERSION=`cat VERSION`; \
|
||||
export RELEASE=`cat RELEASE`; \
|
||||
export DATE=`date -u +%FT%TZ`; \
|
||||
export BUILD="BUILD/$(NAME)_$${VERSION}-$${RELEASE}"; \
|
||||
rm -rf ./$${BUILD}; \
|
||||
cp -r ROOT "$${BUILD}"; \
|
||||
export SED="s_Version:.*_Version: $${VERSION}-$${RELEASE}_"; \
|
||||
sed -i "$$SED" "$${BUILD}/DEBIAN/control"; \
|
||||
go build -o $${BUILD}/opt/sour.is/bin/paste \
|
||||
-ldflags "-X main.APP_VERSION=$${VERSION}-$${RELEASE} -X main.APP_BUILD=$${DATE}"\
|
||||
sour.is/x/paste; \
|
||||
dpkg -b $${BUILD}; \
|
||||
|
||||
copy:
|
||||
export VERSION=`cat VERSION`; \
|
||||
export RELEASE=`cat RELEASE`; \
|
||||
export BUILD="BUILD/$(NAME)_$${VERSION}-$${RELEASE}"; \
|
||||
scp "$${BUILD}.deb" $(REPO_IP):$(REPO_PATH); \
|
||||
ssh $(REPO_IP) -- $(REPO_PATH)scan.sh "$(REPO_PATH)$(NAME)_$${VERSION}-$${RELEASE}.deb";
|
||||
|
||||
deploy:
|
||||
ansible kapha -s -m apt -a "name=sour.is-paste update_cache=yes state=latest"
|
1
debian/RELEASE
vendored
Normal file
1
debian/RELEASE
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
8
|
8
debian/ROOT/DEBIAN/control
vendored
Normal file
8
debian/ROOT/DEBIAN/control
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
Package: sour.is-paste
|
||||
Version: 2.0.0-1
|
||||
Section: base
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Maintainer: xuu <me@sour.is>
|
||||
Description: Encrypted Paste Store
|
||||
API and UI for storing encrypted pastes.
|
17
debian/ROOT/DEBIAN/postinst
vendored
Executable file
17
debian/ROOT/DEBIAN/postinst
vendored
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
systemctl daemon-reload
|
||||
|
||||
if systemctl is-enabled sour.is-paste.service; then
|
||||
systemctl start sour.is-paste.service
|
||||
fi
|
||||
|
||||
sudo chown -R root:root /opt/sour.is
|
||||
sudo chown -R root:root /etc/opt/sour.is
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
20
debian/ROOT/DEBIAN/postrm
vendored
Executable file
20
debian/ROOT/DEBIAN/postrm
vendored
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
if systemctl is-active sour.is-paste.service; then
|
||||
systemctl stop sour.is-paste.service
|
||||
fi
|
||||
;;
|
||||
|
||||
purge)
|
||||
if systemctl is-enabled sour.is-paste.service; then
|
||||
systemctl disable sour.is-paste.service
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
|
20
debian/ROOT/DEBIAN/preinst
vendored
Executable file
20
debian/ROOT/DEBIAN/preinst
vendored
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
install)
|
||||
# do some magic
|
||||
;;
|
||||
|
||||
upgrade|abort-upgrade)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
14
debian/ROOT/DEBIAN/prerm
vendored
Executable file
14
debian/ROOT/DEBIAN/prerm
vendored
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
remove|upgrade)
|
||||
if systemctl is-active sour.is-paste.service; then
|
||||
systemctl stop sour.is-paste.service
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
|
4
debian/ROOT/etc/opt/sour.is/paste/config.toml-example
vendored
Normal file
4
debian/ROOT/etc/opt/sour.is/paste/config.toml-example
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
[http]
|
||||
listen = ":9010"
|
||||
|
||||
|
16
debian/ROOT/lib/systemd/system/sour.is-paste.service
vendored
Normal file
16
debian/ROOT/lib/systemd/system/sour.is-paste.service
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
[Unit]
|
||||
Description=sour.is paste API
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/opt/sour.is/
|
||||
ExecStart=/opt/sour.is/bin/paste serve
|
||||
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
1
debian/VERSION
vendored
Normal file
1
debian/VERSION
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
2.0.0
|
|
@ -1,33 +0,0 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
"database/sql/driver"
|
||||
"log"
|
||||
)
|
||||
|
||||
type NullTime struct {
|
||||
Time time.Time
|
||||
Valid bool // Valid is true if Time is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (nt *NullTime) Scan(value interface{}) error {
|
||||
nt.Time, nt.Valid = value.(time.Time)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (nt NullTime) Value() (driver.Value, error) {
|
||||
if !nt.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return nt.Time, nil
|
||||
}
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
136
model/paste.go
136
model/paste.go
|
@ -1,136 +0,0 @@
|
|||
package model
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"sour.is/x/dbm"
|
||||
"database/sql"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
type Paste struct{
|
||||
Id string `json:"paste_id"`
|
||||
Text string `json:"paste_text"`
|
||||
Chk string `json:"paste_chk"`
|
||||
Lang string `json:"paste_lang"`
|
||||
Zip bool `json:"paste_zip"`
|
||||
Burn bool `json:"paste_burn"`
|
||||
CreatedOn time.Time `json:"created_on"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
ExpiresOn *time.Time `json:"expires_on"`
|
||||
Deleted bool `json:"deleted"`
|
||||
}
|
||||
/*
|
||||
func GetPaste(id string) (p Paste, err error) {
|
||||
db := dbm.GetDB()
|
||||
|
||||
stmt, _ := db.Prepare(`
|
||||
select paste_id, paste_text, created_on, created_by, paste_lang, expires_on, paste_zip, paste_burn, paste_chk
|
||||
from paste
|
||||
where paste_id = ?`)
|
||||
|
||||
var lang sql.NullString
|
||||
var zip sql.NullBool
|
||||
var burn sql.NullBool
|
||||
var expiresOn NullTime
|
||||
|
||||
err = stmt.QueryRow(id).Scan(&p.Id, &p.Text, &p.CreatedOn, &p.CreatedBy, &lang, &expiresOn, &zip, &burn, &p.Chk);
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return p, err
|
||||
}
|
||||
if lang.Valid {
|
||||
p.Lang = lang.String
|
||||
}
|
||||
if zip.Valid {
|
||||
p.Zip = zip.Bool
|
||||
}
|
||||
if burn.Valid {
|
||||
p.Burn = burn.Bool
|
||||
}
|
||||
if expiresOn.Valid {
|
||||
p.ExpiresOn = &expiresOn.Time
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func CreatePaste(p Paste) (Paste, error) {
|
||||
db := dbm.GetDB()
|
||||
|
||||
s256 := sha256.Sum256([]byte(p.String()))
|
||||
|
||||
p.Id = base64.RawURLEncoding.EncodeToString(s256[12:])
|
||||
|
||||
count, err := db.Prepare(`
|
||||
SELECT count(1) ok
|
||||
FROM paste
|
||||
WHERE paste_id=?`)
|
||||
checkErr(err)
|
||||
|
||||
insert, err := db.Prepare(`
|
||||
INSERT paste
|
||||
SET paste_id = ?,
|
||||
paste_text = ?,
|
||||
paste_chk = ?,
|
||||
created_by = ?,
|
||||
paste_lang = ?,
|
||||
paste_zip = ?,
|
||||
paste_burn = ?,
|
||||
expires_on = ?`)
|
||||
checkErr(err)
|
||||
|
||||
update, err := db.Prepare(`
|
||||
UPDATE paste
|
||||
SET paste_text = ?,
|
||||
paste_chk = ?,
|
||||
created_by = ?,
|
||||
paste_lang = ?,
|
||||
paste_zip = ?,
|
||||
paste_burn = ?,
|
||||
expires_on = ?
|
||||
WHERE paste_id = ?`)
|
||||
checkErr(err)
|
||||
|
||||
var ok bool
|
||||
err = count.QueryRow(p.Id).Scan(&ok)
|
||||
checkErr(err)
|
||||
|
||||
if ok {
|
||||
_, err = update.Exec(p.Text, p.Chk, p.CreatedBy, p.Lang, p.Zip, p.Burn, p.ExpiresOn, p.Id)
|
||||
checkErr(err)
|
||||
} else {
|
||||
_, err = insert.Exec(p.Id, p.Text, p.Chk, p.CreatedBy, p.Lang, p.Zip, p.Burn, p.ExpiresOn)
|
||||
checkErr(err)
|
||||
}
|
||||
np, err := GetPaste(p.Id)
|
||||
checkErr(err)
|
||||
|
||||
return np, err
|
||||
}
|
||||
*/
|
||||
|
||||
func (p Paste) String() string {
|
||||
var b bytes.Buffer
|
||||
|
||||
b.WriteString(fmt.Sprintf("; id:\t%s\n", p.Id))
|
||||
b.WriteString(fmt.Sprintf("chk:\t%s\n", p.Chk))
|
||||
b.WriteString(fmt.Sprintf("lang:\t%s\n", p.Lang))
|
||||
if p.ExpiresOn != nil {
|
||||
b.WriteString(fmt.Sprintf("exp:\t%d\n", p.ExpiresOn.Unix()))
|
||||
}
|
||||
if p.Zip {
|
||||
b.WriteString(fmt.Sprintf("zip:\t%t\n", p.Zip))
|
||||
}
|
||||
if p.Burn {
|
||||
b.WriteString(fmt.Sprintf("burn:\t%t\n", p.Burn))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
b.WriteString(p.Text)
|
||||
|
||||
return b.String()
|
||||
}
|
1880
public/app.js
1880
public/app.js
File diff suppressed because one or more lines are too long
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -1,14 +1,15 @@
|
|||
<?doctype html?><html ng-app=souris-app><meta charset=utf-8><meta content="IE=edge"http-equiv=X-UA-Compatible><meta content="width=device-width,initial-scale=1"name=viewport><title>PasteBox</title><script id=create.html type=text/ng-template><div class=row ng-show="result != undefined"><div class=col-xs-12><div class=input-group><span class=input-group-btn><a class="btn btn-default"ng-click=reload() type=button>New</a> </span><input class=form-control readonly select-on-click value={{$base_url}}#/{{result.id}}!{{result.key}}> <span class=input-group-btn><a class="btn btn-default"ng-href=#/{{result.id}}!{{result.key}}>Open</a></span></div></div><pre class=col-xs-12> # Command Line: curl -s {{$base_url}}api/get/{{result.id}} | sed "1,/^\$/d" | openssl aes-256-cbc -d -a -k {{result.key}} <span ng-if="o.zip == true">| gzip -dc</span>
|
||||
<?doctype html?><html ng-app="souris-app"><head><base href="/"><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>PasteBox</title><script id="ui/create.html" type="text/ng-template"><div class="row" ng-show="result != undefined"><div class="col-xs-12"><div class="input-group"><span class="input-group-btn"><a class="btn btn-default" ng-click="reload()" type="button">New</a></span> <input type="text" readonly="readonly" class="form-control" select-on-click value="{{$base_url}}/#/{{result.id}}!{{result.key}}"> <span class="input-group-btn"><a class="btn btn-default" ng-href="/#/{{result.id}}!{{result.key}}">Open</a></span></div></div><pre class="col-xs-12"> # Command Line: curl -s {{$base_url}}/api/get/{{result.id}} | sed "1,/^\$/d" | openssl aes-256-cbc -d -a -k {{result.key}} <span ng-if="o.zip == true">| gzip -dc</span>
|
||||
|
||||
{{result.text}}</pre></div><div ng-hide="result != undefined"><form name=paste ng-submit=Encrypt(o)><div class="form form-inline"><ol class=breadcrumb><li><label>Syntax</label><select class="form-control input-sm"ng-init='o.lang = "text"'ng-model=o.lang ng-options='i.key as i.val for i in HighliteLang | orderBy:"+val"'></select><li><label>Expires</label><select class="form-control input-sm"ng-init="o.exp = 604800"ng-model=o.exp ng-options='i.key as i.val for i in ExpireTimes | orderBy:"+key"'></select><li><label><input ng-model=o.burn type=checkbox> Burn on Read</label></ol></div><textarea class=form-control ng-model=o.text required rows=20 style='font-family:hack,"Anonymous Pro",consolita,monospace'></textarea><pre>Additional Entropy: {{entropy}} bytes / Content size: {{o.text|blength|default:0}} bytes</pre><button class="btn btn-default btn-block btn-lg"ng-disabled="o.text == undefined || o.text.length == 0"type=submit>Encrypt</button></form><p>Create pastes from the command line! <a href=./paste.sh>paste.sh</a><pre>
|
||||
{{result.text}}</pre></div><div ng-hide="result != undefined"><form name="paste" ng-submit="Encrypt(o)"><div class="form form-inline"><ol class="breadcrumb"><li><label>Syntax</label><select class="form-control input-sm" ng-model="o.lang" ng-options="i.key as i.val for i in HighliteLang | orderBy:"+val"" ng-init="o.lang = "text""></select></li><li><label>Expires</label><select class="form-control input-sm" ng-model="o.exp" ng-options="i.key as i.val for i in ExpireTimes | orderBy:"+key"" ng-init="o.exp = 604800"></select></li><li><label><input type="checkbox" ng-model="o.burn"> Burn on Read</label></li></ol></div><textarea style="font-family:hack,"" required class="form-control" rows="20" ng-model="o.text"></textarea><pre>Additional Entropy: {{entropy}} bytes / Content size: {{o.text|blength|default:0}} bytes</pre><button type="submit" class="btn btn-default btn-lg btn-block" ng-disabled="o.text == undefined || o.text.length == 0">Encrypt</button></form><p>Create pastes from the command line! <a href="./paste.sh">paste.sh</a><pre>
|
||||
$ echo /etc/passwd | ./paste.sh
|
||||
|
||||
env options:
|
||||
PASTE_URL - Set the url base for paste operations (default: HTTPS://sour.is/paste)
|
||||
PASTE_URL - Set the url base for paste operations (default: HTTPS://paste.dn42.us)
|
||||
PASTE_GZIP - 0 = No Compression, 1 = Use gzip compression (default: 0)
|
||||
PASTE_BURN - 0 = No Burn on Read, 1 = Burn on read (default: 0)
|
||||
PASTE_DATE - Value to be used when setting expire date. (default: next-week)
|
||||
</pre></div></script><script id=view.html type=text/ng-template><div class=row><div class=col-xs-12><div class=input-group><span class=input-group-btn><a class="btn btn-default" ng-click='new()' type=button>New</a> </span><input class=form-control readonly select-on-click value={{$base_url}}#/{{id}}!{{key}}><span class="input-group-btn"><a class='btn btn-default' ng-click='copy(store.tx)'>Copy</a></span></div></div></div><div ng-if="store.err == undefined"><div class="well well-sm"><b>Lang:</b> {{store.lang}}, <b>Expires:</b> <span ng-if='store.exp != "burn_on_read"'>{{store.exp*1000|date}}</span><span ng-if='store.exp == "burn_on_read"'>Burn on Read</span></div><div hljs language={{store.lang}} source=store.tx></div><pre class=col-xs-12> # Command Line: curl -s {{$base_url}}api/get/{{id}} | sed "1,/^\$/d" | openssl aes-256-cbc -d -a -k {{key}} <span ng-if="store.zip != undefined">| gzip -dc</span></div><div ng-if="store.err != undefined"><h3>Error: {{store.err}}</h3></div></script><div id=wrapper><div class=container-responsive><article ng-view></article><a onclick='var e=document.getElementById("debug");e.parentNode.style.display="block",window.scrollTop=window.scrollHeight'style=margin:3px;cursor:context-menu;font-family:monospace;position:fixed;bottom:0;right:0>π</a><div class="panel panel-default"style=height:13em;margin-bottom:0;margin-top:2em;display:none;position:relative;bottom:0><b>Debug Log</b><div style=float:right><a onclick='document.getElementById("debug").parentNode.style.display="none"'class=btn><i class="glyphicon glyphicon-remove"></i></a><br><a onclick='for(var e=document.getElementById("debug");e.firstChild;)e.removeChild(e.firstChild)'class=btn><i class="glyphicon glyphicon-ban-circle"></i></a></div><pre id=debug style=height:12em;overflow:x-scroll></pre><footer></footer></div></div></div><link href=style.css integrity=sha384-2g6CT1TMuzCclIAqYC+AkSkfA21njEedIBVs+k3tcZ6gHhCU7s17aGJMLvYsF0fK rel=stylesheet><script src=app.js></script><noscript><div class=container-responsive><h1>PasteBox</h1><p>It looks like yo don't have javascript enabled for this site. But thats ok. You can still submit and read the content of pastes by using a few curl/openssl/gunzip commands.<h2>Get the paste</h2><p>Lets say you have the following link <code>https://domain.tld/#/FeLq42kIQV69hQCJA8m9lg!5EDDziaCjceHjeG5UQ9M7-6wgyq5YVfysAEZ0wUNy6w</code>. Query the REST endpoint for the ID or part before the ! in the url hash.<pre><code>$ curl -i https://domain.tld/api/FeLq42kIQV69hQCJA8m9lg</code></pre><h2>Decrypt</h2><p>Using Openssl you want to remove the header and pass the remaining base64 for decryption. The cypher used is aes-256-cbc. The key is the portion after the ! in the link.<pre><code>... | sed '1,/^$/d' | openssl aes-256-cbc -d -a -k 5EDDziaCjceHjeG5UQ9M7-6wgyq5YVfysAEZ0wUNy6w</code></pre><h2>Deflate</h2><p>If as in the provided example the paste has been compressed pass it through gunzip. The header will have "zip: true" if it has been compressed.<pre><code> ... | gzip -dc </code></pre><h2>Example Output</h2><pre><code>$ curl -s "https://domain.tld/api/FeLq42kIQV69hQCJA8m9lg" | sed "1,/^$/d" | openssl aes-256-cbc -d -a -k 5EDDziaCjceHjeG5UQ9M7-6wgyq5YVfysAEZ0wUNy6w | gzip -dc
|
||||
</pre></p></div></script><script id="ui/view.html" type="text/ng-template"><div class="row"><div class="col-xs-12"><div class="input-group"><span class="input-group-btn"><a class="btn btn-default" ng-href="/#/" type="button">New</a></span> <input type="text" readonly="readonly" class="form-control" select-on-click value="{{$base_url}}/#/{{id}}!{{key}}"> <span class="input-group-btn"><a class="btn btn-default" ng-click="copy(store.tx)">Copy</a></span></div></div></div><div ng-if="store.err == undefined"><div class="well well-sm"><b>Lang:</b> {{store.lang}}, <b>Expires:</b> <span ng-if="store.exp != "burn_on_read"">{{store.exp*1000|date}}</span><span ng-if="store.exp == "burn_on_read"">Burn on Read</span></div><div hljs language="{{store.lang}}" source="store.tx"></div><pre class="col-xs-12"> # Command Line: curl -s {{$base_url}}/api/get/{{id}} | sed "1,/^\$/d" | openssl aes-256-cbc -d -a -k {{key}} <span ng-if="store.zip != undefined">| gzip -dc</span>
|
||||
</pre></div><div ng-if="store.err != undefined"><h3>Error: {{store.err}}</h3></div></script></head><body><div id="wrapper"><div class="container-responsive"><article ng-view></article><a onclick="var elm = document.getElementById("debug"); elm.parentNode.style.display="block"; window.scrollTop = window.scrollHeight" style="margin:3px;cursor:context-menu;font-family:monospace;position:fixed;bottom:0;right:0">π</a><div class="panel panel-default" style="height:13em;margin-bottom:0;margin-top:2em;display:none;position:relative;bottom:0"><b>Debug Log</b><div style="float:right"><a class="btn" onclick="document.getElementById("debug").parentNode.style.display="none""><i class="glyphicon glyphicon-remove"></i></a><br><a class="btn" onclick="var elm=document.getElementById("debug");while (elm.firstChild) {elm.removeChild(elm.firstChild);}"><i class="glyphicon glyphicon-ban-circle"></i></a></div><pre id="debug" style="height:12em;overflow:scroll"></pre><footer></footer></div></div></div><link rel="stylesheet" href="style.css"><script src="app.js"></script><noscript><div class="container-responsive"><h1>PasteBox</h1><p>It looks like yo don't have javascript enabled for this site. But thats ok. You can still submit and read the content of pastes by using a few curl/openssl/gunzip commands.</p><h2>Get the paste</h2><p>Lets say you have the following link <code>https://domain.tld/#/FeLq42kIQV69hQCJA8m9lg!5EDDziaCjceHjeG5UQ9M7-6wgyq5YVfysAEZ0wUNy6w</code>. Query the REST endpoint for the ID or part before the ! in the url hash.</p><pre><code>$ curl -i https://domain.tld/api/FeLq42kIQV69hQCJA8m9lg</code></pre><h2>Decrypt</h2><p>Using Openssl you want to remove the header and pass the remaining base64 for decryption. The cypher used is aes-256-cbc. The key is the portion after the ! in the link.</p><pre><code>... | sed '1,/^$/d' | openssl aes-256-cbc -d -a -k 5EDDziaCjceHjeG5UQ9M7-6wgyq5YVfysAEZ0wUNy6w</code></pre><h2>Deflate</h2><p>If as in the provided example the paste has been compressed pass it through gunzip. The header will have "zip: true" if it has been compressed.</p><pre><code> ... | gzip -dc </code></pre><h2>Example Output</h2><pre><code>$ curl -s "https://domain.tld/api/FeLq42kIQV69hQCJA8m9lg" | sed "1,/^$/d" | openssl aes-256-cbc -d -a -k 5EDDziaCjceHjeG5UQ9M7-6wgyq5YVfysAEZ0wUNy6w | gzip -dc
|
||||
. ____ .-.
|
||||
.-"` `",( __\_
|
||||
.-==:;-._ .' .-. `'.
|
||||
|
@ -18,4 +19,4 @@ env options:
|
|||
\ _/---'\ ( `"`
|
||||
/.`._ ) \ `; Sour.is Paste
|
||||
\`-/.' `"`
|
||||
`"\`-.</code></pre></div></noscript>
|
||||
`"\`-.</code></pre></div></noscript></body></html>
|
|
@ -5,7 +5,7 @@ if [ "$1" = "-h" ]; then
|
|||
usage: echo /etc/passwd | ./paste.sh
|
||||
|
||||
env options:
|
||||
PASTE_URL - Set the url base for paste operations (default: HTTPS://sour.is/paste)
|
||||
PASTE_URL - Set the url base for paste operations (default: HTTPS://paste.dn42.us)
|
||||
PASTE_GZIP - 0 = No Compression, 1 = Use gzip compression (default: 0)
|
||||
PASTE_BURN - 0 = No Burn on Read, 1 = Burn on read (default: 0)
|
||||
PASTE_DATE - Value to be used when setting expire date. (default: next-week)
|
||||
|
@ -13,7 +13,7 @@ EOL
|
|||
exit
|
||||
fi
|
||||
|
||||
PASTE_URL=${PASTE_URL-"https://sour.is/paste"}
|
||||
PASTE_URL=${PASTE_URL-"https://paste.dn42.us"}
|
||||
PASTE_BURN=${PASTE_BURN-0}
|
||||
PASTE_DATE=${PASTE_DATE-"next-week"}
|
||||
PASTE_GZIP=${PASTE_GZIP-0}
|
||||
|
@ -29,7 +29,7 @@ HASH=$((echo -e "exp:\t$(date +%s -d ${PASTE_DATE})"; \
|
|||
[ "$PASTE_GZIP" -eq "1" ] && echo -e "zip:\ttrue"; \
|
||||
echo; \
|
||||
cat /dev/stdin | $GZBIN | openssl aes-256-cbc -e -a -k $PASS) | \
|
||||
curl -s -X POST ${PASTE_URL}/api/ --data-binary @-)
|
||||
curl -s -X POST ${PASTE_URL}/paste --data-binary @-)
|
||||
|
||||
HASH_OK=$(echo $HASH | cut -c1-2)
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
207
routes/paste.go
207
routes/paste.go
|
@ -2,11 +2,8 @@ package routes
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"sour.is/x/httpsrv"
|
||||
"sour.is/x/ident"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
|
@ -15,29 +12,37 @@ import (
|
|||
"golang.org/x/sys/unix"
|
||||
"crypto/rand"
|
||||
"strconv"
|
||||
"sour.is/x/paste/model"
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var store string
|
||||
var randBytes int
|
||||
|
||||
func init() {
|
||||
httpsrv.HttpRegister("paste", httpsrv.HttpRoutes{
|
||||
{ "Paste", "GET", "/paste/rng", GetRandom, },
|
||||
{ "Paste", "GET", "/paste/{id}", GetPaste, },
|
||||
{ "Paste", "GET", "/paste/get/{id}", GetPaste, },
|
||||
{ "Paste", "POST", "/paste", PostPaste, },
|
||||
// { "Paste", "DELETE", "/paste/{id}", DeletePaste, },
|
||||
httpsrv.RegisterModule("paste", setConfig)
|
||||
|
||||
{ "Paste", "GET", "/api/rng", GetRandom, },
|
||||
{ "Paste", "GET", "/api/{id}", GetPaste, },
|
||||
{ "Paste", "GET", "/api/get/{id}", GetPaste, },
|
||||
{ "Paste", "POST", "/api", PostPaste, },
|
||||
// { "Paste", "DELETE", "/api/{id}", DeletePaste, },
|
||||
httpsrv.HttpRegister("paste", httpsrv.HttpRoutes{
|
||||
{ "getRandom", "GET", "/paste/rng", getRandom, },
|
||||
{ "getPaste", "GET", "/paste/{id}", getPaste, },
|
||||
{ "getPaste", "GET", "/paste/get/{id}", getPaste, },
|
||||
{ "postPaste", "POST", "/paste", postPaste, },
|
||||
|
||||
{ "getRandom", "GET", "/api/rng", getRandom, },
|
||||
{ "getPaste", "GET", "/api/{id}", getPaste, },
|
||||
{ "getPaste", "GET", "/api/get/{id}", getPaste, },
|
||||
{ "postPaste", "POST", "/api", postPaste, },
|
||||
})
|
||||
|
||||
httpsrv.AssetRegister("paste", httpsrv.AssetRoutes{
|
||||
{ "Assets", "/", httpsrv.FsHtml5( assetFS() ) },
|
||||
})
|
||||
}
|
||||
|
||||
func SetConfig (config map[string]string) {
|
||||
func setConfig (config map[string]string) {
|
||||
|
||||
store = "data/"
|
||||
if config["store"] != "" {
|
||||
|
@ -45,16 +50,17 @@ func SetConfig (config map[string]string) {
|
|||
}
|
||||
|
||||
if !chkStore(store) {
|
||||
log.Fatalf("[routes::Paste] Store location [%s] does not exist or is not writable.", store)
|
||||
log.Criticalf("[routes::Paste] Store location [%s] does not exist or is not writable.", store)
|
||||
}
|
||||
log.Noticef("[paste::getPaste] Store location set to [%s]", store)
|
||||
|
||||
randBytes = 1024
|
||||
if config["random"] != "" {
|
||||
randBytes, _ = strconv.Atoi(config["random"])
|
||||
log.Noticef("[paste:getRandom] set random size to %d bytes", randBytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func chkStore(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
if err == nil { return true }
|
||||
|
@ -82,120 +88,107 @@ func chkGone(path string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func GetRandom(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("content-type","application/octet-stream")
|
||||
func getRandom(w http.ResponseWriter, r *http.Request) {
|
||||
s := make([]byte, randBytes)
|
||||
rand.Read(s)
|
||||
|
||||
w.Header().Set("content-type","application/octet-stream")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(s)
|
||||
}
|
||||
// swagger:route GET /paste pasteIndex
|
||||
//
|
||||
// Welcomes user.
|
||||
//
|
||||
// This welcome user based on identity used.
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
//
|
||||
// Produces:
|
||||
// - application/json
|
||||
//
|
||||
// Schemes: http
|
||||
//
|
||||
// Security:
|
||||
// user_token:
|
||||
//
|
||||
// Responses:
|
||||
// default: genericError
|
||||
// 200: someResponse
|
||||
// 422: validationError
|
||||
|
||||
func GetPaste(w http.ResponseWriter, r *http.Request, i ident.Ident) {
|
||||
func getPaste(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
p, err := model.GetPaste(id)
|
||||
log.Printf("%#v",p)
|
||||
|
||||
if !chkFile(store + id) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("ERR Not Found"))
|
||||
return
|
||||
}
|
||||
|
||||
if chkGone(store + id) {
|
||||
w.WriteHeader(http.StatusGone)
|
||||
w.Write([]byte("ERR Gone"))
|
||||
return
|
||||
}
|
||||
|
||||
head, err := os.Open(store + id)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("Bad Request"))
|
||||
return
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer head.Close()
|
||||
|
||||
if p.Id == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("Not Found"))
|
||||
return
|
||||
}
|
||||
keep := true
|
||||
scanner := bufio.NewScanner(head)
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
log.Debug(txt)
|
||||
|
||||
//switch accept {
|
||||
//case "text/plain":
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if _, err := w.Write([]byte(p.String()));
|
||||
err != nil {
|
||||
panic(err)
|
||||
}
|
||||
/*
|
||||
case "application/json":
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(p);
|
||||
err != nil {
|
||||
panic(err)
|
||||
if (txt == "") {
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
w.Write([]byte(""))
|
||||
if strings.HasPrefix(txt, "exp:") {
|
||||
now := time.Now().Unix()
|
||||
exp, err := strconv.ParseInt(strings.TrimSpace(strings.TrimPrefix(txt, "exp:")), 10, 64)
|
||||
if err != nil {
|
||||
log.Warning(err)
|
||||
}
|
||||
|
||||
log.Debugf("%d > %d", now, exp)
|
||||
if now > exp {
|
||||
w.WriteHeader(http.StatusGone)
|
||||
w.Write([]byte("ERR Gone"))
|
||||
|
||||
deleteFile(store + id)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(txt, "burn:") {
|
||||
burn := strings.TrimSpace(strings.TrimPrefix(txt, "burn:"))
|
||||
|
||||
if burn == "true" {
|
||||
keep = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file, _ := os.Open(store + id)
|
||||
defer file.Close()
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
scanner = bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
w.Write(scanner.Bytes())
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
if !keep {
|
||||
deleteFile(store + id)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func PostPaste(w http.ResponseWriter, r *http.Request, i ident.Ident) {
|
||||
var p model.Paste
|
||||
func postPaste(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
|
||||
checkErr(err, w)
|
||||
|
||||
err = r.Body.Close()
|
||||
checkErr(err, w)
|
||||
s256 := sha256.Sum256(body)
|
||||
id := base64.RawURLEncoding.EncodeToString(s256[12:])
|
||||
|
||||
if err := json.Unmarshal(body, &p); err != nil {
|
||||
w.WriteHeader(422) // unprocessable entity
|
||||
err = json.NewEncoder(w).Encode(err)
|
||||
checkErr(err, w)
|
||||
}
|
||||
ioutil.WriteFile(store + id, body, 0644)
|
||||
|
||||
p.CreatedBy = i.Identity()
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte("OK " + id))
|
||||
|
||||
np, err := model.CreatePaste(p)
|
||||
checkErr(err, w)
|
||||
|
||||
// accept := negotiate.Negotiate("text/plain,application/json;q=0.7", r)
|
||||
|
||||
//switch accept {
|
||||
//case "text/plain":
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
if _, err := w.Write([]byte(np.String()));
|
||||
err != nil {
|
||||
panic(err)
|
||||
}
|
||||
/*
|
||||
case "application/json":
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
if err := json.NewEncoder(w).Encode(np);
|
||||
err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
w.Write([]byte(""))
|
||||
}
|
||||
*/
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
func checkErr(err error, w http.ResponseWriter) {
|
||||
|
@ -204,4 +197,8 @@ func checkErr(err error, w http.ResponseWriter) {
|
|||
json.NewEncoder(w).Encode(err);
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteFile(path string) {
|
||||
ioutil.WriteFile(path, []byte(""), 0644)
|
||||
}
|
195
routes/paste.go.db
Normal file
195
routes/paste.go.db
Normal file
|
@ -0,0 +1,195 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"sour.is/x/httpsrv"
|
||||
"os"
|
||||
"golang.org/x/sys/unix"
|
||||
"log"
|
||||
"net/http"
|
||||
"sour.is/x/ident"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"crypto/sha256"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"bufio"
|
||||
"github.com/gorilla/mux"
|
||||
"strings"
|
||||
"time"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var store string
|
||||
var randBytes int
|
||||
|
||||
func init() {
|
||||
httpsrv.RegisterModule("paste", SetConfig)
|
||||
|
||||
httpsrv.IdentRegister("paste", httpsrv.IdentRoutes{
|
||||
{ "Paste", "GET", "/paste/rng", GetRandom, },
|
||||
{ "Paste", "GET", "/paste/{id}", GetPaste, },
|
||||
{ "Paste", "GET", "/paste/get/{id}", GetPaste, },
|
||||
{ "Paste", "POST", "/paste", PostPaste, },
|
||||
{ "Paste", "DELETE", "/paste/{id}", DeletePaste, },
|
||||
|
||||
{ "Paste", "GET", "/api/rng", GetRandom, },
|
||||
{ "Paste", "GET", "/api/{id}", GetPaste, },
|
||||
{ "Paste", "GET", "/api/get/{id}", GetPaste, },
|
||||
{ "Paste", "POST", "/api", PostPaste, },
|
||||
{ "Paste", "DELETE", "/api/{id}", DeletePaste, },
|
||||
})
|
||||
}
|
||||
|
||||
func SetConfig (config map[string]string) {
|
||||
|
||||
store = "data/"
|
||||
if config["store"] != "" {
|
||||
store = config["store"]
|
||||
}
|
||||
|
||||
if !chkStore(store) {
|
||||
log.Fatalf("[routes::Paste] Store location [%s] does not exist or is not writable.", store)
|
||||
}
|
||||
|
||||
randBytes = 1024
|
||||
if config["random"] != "" {
|
||||
randBytes, _ = strconv.Atoi(config["random"])
|
||||
}
|
||||
}
|
||||
|
||||
func chkStore(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
if err == nil { return true }
|
||||
if os.IsNotExist(err) { return false }
|
||||
if !file.IsDir() { return false }
|
||||
if unix.Access(path, unix.W_OK & unix.R_OK) != nil { return false }
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func chkFile(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
if err == nil { return true }
|
||||
if os.IsNotExist(err) { return false }
|
||||
if file.IsDir() { return false }
|
||||
if unix.Access(path, unix.W_OK & unix.R_OK) != nil { return false }
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func chkGone(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
if err != nil { return true }
|
||||
if file.Size() == 0 { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
func GetRandom(w http.ResponseWriter, r *http.Request, i ident.Ident) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("content-type","application/octet-stream")
|
||||
s := make([]byte, randBytes)
|
||||
rand.Read(s)
|
||||
|
||||
w.Write(s)
|
||||
}
|
||||
|
||||
func GetPaste(w http.ResponseWriter, r *http.Request, i ident.Ident) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
if !chkFile(store + id) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("ERR Not Found"))
|
||||
return
|
||||
}
|
||||
|
||||
if chkGone(store + id) {
|
||||
w.WriteHeader(http.StatusGone)
|
||||
w.Write([]byte("ERR Gone"))
|
||||
return
|
||||
}
|
||||
|
||||
head, err := os.Open(store + id)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer head.Close()
|
||||
|
||||
keep := true
|
||||
|
||||
scanner := bufio.NewScanner(head)
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
log.Println(txt)
|
||||
if (txt == "") {
|
||||
break
|
||||
}
|
||||
|
||||
if strings.HasPrefix(txt, "exp:") {
|
||||
now := time.Now().Unix()
|
||||
exp, err := strconv.ParseInt(strings.TrimSpace(strings.TrimPrefix(txt, "exp:")), 10, 64)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
if now > exp {
|
||||
log.Printf("%d > %d", now, exp)
|
||||
w.WriteHeader(http.StatusGone)
|
||||
w.Write([]byte("ERR Gone"))
|
||||
|
||||
Delete(store + id)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(txt, "burn:") {
|
||||
burn := strings.TrimSpace(strings.TrimPrefix(txt, "burn:"))
|
||||
|
||||
if burn == "true" {
|
||||
keep = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file, _ := os.Open(store + id)
|
||||
defer file.Close()
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
scanner = bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
w.Write(scanner.Bytes())
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
if !keep {
|
||||
Delete(store + id)
|
||||
}
|
||||
}
|
||||
|
||||
func PostPaste(w http.ResponseWriter, r *http.Request, i ident.Ident) {
|
||||
body, _ := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
|
||||
|
||||
s256 := sha256.Sum256(body)
|
||||
id := base64.RawURLEncoding.EncodeToString(s256[12:])
|
||||
|
||||
ioutil.WriteFile(store + id, body, 0644)
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte("OK " + id))
|
||||
}
|
||||
|
||||
func DeletePaste(w http.ResponseWriter, r *http.Request, i ident.Ident) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
Delete(store + id)
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
w.Write([]byte("OK"))
|
||||
}
|
||||
|
||||
func Delete(path string) {
|
||||
ioutil.WriteFile(path, []byte(""), 0644)
|
||||
}
|
Loading…
Reference in New Issue
Block a user