1
0
mirror of https://github.com/Tygs/0bin.git synced 2023-08-10 21:13:00 +03:00
0bin/zerobin/routes.py

190 lines
5.2 KiB
Python
Raw Normal View History

2012-05-14 19:17:49 +04:00
"""
Script including controller, rooting, and dependency management.
2012-05-14 19:17:49 +04:00
"""
import os
import sys
2015-05-10 20:19:02 +03:00
2020-08-11 17:37:03 +03:00
import _thread as thread
2015-05-10 20:19:02 +03:00
2020-08-11 17:37:03 +03:00
import urllib.parse as urlparse
2015-05-10 20:19:02 +03:00
2012-05-14 19:17:49 +04:00
from datetime import datetime, timedelta
2020-08-11 17:37:03 +03:00
from zerobin import __version__
from zerobin.utils import settings, SettingsValidationError, dmerge
2012-05-14 19:17:49 +04:00
import bottle
2020-08-11 17:37:03 +03:00
from bottle import Bottle, static_file, view, request, HTTPResponse
2012-05-14 19:17:49 +04:00
2015-05-10 20:19:02 +03:00
from zerobin.paste import Paste
2012-05-14 19:17:49 +04:00
app = Bottle()
GLOBAL_CONTEXT = {
2020-08-11 12:55:29 +03:00
"settings": settings,
2020-08-11 17:37:03 +03:00
"VERSION": __version__,
2020-08-11 12:55:29 +03:00
"pastes_count": Paste.get_pastes_count(),
"refresh_counter": datetime.now(),
2012-05-14 19:17:49 +04:00
}
2012-05-14 19:17:49 +04:00
2020-08-11 12:55:29 +03:00
@app.route("/")
@view("home")
2012-05-14 19:17:49 +04:00
def index():
return GLOBAL_CONTEXT
2020-08-11 12:55:29 +03:00
@app.route("/faq/")
@view("faq")
2012-05-21 19:14:01 +04:00
def faq():
2012-05-21 14:25:24 +04:00
return GLOBAL_CONTEXT
2020-08-11 12:55:29 +03:00
@app.route("/paste/create", method="POST")
2012-05-14 19:17:49 +04:00
def create_paste():
2020-08-11 12:55:29 +03:00
try:
2013-01-19 21:54:51 +04:00
body = urlparse.parse_qs(request.body.read(int(settings.MAX_SIZE * 1.1)))
except ValueError:
2020-08-11 12:55:29 +03:00
return {"status": "error", "message": "Wrong data payload."}
2012-05-14 19:17:49 +04:00
try:
2020-08-11 12:55:29 +03:00
content = "".join(x.decode("utf8") for x in body[b"content"])
except (UnicodeDecodeError, KeyError):
2020-08-11 12:55:29 +03:00
return {
"status": "error",
"message": "Encoding error: the paste couldn't be saved.",
}
2012-05-14 19:17:49 +04:00
if '{"iv":' not in content: # reject silently non encrypted content
2020-08-11 12:55:29 +03:00
return {"status": "error", "message": "Wrong data payload."}
2015-05-10 20:19:02 +03:00
# check size of the paste. if more than settings return error
# without saving paste. prevent from unusual use of the
# system. need to be improved
if 0 < len(content) < settings.MAX_SIZE:
2020-08-11 12:55:29 +03:00
expiration = body.get(b"expiration", [b"burn_after_reading"])[0]
paste = Paste(
expiration=expiration.decode("utf8"),
content=content,
uuid_length=settings.PASTE_ID_LENGTH,
)
2015-05-10 20:19:02 +03:00
paste.save()
# display counter
if settings.DISPLAY_COUNTER:
2020-08-11 12:55:29 +03:00
# increment paste counter
2015-05-10 20:19:02 +03:00
paste.increment_counter()
# if refresh time elapsed pick up new counter value
now = datetime.now()
2020-08-11 12:55:29 +03:00
timeout = GLOBAL_CONTEXT["refresh_counter"] + timedelta(
seconds=settings.REFRESH_COUNTER
)
2015-05-10 20:19:02 +03:00
if timeout < now:
2020-08-11 12:55:29 +03:00
GLOBAL_CONTEXT["pastes_count"] = Paste.get_pastes_count()
GLOBAL_CONTEXT["refresh_counter"] = now
2015-05-10 20:19:02 +03:00
2020-08-11 17:37:03 +03:00
return {"status": "ok", "paste": paste.uuid, "owner_key": paste.owner_key}
2012-05-14 19:17:49 +04:00
2020-08-11 12:55:29 +03:00
return {
"status": "error",
"message": "Serveur error: the paste couldn't be saved. " "Please try later.",
}
2012-05-14 19:17:49 +04:00
2020-08-11 17:37:03 +03:00
@app.route("/paste/:paste_id", method="GET")
2020-08-11 12:55:29 +03:00
@view("paste")
2012-05-14 19:17:49 +04:00
def display_paste(paste_id):
now = datetime.now()
keep_alive = False
try:
paste = Paste.load(paste_id)
# Delete the paste if it expired:
2015-05-10 20:19:02 +03:00
if not isinstance(paste.expiration, datetime):
2012-05-14 19:17:49 +04:00
# burn_after_reading contains the paste creation date
# if this read appends 10 seconds after the creation date
# we don't delete the paste because it means it's the redirection
# to the paste that happens during the paste creation
try:
2020-08-11 12:55:29 +03:00
keep_alive = paste.expiration.split("#")[1]
keep_alive = datetime.strptime(keep_alive, "%Y-%m-%d %H:%M:%S.%f")
2012-05-14 19:17:49 +04:00
keep_alive = now < keep_alive + timedelta(seconds=10)
except IndexError:
keep_alive = False
if not keep_alive:
paste.delete()
elif paste.expiration < now:
paste.delete()
raise ValueError()
except (TypeError, ValueError):
return error404(ValueError)
2020-08-11 12:55:29 +03:00
context = {"paste": paste, "keep_alive": keep_alive}
2012-05-14 19:17:49 +04:00
return dmerge(context, GLOBAL_CONTEXT)
2020-08-11 17:37:03 +03:00
@app.route("/paste/:paste_id", method="DELETE")
def delete_paste(paste_id):
try:
paste = Paste.load(paste_id)
except (TypeError, ValueError):
return error404(ValueError)
if paste.owner_key != request.forms.get("owner_key", None):
return HTTPResponse(status=403, body="Wrong owner key")
paste.delete()
return {
"status": "ok",
"message": "Paste deleted",
}
2012-05-14 19:17:49 +04:00
@app.error(404)
2020-08-11 12:55:29 +03:00
@view("404")
2012-05-14 19:17:49 +04:00
def error404(code):
return GLOBAL_CONTEXT
2020-08-11 12:55:29 +03:00
@app.route("/static/<filename:path>")
def server_static(filename):
return static_file(filename, root=settings.STATIC_FILES_ROOT)
2020-08-11 12:55:29 +03:00
def get_app(debug=None, settings_file="", compressed_static=None, settings=settings):
"""
Return a tuple (settings, app) configured using passed
parameters and/or a setting file.
"""
2020-08-11 12:55:29 +03:00
settings_file = settings_file or os.environ.get("ZEROBIN_SETTINGS_FILE")
2012-05-14 19:17:49 +04:00
if settings_file:
settings.update_with_file(os.path.realpath(settings_file))
2012-05-14 19:17:49 +04:00
if settings.PASTE_ID_LENGTH < 4:
2020-08-11 12:55:29 +03:00
raise SettingsValidationError("PASTE_ID_LENGTH cannot be lower than 4")
2012-05-17 13:13:40 +04:00
if compressed_static is not None:
settings.COMPRESSED_STATIC_FILES = compressed_static
if debug is not None:
settings.DEBUG = debug
2012-05-14 19:17:49 +04:00
# make sure the templates can be loaded
for d in reversed(settings.TEMPLATE_DIRS):
bottle.TEMPLATE_PATH.insert(0, d)
if settings.DEBUG:
bottle.debug(True)
return settings, app