from flask import Flask, request, jsonify, send_from_directory, render_template, url_for, redirect, send_file from werkzeug.utils import secure_filename import shortuuid import os from datetime import datetime import zipfile import sqlite3 import threading import time import shutil from datetime import timedelta from pygments import highlight from pygments.lexers import get_lexer_by_name, guess_lexer from pygments.formatters import HtmlFormatter from pygments.util import ClassNotFound import json from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user import hashlib app = Flask(__name__) app.secret_key = 'your_secret_key_here' # Add this line UPLOAD_FOLDER = './uploads' app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER DATABASE = 'data.db' if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) # Database setup and helper functions def get_db(): db = getattr(threading.current_thread(), '_database', None) if db is None: db = threading.current_thread()._database = sqlite3.connect(DATABASE) return db def init_db(): with app.app_context(): db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() @app.teardown_appcontext def close_connection(exception): db = getattr(threading.current_thread(), '_database', None) if db is not None: db.close() # Initialize database init_db() # Add this function to delete old files def delete_old_files(): while True: db = get_db() cursor = db.cursor() # Delete files older than 30 days thirty_days_ago = datetime.now() - timedelta(days=30) cursor.execute("SELECT vanity, type, data FROM content WHERE created_at < ?", (thirty_days_ago,)) old_files = cursor.fetchall() for vanity, content_type, data in old_files: if content_type == 'file': file_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{data}') if os.path.exists(file_path): os.remove(file_path) elif content_type == 'folder': folder_path = os.path.join(app.config['UPLOAD_FOLDER'], vanity) if os.path.exists(folder_path): shutil.rmtree(folder_path) cursor.execute("DELETE FROM content WHERE created_at < ?", (thirty_days_ago,)) db.commit() time.sleep(86400) # Sleep for 24 hours # Start the cleanup thread cleanup_thread = threading.Thread(target=delete_old_files) cleanup_thread.daemon = True cleanup_thread.start() login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'login' class User(UserMixin): def __init__(self, id, username, password_hash): self.id = id self.username = username self.password_hash = password_hash @staticmethod def hash_password(password): salt = os.urandom(32) key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000) return salt + key @staticmethod def verify_password(stored_password, provided_password): salt = stored_password[:32] stored_key = stored_password[32:] new_key = hashlib.pbkdf2_hmac('sha256', provided_password.encode('utf-8'), salt, 100000) return stored_key == new_key @login_manager.user_loader def load_user(user_id): db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,)) user = cursor.fetchone() if user: return User(user[0], user[1], user[2]) return None @app.route('/') def index(): return render_template('index.html') @app.route('/content/') def content(vanity): db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM content WHERE vanity = ?", (vanity,)) target = cursor.fetchone() if target: content_type, content_data = target[1], target[2] if content_type == 'pastebin': try: lexer = guess_lexer(content_data) language = lexer.aliases[0] except ClassNotFound: language = 'text' lexer = get_lexer_by_name(language) formatter = HtmlFormatter(style='monokai', linenos=True, cssclass="source") highlighted_code = highlight(content_data, lexer, formatter) css = formatter.get_style_defs('.source') return render_template('content.html', highlighted_content=highlighted_code, css=css, raw_content=content_data, created_at=target[3], vanity=vanity, language=language) elif content_type == 'file': file_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{content_data}') file_info = { 'name': content_data, 'size': os.path.getsize(file_path), 'modified_at': datetime.fromtimestamp(os.path.getmtime(file_path)).strftime('%Y-%m-%d %H:%M:%S'), 'url': url_for('download_file', vanity=vanity) } return render_template('file.html', **file_info) elif content_type == 'url': return render_template('content.html', url=content_data) return 'Not Found', 404 @app.route('/download/', methods=['GET']) def download_file(vanity): db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM content WHERE vanity = ? AND type = 'file'", (vanity,)) target = cursor.fetchone() if target: filename = f'{vanity}_{target[2]}' return send_from_directory(app.config['UPLOAD_FOLDER'], filename, as_attachment=True) return 'Not Found', 404 @app.route('/upload/pastebin', methods=['POST']) def upload_pastebin(): content = request.form['content'] vanity = shortuuid.uuid()[:6] created_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S') db = get_db() cursor = db.cursor() cursor.execute("INSERT INTO content (vanity, type, data, created_at) VALUES (?, ?, ?, ?)", (vanity, 'pastebin', content, created_at)) db.commit() return jsonify({'vanity': vanity}) @app.route('/upload/file', methods=['POST']) def upload_file(): if 'file' not in request.files: return 'No file part', 400 file = request.files['file'] if file.filename == '': return 'No selected file', 400 if file: vanity = shortuuid.uuid()[:6] filename = secure_filename(file.filename) filepath = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{filename}') file.save(filepath) db = get_db() cursor = db.cursor() cursor.execute("INSERT INTO content (vanity, type, data) VALUES (?, ?, ?)", (vanity, 'file', filename)) db.commit() return jsonify({'vanity': vanity}) def save_file(file, folder_path): filename = secure_filename(file.filename) file_path = os.path.join(folder_path, filename) file.save(file_path) def handle_uploaded_folder(files, base_path): for file in files: if file.filename.endswith('/'): subfolder_path = os.path.join(base_path, secure_filename(file.filename)) os.makedirs(subfolder_path, exist_ok=True) handle_uploaded_folder(request.files.getlist(file.filename), subfolder_path) else: save_file(file, base_path) @app.route('/upload/folder', methods=['POST']) def upload_folder(): if 'file' not in request.files: return 'No files uploaded', 400 files = request.files.getlist('file') if not files: return 'No files selected', 400 vanity = shortuuid.uuid()[:6] folder_path = os.path.join(app.config['UPLOAD_FOLDER'], vanity) os.makedirs(folder_path) handle_uploaded_folder(files, folder_path) db = get_db() cursor = db.cursor() cursor.execute("INSERT INTO content (vanity, type, data) VALUES (?, ?, ?)", (vanity, 'folder', ','.join([file.filename for file in files]))) db.commit() return jsonify({'vanity': vanity}) @app.route('/shorten', methods=['POST']) def shorten_url(): original_url = request.form['url'] vanity = shortuuid.uuid()[:6] db = get_db() cursor = db.cursor() cursor.execute("INSERT INTO content (vanity, type, data) VALUES (?, ?, ?)", (vanity, 'url', original_url)) db.commit() return jsonify({'vanity': vanity}) @app.route('/', methods=['GET']) def redirect_vanity(vanity): db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM content WHERE vanity = ?", (vanity,)) target = cursor.fetchone() if target: content_type, content_data = target[1], target[2] if content_type == 'pastebin': try: lexer = guess_lexer(content_data) language = lexer.aliases[0] except ClassNotFound: language = 'text' lexer = get_lexer_by_name(language) formatter = HtmlFormatter(style='monokai', linenos=True, cssclass="source") highlighted_code = highlight(content_data, lexer, formatter) css = formatter.get_style_defs('.source') return render_template('content.html', highlighted_content=highlighted_code, css=css, raw_content=content_data, created_at=target[3], vanity=vanity, language=language) elif content_type == 'file': file_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{content_data}') file_info = { 'name': content_data, 'size': os.path.getsize(file_path), 'modified_at': datetime.fromtimestamp(os.path.getmtime(file_path)).strftime('%Y-%m-%d %H:%M:%S'), 'url': url_for('download_file', vanity=vanity) } return render_template('file.html', **file_info) elif content_type == 'folder': return redirect(url_for('folder_content', vanity=vanity)) elif content_type == 'url': return render_template('content.html', content=content_data, url=content_data) return render_template('404.html'), 404 @app.route('//raw', methods=['GET']) def raw_vanity(vanity): db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM content WHERE vanity = ? AND type = 'pastebin'", (vanity,)) target = cursor.fetchone() if target: return target[2], 200, {'Content-Type': 'text/plain; charset=utf-8'} return 'Not Found', 404 @app.route('/folder/', methods=['GET']) def folder_content(vanity): db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM content WHERE vanity = ? AND type = 'folder'", (vanity,)) target = cursor.fetchone() if target: folder_path = os.path.join(app.config['UPLOAD_FOLDER'], vanity) files = [] for root, _, filenames in os.walk(folder_path): for filename in filenames: file_path = os.path.join(root, filename) relative_path = os.path.relpath(file_path, folder_path) file_url = url_for('download_folder_file', vanity=vanity, file_name=relative_path) files.append({'name': relative_path, 'url': file_url}) # Pagination per_page = 10 page = int(request.args.get('page', 1)) start = (page - 1) * per_page end = start + per_page total_files = len(files) files = files[start:end] prev_url = url_for('folder_content', vanity=vanity, page=page-1) if page > 1 else None next_url = url_for('folder_content', vanity=vanity, page=page+1) if end < total_files else None download_all_url = url_for('download_folder_as_zip', vanity=vanity) # Get the current folder name current_folder = os.path.basename(folder_path) return render_template('folder.html', files=files, prev_url=prev_url, next_url=next_url, download_all_url=download_all_url, current_folder=current_folder) return 'Not Found', 404 @app.route('/folder//download', methods=['GET']) def download_folder_as_zip(vanity): db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM content WHERE vanity = ? AND type = 'folder'", (vanity,)) target = cursor.fetchone() if target: folder_path = os.path.join(app.config['UPLOAD_FOLDER'], vanity) zip_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}.zip') with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: for root, _, files in os.walk(folder_path): for file in files: file_path = os.path.join(root, file) zipf.write(file_path, os.path.relpath(file_path, folder_path)) return send_from_directory(app.config['UPLOAD_FOLDER'], f'{vanity}.zip', as_attachment=True) return 'Not Found', 404 @app.route('/folder//', methods=['GET']) def download_folder_file(vanity, file_name): folder_path = os.path.join(app.config['UPLOAD_FOLDER'], vanity) file_path = os.path.join(folder_path, file_name) if os.path.isfile(file_path): return send_from_directory(folder_path, file_name, as_attachment=True) return 'Not Found', 404 @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM users WHERE username = ?", (username,)) if cursor.fetchone(): return "Username already exists" hashed_password = User.hash_password(password) cursor.execute("INSERT INTO users (username, password_hash) VALUES (?, ?)", (username, hashed_password)) db.commit() return redirect(url_for('login')) return render_template('register.html') @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM users WHERE username = ?", (username,)) user = cursor.fetchone() if user and User.verify_password(user[2], password): login_user(User(user[0], user[1], user[2])) return redirect(url_for('user_files', username=username)) return "Invalid username or password" return render_template('login.html') @app.route('/logout') @login_required def logout(): logout_user() return redirect(url_for('index')) @app.route('/user/') def user_files(username): if current_user.is_authenticated and current_user.username == username: user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) if not os.path.exists(user_folder): os.makedirs(user_folder) files = os.listdir(user_folder) return render_template('user_files.html', username=username, files=files) return "Unauthorized", 401 @app.route('/user//upload', methods=['POST']) @login_required def upload_user_file(username): if current_user.username != username: return "Unauthorized", 401 if 'file' not in request.files: return 'No file part', 400 file = request.files['file'] if file.filename == '': return 'No selected file', 400 if file: filename = secure_filename(file.filename) file_path = os.path.join(app.config['UPLOAD_FOLDER'], username, filename) file.save(file_path) return redirect(url_for('user_files', username=username)) @app.route('/user//delete/', methods=['POST']) @login_required def delete_user_file(username, filename): if current_user.username != username: return "Unauthorized", 401 file_path = os.path.join(app.config['UPLOAD_FOLDER'], username, filename) if os.path.exists(file_path): os.remove(file_path) return redirect(url_for('user_files', username=username)) @app.route('/user//rename', methods=['POST']) @login_required def rename_user_file(username): if current_user.username != username: return "Unauthorized", 401 old_filename = request.form['old_filename'] new_filename = secure_filename(request.form['new_filename']) old_path = os.path.join(app.config['UPLOAD_FOLDER'], username, old_filename) new_path = os.path.join(app.config['UPLOAD_FOLDER'], username, new_filename) if os.path.exists(old_path): os.rename(old_path, new_path) return redirect(url_for('user_files', username=username)) @app.route('/') @app.route('//') @app.route('//') def serve_user_page(username, filename=None): print(f"Accessing user page: {username}, filename: {filename}") # Debug print # Check if the username exists in the database db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM users WHERE username = ?", (username,)) user = cursor.fetchone() if not user: print(f"User {username} not found") # Debug print return "User not found", 404 user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) print(f"User folder path: {user_folder}") # Debug print if not os.path.exists(user_folder): print(f"User folder does not exist for {username}") # Debug print os.makedirs(user_folder) # Create the folder if it doesn't exist if filename is None or filename == '': # Try to serve index.html index_path = os.path.join(user_folder, 'index.html') print(f"Checking for index.html at: {index_path}") # Debug print if os.path.exists(index_path): print(f"Serving index.html for {username}") # Debug print return send_file(index_path) else: print(f"No index.html found, listing files for {username}") # Debug print # If no index.html, list all files files = os.listdir(user_folder) print(f"Files in {username}'s folder: {files}") # Debug print return render_template('user_files_public.html', username=username, files=files) else: # Serve the requested file file_path = os.path.join(user_folder, filename) print(f"Attempting to serve file: {file_path}") # Debug print if os.path.exists(file_path) and os.path.isfile(file_path): print(f"Serving file: {file_path}") # Debug print return send_file(file_path) else: print(f"File not found: {file_path}") # Debug print return "File not found", 404 @app.route('/debug/users') def debug_users(): db = get_db() cursor = db.cursor() cursor.execute("SELECT username FROM users") users = cursor.fetchall() user_files = {} for user in users: username = user[0] user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) if os.path.exists(user_folder): user_files[username] = os.listdir(user_folder) else: user_files[username] = [] return jsonify(user_files) @app.route('/user//edit/', methods=['GET', 'POST']) @login_required def edit_file(username, filename): if current_user.username != username: return "Unauthorized", 401 file_path = os.path.join(app.config['UPLOAD_FOLDER'], username, filename) if request.method == 'POST': content = request.form['content'] with open(file_path, 'w') as file: file.write(content) return redirect(url_for('user_files', username=username)) if os.path.exists(file_path) and os.path.isfile(file_path): with open(file_path, 'r') as file: content = file.read() return render_template('edit_file.html', username=username, filename=filename, content=content) else: return "File not found", 404 if __name__ == '__main__': app.run(debug=True, port=7123)