2023-12-26 21:13:07 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
"""
|
|
|
|
XYGT.CC - Routes
|
|
|
|
A no-bullshit, anonymous, temporary file host.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import io
|
|
|
|
import random
|
|
|
|
from io import BytesIO
|
|
|
|
import magic
|
2023-12-26 20:16:58 +01:00
|
|
|
from flask import render_template, request, send_file, redirect, flash
|
|
|
|
from flask_login import login_user, current_user, logout_user, login_required
|
|
|
|
from flask_wtf import FlaskForm
|
|
|
|
from wtforms import StringField, PasswordField, SubmitField, BooleanField
|
|
|
|
from wtforms.validators import DataRequired, Length, EqualTo
|
2023-12-21 17:19:22 +01:00
|
|
|
from werkzeug.datastructures import FileStorage
|
2023-12-21 18:03:03 +01:00
|
|
|
from werkzeug.utils import secure_filename
|
2023-12-26 21:13:07 +01:00
|
|
|
from app import app, worker, bcrypt, loginManager, csrf
|
|
|
|
from app.models import User
|
2024-01-05 12:17:46 +01:00
|
|
|
from config import Config, Errors, quotes
|
2023-12-14 12:39:23 +01:00
|
|
|
|
2023-12-26 20:16:58 +01:00
|
|
|
class RegistrationForm(FlaskForm):
|
|
|
|
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=16)])
|
|
|
|
password = PasswordField('Password', validators=[DataRequired(), Length(min=8, max=32)])
|
|
|
|
password2 = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
|
|
|
|
tnc = BooleanField('I agree to the Terms and Conditions', validators=[DataRequired()])
|
|
|
|
submit = SubmitField('Register')
|
|
|
|
|
|
|
|
def validate_username(self, username):
|
|
|
|
user = Config.users.find_one({"username": username.data})
|
|
|
|
if user:
|
|
|
|
raise ValueError("That username is taken. Try another.")
|
|
|
|
|
|
|
|
class LoginForm(FlaskForm):
|
|
|
|
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=16)])
|
|
|
|
password = PasswordField('Password', validators=[DataRequired(), Length(min=8, max=32)])
|
|
|
|
submit = SubmitField('Login')
|
|
|
|
|
|
|
|
@loginManager.user_loader
|
|
|
|
def load_user(userid):
|
|
|
|
user = User.get(userid)
|
|
|
|
return user
|
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
### THIS ENDPOINT CAN ONLY BE USED FOR CLI, ENDPOINT FOR FORM IS BELOW
|
2023-12-26 21:13:07 +01:00
|
|
|
@csrf.exempt
|
2023-12-14 12:39:23 +01:00
|
|
|
@app.route('/', methods=["GET", "POST"])
|
|
|
|
def index():
|
|
|
|
|
|
|
|
# Check for a GET or POST request
|
|
|
|
if request.method == "GET":
|
2024-01-05 12:17:46 +01:00
|
|
|
randomQuote = random.choice(list(quotes.items()))
|
|
|
|
author = randomQuote[0]
|
|
|
|
quote = randomQuote[1]
|
2024-01-12 10:31:47 +01:00
|
|
|
return render_template('index.html', author=author, quote=quote, title="Home")
|
2023-12-14 12:39:23 +01:00
|
|
|
|
|
|
|
elif request.method == "POST":
|
|
|
|
|
|
|
|
# Before anything else, we want to take the IP if the logging is enabled
|
2023-12-26 21:13:07 +01:00
|
|
|
if Config.ipLogEnabled:
|
2023-12-14 12:39:23 +01:00
|
|
|
ip = request.remote_addr
|
|
|
|
else:
|
|
|
|
# If not then return a 0
|
|
|
|
ip = 0
|
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
# Now check the userid and idpass against the db
|
2024-02-26 00:58:01 +01:00
|
|
|
print(Config.users.find({"userid": request.form["userid"]})["userid"])
|
|
|
|
print(Config.users.find({"userid": request.form["userid"]})["idpass"])
|
2024-02-26 00:54:34 +01:00
|
|
|
if Config.users.find({"userid": request.form["userid"]})["userid"] == request.form["userid"] and Config.users.find({"userid": request.form["userid"]})["idpass"] == request.form["idpass"]:
|
2023-12-14 12:39:23 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
# Init variables before they're passed
|
|
|
|
userid = request.form.get("userid") if request.form.get("userid") else None
|
|
|
|
filename = request.form.get("filename") if request.form.get("filename") else None
|
|
|
|
retention = int(request.form.get("retention")) if request.form.get("retention") else None
|
|
|
|
id = request.form.get("filename") if Config.files.find_one({"id": filename}) is None else None
|
2023-12-14 12:39:23 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
# We got a file or a url?
|
|
|
|
if 'file' in request.files:
|
2023-12-14 12:39:23 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
# Grab the file and store it, this is a FileStorage object
|
|
|
|
file = request.files['file']
|
2023-12-14 12:39:23 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
# Call the function to upload the file, this will return either HTTP Status codes or a 200 with a URL.
|
|
|
|
result, status = worker.uploadFile(file, ip, userid, filename, id, retention)
|
2023-12-14 12:39:23 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
return result, status
|
2023-12-21 17:19:22 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
elif 'file' in request.form:
|
2023-12-21 17:19:22 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
file = FileStorage(stream=BytesIO(request.form['file'].encode("utf-8")), filename=id, content_type="text/plain")
|
2023-12-21 17:19:22 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
result, status = worker.uploadFile(file, ip, userid, filename, id, retention)
|
2023-12-21 17:19:22 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
return result, status
|
2023-12-22 22:16:10 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
elif 'url' in request.form:
|
2023-12-22 22:16:10 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
url = request.form['url']
|
2023-12-14 12:39:23 +01:00
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
result, status = worker.shortenURL(url, ip, userid, id, retention)
|
|
|
|
|
|
|
|
return result, status
|
2023-12-29 20:47:28 +01:00
|
|
|
|
|
|
|
@app.route('/about')
|
|
|
|
def about():
|
2024-01-12 10:31:47 +01:00
|
|
|
return render_template('about.html', title="About")
|
2023-12-29 20:47:28 +01:00
|
|
|
|
|
|
|
@app.route('/tos')
|
|
|
|
def tos():
|
2024-01-12 10:31:47 +01:00
|
|
|
return render_template('tos.html', title="Terms of Service")
|
2023-12-29 20:47:28 +01:00
|
|
|
|
|
|
|
@app.route('/privacy')
|
|
|
|
def privacy():
|
2024-01-12 10:31:47 +01:00
|
|
|
return render_template('privacy.html', title="Privacy Policy")
|
2023-12-29 20:47:28 +01:00
|
|
|
|
|
|
|
@app.route('/faq')
|
|
|
|
def faq():
|
2024-01-12 10:31:47 +01:00
|
|
|
return render_template('faq.html', title="FAQ")
|
2023-12-29 20:47:28 +01:00
|
|
|
|
|
|
|
@app.route('/contact')
|
|
|
|
def contact():
|
2024-01-12 10:31:47 +01:00
|
|
|
return render_template('contact.html', title="Contact")
|
2023-12-29 20:47:28 +01:00
|
|
|
|
|
|
|
@app.route('/transparency')
|
|
|
|
def transparency():
|
2024-01-12 10:31:47 +01:00
|
|
|
return render_template('transparency.html', title="Transparency Report")
|
2024-01-03 09:41:50 +01:00
|
|
|
|
|
|
|
@app.route('/transparency/public')
|
|
|
|
def public():
|
|
|
|
return "Nothing here yet."
|
2023-12-29 20:47:28 +01:00
|
|
|
|
2024-01-04 19:58:42 +01:00
|
|
|
@app.route('/dashboard')
|
|
|
|
@login_required
|
|
|
|
def dashboard():
|
2024-01-12 10:31:47 +01:00
|
|
|
return render_template('dashboard.html', files=Config.files.find({"userid": current_user.userid}), urls=Config.url.find({"userid": current_user.userid}), title="Dashboard")
|
2024-01-04 19:58:42 +01:00
|
|
|
|
2023-12-14 12:39:23 +01:00
|
|
|
@app.route('/<id>')
|
|
|
|
def getData(id):
|
|
|
|
|
|
|
|
# Does it exist in the files DB?
|
|
|
|
if Config.files.find_one({"id": id}) is not None:
|
|
|
|
data = Config.files.find_one({"id": id})
|
|
|
|
|
2023-12-22 17:09:46 +01:00
|
|
|
with open(os.path.join(Config.fileDir, secure_filename(id)), "rb") as f:
|
2023-12-14 12:39:23 +01:00
|
|
|
file = f.read()
|
|
|
|
|
2024-01-28 11:24:13 +01:00
|
|
|
# Get the mimetype from the db
|
2023-12-14 12:39:23 +01:00
|
|
|
try:
|
|
|
|
mimetype = data["mimetype"]
|
2024-01-28 11:24:13 +01:00
|
|
|
except:
|
|
|
|
mimetype = "text/plain" # This is the default because it seems loads of files are being given the wrong mime
|
2023-12-14 12:39:23 +01:00
|
|
|
|
|
|
|
# Return the file with the correct MIME type
|
|
|
|
return send_file(io.BytesIO(file), mimetype=mimetype)
|
|
|
|
|
|
|
|
# If not then check the URL Shortening DB
|
|
|
|
elif Config.url.find_one({"id": id}) is not None:
|
|
|
|
data = Config.url.find_one({"id": id})
|
|
|
|
|
|
|
|
return redirect(data["url"])
|
|
|
|
|
|
|
|
else:
|
|
|
|
return random.choice(Errors.file404)
|
|
|
|
|
|
|
|
@app.route('/<id>/info')
|
|
|
|
def getInfo(id):
|
|
|
|
|
|
|
|
return worker.idInfo(id)
|
|
|
|
|
2024-01-12 10:35:00 +01:00
|
|
|
@csrf.exempt
|
2024-01-19 14:20:10 +01:00
|
|
|
@app.route('/<id>/delete')
|
2024-01-04 19:58:42 +01:00
|
|
|
def delete(id):
|
|
|
|
if Config.files.find_one({"id": id}) is not None:
|
2024-01-08 12:36:53 +01:00
|
|
|
|
|
|
|
data = Config.files.find_one({"id": id})
|
2024-01-04 19:58:42 +01:00
|
|
|
|
2024-01-12 10:40:13 +01:00
|
|
|
if data["userid"] == request.form.get("userid") and bcrypt.check_password_hash(Config.users.find_one({"userid": data["userid"]})["idpass"], request.form.get("idpass")):
|
2024-01-04 19:58:42 +01:00
|
|
|
Config.files.delete_one({"id": id})
|
|
|
|
os.remove(os.path.join(Config.fileDir, secure_filename(id)))
|
|
|
|
return "File deleted."
|
|
|
|
|
2024-01-12 10:38:43 +01:00
|
|
|
elif data["userid"] == current_user.userid:
|
2024-01-04 19:58:42 +01:00
|
|
|
Config.files.delete_one({"id": id})
|
|
|
|
os.remove(os.path.join(Config.fileDir, secure_filename(id)))
|
|
|
|
return "File deleted."
|
|
|
|
|
|
|
|
else:
|
|
|
|
return "You are not the owner of this file."
|
|
|
|
|
2024-01-08 12:36:53 +01:00
|
|
|
elif Config.url.find_one({"id": id}) is not None:
|
|
|
|
|
|
|
|
data = Config.url.find_one({"id": id})
|
|
|
|
|
|
|
|
if data["userid"] == current_user.userid:
|
|
|
|
Config.files.delete_one({"id": id})
|
|
|
|
return "URL deleted."
|
|
|
|
|
2024-02-26 00:43:42 +01:00
|
|
|
elif data["userid"] == request.form.get("userid") and bcrypt.check_password_hash(Config.users.find_one({"userid": data["userid"]})["idpass"], request.form.get("idpass")):
|
2024-01-08 12:36:53 +01:00
|
|
|
Config.files.delete_one({"id": id})
|
|
|
|
return "URL deleted."
|
|
|
|
|
|
|
|
else:
|
|
|
|
return "You are not the owner of this link."
|
|
|
|
|
|
|
|
else:
|
|
|
|
return "This ID does not exist."
|
|
|
|
|
2023-12-14 12:39:23 +01:00
|
|
|
@app.route('/teapot')
|
|
|
|
def teapot():
|
2023-12-26 20:16:58 +01:00
|
|
|
return 'I\'m a teapot. 418.', 418
|
|
|
|
|
|
|
|
@app.route('/register', methods=["GET", "POST"])
|
|
|
|
def register():
|
|
|
|
if current_user.is_authenticated:
|
|
|
|
return redirect("/")
|
|
|
|
else:
|
|
|
|
if request.method == "GET":
|
2024-01-12 10:35:00 +01:00
|
|
|
return render_template("register.html", form=RegistrationForm(), title="Register")
|
2023-12-26 20:16:58 +01:00
|
|
|
elif request.method == "POST":
|
|
|
|
username = request.form.get("username")
|
|
|
|
password = request.form.get("password")
|
|
|
|
|
|
|
|
res = worker.registerUser(username, password)
|
|
|
|
|
|
|
|
if res == True:
|
|
|
|
flash("Successfully registered!", "success")
|
|
|
|
return redirect("/login")
|
|
|
|
else:
|
|
|
|
flash("Something went wrong, sorry.", "danger")
|
|
|
|
return redirect("/register")
|
|
|
|
|
|
|
|
@app.route('/login', methods=["GET", "POST"])
|
|
|
|
def login():
|
|
|
|
if current_user.is_authenticated:
|
|
|
|
return redirect("/")
|
|
|
|
else:
|
|
|
|
if request.method == "GET":
|
2024-01-12 10:35:00 +01:00
|
|
|
return render_template("login.html", form=LoginForm(), title="Login")
|
2023-12-26 20:16:58 +01:00
|
|
|
elif request.method == "POST":
|
|
|
|
username = request.form.get("username")
|
|
|
|
password = request.form.get("password")
|
|
|
|
|
|
|
|
userid = Config.users.find_one({"user": username})["userid"]
|
|
|
|
user = User.get(userid)
|
|
|
|
|
|
|
|
if user and bcrypt.check_password_hash(user.password, password):
|
|
|
|
login_user(user)
|
|
|
|
flash("Successfully logged in!", "success")
|
|
|
|
return redirect("/")
|
|
|
|
else:
|
|
|
|
flash("Incorrect username or password.", "danger")
|
|
|
|
return redirect("/login")
|
|
|
|
|
|
|
|
@app.route('/logout')
|
|
|
|
def logout():
|
|
|
|
logout_user()
|
2024-01-04 19:58:42 +01:00
|
|
|
return redirect("/")
|
|
|
|
|
|
|
|
@app.route('/resetidpass')
|
|
|
|
def resetidpass():
|
|
|
|
idpass = worker.resetIDPass(current_user.userid)
|
|
|
|
if idpass == False:
|
|
|
|
return "Something went wrong, sorry. Please try again."
|
|
|
|
else:
|
|
|
|
return f"Your new IDPass is \n {idpass}\n This will only be shown once, please save it somewhere safe."
|
|
|
|
|
|
|
|
|
|
|
|
@app.errorhandler(404)
|
|
|
|
def page_not_found(e):
|
2024-02-26 00:43:42 +01:00
|
|
|
return random.choice(Errors.file404), 404
|