|
@@ -3,20 +3,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
+from hashlib import blake2b
|
|
|
+from io import BufferedRandom
|
|
|
import os
|
|
|
from threading import Thread
|
|
|
from typing import Union
|
|
|
from bottle import (
|
|
|
route, request, response,
|
|
|
- redirect,
|
|
|
+ redirect, abort,
|
|
|
template, static_file,
|
|
|
FormsDict, HTTPError,
|
|
|
)
|
|
|
from psycopg import Cursor, connect
|
|
|
from psycopg.rows import TupleRow
|
|
|
+from uuid import uuid4
|
|
|
|
|
|
-from .validate import validate, validate_parameter, validate_url
|
|
|
-from .hash_util import blake, bytes_to_base32, normalize_base32
|
|
|
+from .validate import validate, validate_file, validate_parameter, validate_url
|
|
|
+from .hash_util import DIGEST_SIZE_BYTES, blake, bytes_to_base32, normalize_base32
|
|
|
from .route_decorators import normalize, poison, cursor
|
|
|
from .query_to_xml import get_categories, get_groups, get_products, get_tags
|
|
|
from .CachedLoadingPage import CachedLoadingPage
|
|
@@ -110,6 +113,39 @@ def save(content: bytes, root='app/rest/static') -> str:
|
|
|
f.write(content)
|
|
|
return _b32
|
|
|
|
|
|
+def save_upload(content: BufferedRandom, root='app/rest/static') -> str:
|
|
|
+ tmpdir = '/tmp/upload'
|
|
|
+ try:
|
|
|
+ os.mkdir(tmpdir, mode=0o700, dir_fd=None)
|
|
|
+ except FileExistsError:
|
|
|
+ pass
|
|
|
+ unique = uuid4()
|
|
|
+ fd = os.open(f'{tmpdir}/{unique.hex}', os.O_WRONLY | os.O_TRUNC | os.O_CREAT, 0o600)
|
|
|
+ with open(fd, "wb") as f:
|
|
|
+ while content.peek(1):
|
|
|
+ seg = content.read(1024)
|
|
|
+ f.write(seg)
|
|
|
+
|
|
|
+ fd = os.open(f'{tmpdir}/{unique.hex}', os.O_RDONLY, 0o600)
|
|
|
+ with open(fd, "rb") as f:
|
|
|
+ f.seek(0)
|
|
|
+ _blake = blake2b(usedforsecurity=False, digest_size=DIGEST_SIZE_BYTES, person='upload'.encode('utf-8'))
|
|
|
+ while f.peek(1):
|
|
|
+ _blake.update(f.read(1024))
|
|
|
+
|
|
|
+ _bytes = _blake.digest()
|
|
|
+ _b32 = bytes_to_base32(_bytes)
|
|
|
+
|
|
|
+ directory = f'{root}/{_b32}'
|
|
|
+ try:
|
|
|
+ os.mkdir(directory, mode=0o700, dir_fd=None)
|
|
|
+ except FileExistsError:
|
|
|
+ pass
|
|
|
+
|
|
|
+ os.replace(f'{tmpdir}/{unique.hex}', f'{directory}/{_b32}.file')
|
|
|
+
|
|
|
+ return _b32
|
|
|
+
|
|
|
|
|
|
@route('/clip', method=['GET', 'POST'])
|
|
|
def clip():
|
|
@@ -161,10 +197,6 @@ def clip():
|
|
|
)
|
|
|
|
|
|
|
|
|
-@route('/clip/', method='GET')
|
|
|
-def _clip(): return redirect(f'/clip')
|
|
|
-
|
|
|
-
|
|
|
@route('/clip/<filename:path>', method='GET')
|
|
|
def get_clip(filename):
|
|
|
filename = filename and normalize_base32(filename)
|
|
@@ -179,6 +211,33 @@ def get_clip(filename):
|
|
|
return static_file('/'.join([filename,]*2) + '.file', root='app/rest/static')
|
|
|
|
|
|
|
|
|
+@route('/upload', method=['GET', 'POST'])
|
|
|
+def upload():
|
|
|
+ if request.method == 'GET':
|
|
|
+ _hash = request.params.hash
|
|
|
+ if _hash:
|
|
|
+ _hash = normalize_base32(_hash)
|
|
|
+
|
|
|
+ link = f'{LOCATION}/upload/{_hash}' if _hash else f'{LOCATION}/upload'
|
|
|
+ response.content_type = 'text/html; charset=utf-8'
|
|
|
+ form = template('file-form', action='/upload', method='post')
|
|
|
+ return template('upload', form=form, link=link)
|
|
|
+
|
|
|
+ if request.method == 'POST':
|
|
|
+ if 'paste' not in request.files:
|
|
|
+ return abort(400, "Parameter 'paste' must be specified")
|
|
|
+
|
|
|
+ _b32 = save_upload(request.files['paste'].file)
|
|
|
+ return redirect(f'/upload?hash={_b32}')
|
|
|
+
|
|
|
+
|
|
|
+@route('/upload/<filename:path>', method='GET')
|
|
|
+def get_upload(filename):
|
|
|
+ filename = filename and normalize_base32(filename)
|
|
|
+
|
|
|
+ return validate_file(filename)
|
|
|
+
|
|
|
+
|
|
|
@route('/goto', method=['GET', 'POST'])
|
|
|
def goto():
|
|
|
if request.method == 'GET':
|
|
@@ -214,11 +273,10 @@ def goto():
|
|
|
return redirect(f'/goto?hash={_b32}')
|
|
|
|
|
|
|
|
|
-@route('/goto/', method='GET')
|
|
|
-def _goto(): return redirect(f'/goto')
|
|
|
-
|
|
|
-
|
|
|
@route('/goto/<filename:path>', method='GET')
|
|
|
def redirect_goto(filename):
|
|
|
filename = filename and normalize_base32(filename)
|
|
|
return redirect(f'/goto?hash={filename}&go=true')
|
|
|
+
|
|
|
+@route('/<any>/', method='GET')
|
|
|
+def redirect_trailing_slash(any): return redirect(f'/{any}')
|