import os from datetime import datetime from flask import current_app as app import time import sqlite3 import shutil from .misc import file_size_human, file_date_human, file_time_human def get_db(): db = sqlite3.connect(app.config["DB"], timeout=5) c = db.cursor() return db, c def db_store_file(token, name, expires, max_dl, password): db, c = get_db() c.execute( """ insert into files(token,name,added,expires,downloads,max_downloads,passhash) values (?,?,?,?,?,?,?) """, (token, name, int(time.time()), expires, 0, max_dl, password), ) if c.rowcount > 0: db.commit() return def db_get_file(token, name): db, c = get_db() return db.execute( """ SELECT added,expires,downloads,max_downloads,passhash FROM files WHERE token = ? AND name = ? """, (token, name), ).fetchone() def db_get_files(): db, c = get_db() return db.execute( """ SELECT token,name,added,expires,downloads,max_downloads,passhash FROM files WHERE expires > ? and (downloads < max_downloads or max_downloads = -1) ORDER BY added """, (time.time(),), ) def db_delete_file(token, name): db, c = get_db() c.execute( """ DELETE FROM files WHERE token = ? and name = ? """, (token, name), ) if c.rowcount > 0: db.commit() def db_add_download(token, name): db, c = get_db() c.execute( """ UPDATE files SET downloads = downloads + 1 WHERE token = ? AND name = ? """, (token, name), ) if c.rowcount > 0: db.commit() return def db_maintenance(): messages = [] # === Delete DB entries where expiry or max DL is used up === db, c = get_db() rows = db.execute( """ select token, name from files where expires < ? or (downloads >= max_downloads and max_downloads != -1) """, (int(time.time()),), ) deleted_tokens = [] for row in rows: deleted_tokens.append((row[0],)) messages.append(f"Deleting DB {row[0]}/{row[1]}") if len(deleted_tokens) > 0: db, c = get_db() c.executemany("DELETE FROM files WHERE token = ?", deleted_tokens) if c.rowcount > 0: db.commit() # Delete files, if DB entry is gone subdirs = next(os.walk(app.config["DATAFOLDER"]))[1] db, c = get_db() for d in subdirs: keep = db.execute( "SELECT 1 FROM files WHERE token = ?", (d,), ).fetchone() if not keep: try: for fname in os.listdir(os.path.join(app.config["DATAFOLDER"], d)): os.remove(os.path.join(app.config["DATAFOLDER"], d, fname)) messages.append(f"Deleting file {d}/{fname}") except Exception: pass shutil.rmtree(os.path.join(app.config["DATAFOLDER"], d), ignore_errors=True) messages.append(f"Deleting folder {d}") # Delete DB entries, if files have been deleted (probably manually) db, c = get_db() rows = db.execute( """ select token, name from files """ ) deleted_tokens = [] for row in rows: full_path = file_full_path(row[0], row[1]) if not os.path.exists(full_path): deleted_tokens.append((row[0],)) messages.append(f"Deleting DB {row[0]}/{row[1]} - files missing") if len(deleted_tokens) > 0: db, c = get_db() c.executemany("DELETE FROM files WHERE token = ?", deleted_tokens) if c.rowcount > 0: db.commit() messages.append("Maintenance done.") return "\n".join(messages) def file_age(path): now = datetime.now() then = datetime.fromtimestamp(os.stat(path).st_mtime) diff = now - then return ( diff, "%03d d %s" % (diff.days, datetime.utcfromtimestamp(diff.seconds).strftime("%H:%M:%S")), ) def file_details(token, name): full_path = file_full_path(token, name) try: s = os.stat(full_path) db_stat = db_get_file(token, name) if db_stat: added, expires, downloads, max_dl, password = db_stat else: return {} return { "size": s.st_size, "hsize": file_size_human(s.st_size, HTML=False), "added": file_time_human(added), "name": name, "url": file_full_url(token, name), "expires": file_time_human(expires), "downloaded": downloads, "max-dl": max_dl, "protected": password is not None, } except FileNotFoundError: return {} def file_full_path(token, name): return os.path.join(app.config["DATAFOLDER"], token, name) def file_full_url(token, name): return f"{app.config['PUBLIC_URL']}/d/{token}/{name}" def file_list(): details = [] details.append(" Added/Expiry DL/MaxDL URL") details.append("=" * 75) for file in db_get_files(): url = file_full_url(file[0], file[1]) added = file_date_human(file[2]) expiry = file_date_human(file[3]) pw = " (PW)" if file[6] else "" details.append(f"{added}/{expiry} {file[4]:4d}/{file[5]:4d} {url}{pw}") return details def file_list_simple(): return [f"{r[0]}/{r[1]}" for r in db_get_files()]