|
@@ -3,21 +3,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
-from io import BufferedReader
|
|
|
import os
|
|
|
from threading import Thread
|
|
|
from typing import Union
|
|
|
from bottle import (
|
|
|
route, request, response,
|
|
|
- redirect, abort,
|
|
|
+ redirect,
|
|
|
template, static_file,
|
|
|
- FormsDict, HTTPError, LocalRequest,
|
|
|
+ FormsDict, HTTPError,
|
|
|
)
|
|
|
from psycopg import Cursor, connect
|
|
|
from psycopg.rows import TupleRow
|
|
|
|
|
|
-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 .validate import validate, validate_parameter, validate_url
|
|
|
+from .hash_util import 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
|
|
|
from .Cache import Cache
|
|
@@ -89,49 +89,13 @@ def tags(cur: Cursor[TupleRow], key: Union[int, str], forms: FormsDict, cache: C
|
|
|
response.content_type = 'application/xhtml+xml; charset=utf-8'
|
|
|
return get_tags(cur, forms)
|
|
|
|
|
|
-CLIP_SIZE_LIMIT = 65535
|
|
|
+
|
|
|
SCHEME = "https://"
|
|
|
HOST = ""
|
|
|
DOMAIN = "shandan.one"
|
|
|
PORT = ""
|
|
|
LOCATION = SCHEME + (f"{HOST}." if HOST else "") + DOMAIN + (f":{PORT}" if PORT else "")
|
|
|
|
|
|
-def validate(filename: str) -> bytes:
|
|
|
- ret = static_file('/'.join([filename,]*2) + '.file', root='app/rest/static')
|
|
|
- if isinstance(ret, HTTPError):
|
|
|
- return abort(404, f"No such paste: {filename}")
|
|
|
-
|
|
|
- if ret.content_length > CLIP_SIZE_LIMIT:
|
|
|
- return abort(418, f"Paste size exceeds {CLIP_SIZE_LIMIT}")
|
|
|
-
|
|
|
- content: bytes = 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 != filename:
|
|
|
- return abort(410, f"Paste content differs")
|
|
|
- return content
|
|
|
-
|
|
|
-
|
|
|
-def validate_parameter(request: LocalRequest, name: str) -> bytes:
|
|
|
- if name not in request.params:
|
|
|
- return abort(400, f"Missing parameter: '{name}'")
|
|
|
-
|
|
|
-
|
|
|
- OVERHEAD = 1024
|
|
|
- content: bytes = request.query.get(name, None)
|
|
|
- content_length = request.content_length
|
|
|
- if content_length == -1:
|
|
|
- return abort(418, f"Content-Length must be specified")
|
|
|
- if content_length > CLIP_SIZE_LIMIT + OVERHEAD:
|
|
|
- return abort(418, f"Content-Length can not exceed {CLIP_SIZE_LIMIT+OVERHEAD}")
|
|
|
-
|
|
|
-
|
|
|
- content: bytes = (content or request.params[name]).encode('latin-1')
|
|
|
- if len(content) > CLIP_SIZE_LIMIT:
|
|
|
- return abort(418, f"Paste can not exceed {CLIP_SIZE_LIMIT}")
|
|
|
- return content
|
|
|
-
|
|
|
|
|
|
def save(content: bytes, root='app/rest/static') -> str:
|
|
|
_bytes = blake(content, person='clip'.encode('utf-8'))
|
|
@@ -226,8 +190,8 @@ def goto():
|
|
|
content = None
|
|
|
|
|
|
if content and request.params.go == 'true':
|
|
|
-
|
|
|
- return redirect(content)
|
|
|
+ target = validate_url(content)
|
|
|
+ return redirect(target)
|
|
|
|
|
|
link = f'{LOCATION}/goto/{_hash}' if content else f'{LOCATION}/goto'
|
|
|
disabled = True if content else False
|
|
@@ -244,6 +208,9 @@ def goto():
|
|
|
if request.method == 'POST':
|
|
|
content = validate_parameter(request, 'url')
|
|
|
_b32 = save(content)
|
|
|
+
|
|
|
+
|
|
|
+ _ = validate_url(content.decode('utf-8'))
|
|
|
return redirect(f'/goto?hash={_b32}')
|
|
|
|
|
|
|