|
@@ -3,14 +3,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
+from io import BufferedReader
|
|
|
import os
|
|
|
from threading import Thread
|
|
|
-from bottle import route, request, response, template, static_file, FormsDict
|
|
|
+from bottle import (
|
|
|
+ route, request, response,
|
|
|
+ redirect, abort,
|
|
|
+ template, static_file,
|
|
|
+ FormsDict, HTTPError, HTTPResponse,
|
|
|
+)
|
|
|
from psycopg import connect
|
|
|
|
|
|
from app.data.filter import get_filter, get_query_param
|
|
|
|
|
|
-from .hash_util import hex_to_hash
|
|
|
+from .hash_util import blake, bytes_to_base32, hash_to_base32, hex_to_hash, normalize_base32
|
|
|
from .route_decorators import normalize, normalize_query, poison, cursor
|
|
|
from .query_to_xml import get_categories, get_groups, get_products, get_tags
|
|
|
from .CachedLoadingPage import CachedLoadingPage
|
|
@@ -93,3 +99,55 @@ def products(cur):
|
|
|
def tags(cur):
|
|
|
response.content_type = 'application/xhtml+xml; charset=utf-8'
|
|
|
return get_tags(cur, request.query)
|
|
|
+
|
|
|
+CLIP_SIZE_LIMIT = 65535
|
|
|
+
|
|
|
+@route('/clip', method=['GET', 'POST'])
|
|
|
+def clip():
|
|
|
+ if request.method == 'GET':
|
|
|
+ if request.query:
|
|
|
+ return redirect('/clip')
|
|
|
+ response.content_type = 'text/html; charset=utf-8'
|
|
|
+ form = template('clip-form', action='/clip', method='post', content=None)
|
|
|
+ return template('paste', form=form, link=None)
|
|
|
+
|
|
|
+ if request.method == 'POST':
|
|
|
+ if 'paste' not in request.params:
|
|
|
+ return abort(400, "Missing arameter: 'paste'")
|
|
|
+ content = request.params['paste'].encode('utf-8')
|
|
|
+ if len(content) > CLIP_SIZE_LIMIT:
|
|
|
+ return abort(413, f"Paste size can not exceed {CLIP_SIZE_LIMIT}")
|
|
|
+ _bytes = blake(content, person='clip'.encode('utf-8'))
|
|
|
+ _b32 = bytes_to_base32(_bytes)
|
|
|
+ directory = f'app/rest/static/{_b32}'
|
|
|
+ try:
|
|
|
+ os.mkdir(directory, mode=0o700, dir_fd=None)
|
|
|
+ except FileExistsError:
|
|
|
+ pass
|
|
|
+ fd = os.open(f'{directory}/{_b32}.file', os.O_WRONLY | os.O_CREAT, 0o600)
|
|
|
+ with open(fd, "wb") as f:
|
|
|
+ f.write(content)
|
|
|
+
|
|
|
+ form = template('clip-form', action='/clip', method='post', content=content)
|
|
|
+ response.content_type = 'text/html; charset=utf-8'
|
|
|
+ return HTTPResponse(template('paste', form=form, link=f'/clip/{_b32}'), 201)
|
|
|
+
|
|
|
+@route('/clip/<filename:path>', method='GET')
|
|
|
+def get_clip(filename):
|
|
|
+ f = normalize_base32(filename)
|
|
|
+
|
|
|
+ ret = static_file('/'.join([f,]*2) + '.file', root='app/rest/static')
|
|
|
+ if isinstance(ret, HTTPError):
|
|
|
+ return ret
|
|
|
+
|
|
|
+ if ret.content_length > CLIP_SIZE_LIMIT:
|
|
|
+ return abort(418, f"Paste size exceeds {CLIP_SIZE_LIMIT}")
|
|
|
+
|
|
|
+ content: str = ret.body.read() if isinstance(ret.body, BufferedReader) else ret.body.encode('utf-8')
|
|
|
+
|
|
|
+ _bytes = blake(content, person='clip'.encode('utf-8'))
|
|
|
+ _b32 = bytes_to_base32(_bytes)
|
|
|
+ if _b32 != f:
|
|
|
+ return abort(410, f"Paste content differs")
|
|
|
+
|
|
|
+ return HTTPResponse(content, 200)
|