Files
mini-flees/code/utils/files.py
2023-08-19 12:28:58 +03:00

231 lines
6.3 KiB
Python

import os
from datetime import datetime
from flask import current_app as app
import re
import sys
import json
import stat
import time
import sqlite3
import shutil
from .misc import *
from .crypt import *
def get_db():
db = sqlite3.connect(os.getenv("FLASK_DB"), timeout=5)
c = db.cursor()
return db, c
def db_store_file(token, name, expires, max_dl):
db, c = get_db()
c.execute(
"""
insert into files(token,name,added,expires,downloads,max_downloads)
values (?,?,?,?,?,?)
""",
(token, name, int(time.time()), expires, 0, max_dl),
)
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
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
FROM files
WHERE expires > ? and downloads < max_downloads
""",
(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 = []
db, c = get_db()
rows = db.execute(
"""
select
token, name
from files
where expires < ? or downloads >= max_downloads
""",
(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()
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}")
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 = os.path.join(os.getenv("FLASK_DATAFOLDER"), token, name)
try:
s = os.stat(full_path)
db_stat = db_get_file(token, name)
if db_stat:
added, expires, downloads, max_dl = db_stat
else:
return {}
return {
"size": file_size_MB(s.st_size),
"hsize": file_size_human(s.st_size, HTML=False),
"added": file_date_human(added),
"name": name,
"url": f"{app.config['PUBLIC_URL']}/dl/{token}/{name}",
"expires": file_date_human(expires),
"downloaded": downloads,
"max-dl": max_dl,
}
except FileNotFoundError:
return {}
def file_list():
files = list(db_get_files())
details = []
details.append(" Added/Expiry DL/MaxDL URL")
details.append("=" * 75)
for file in files:
url = f"{app.config['PUBLIC_URL']}/dl/{file[0]}/{file[1]}"
details.append(
f"{file_date_human(file[2])}/{file_date_human(file[3])} {file[4]:4d}/{file[5]:4d} {url}"
)
return details
def get_expiring_file(ehash):
connection = apsw.Connection(app.config["SQLITE_FILE"])
cursor = connection.cursor()
for row in cursor.execute(
"SELECT file, expires FROM expiring WHERE hash = ?", (ehash,)
):
return row[0], row[1]
return None, None
def get_script_url(public_url, share, end_point, token="[TOKEN]"):
cmd = None
doc = None
if get_or_none("direct_links", share) and end_point == "download":
end_point = "direct"
url = "%s/script/%s/%s/%s" % (public_url, end_point, share["name"], token)
if end_point in ("download", "direct"):
cmd = "curl -s %s | bash /dev/stdin [-f]" % (url,)
doc = "Download all files in the share. -f to force overwrite existing files."
if end_point == "client":
cmd = "python <( curl -s %s )" % (url,)
doc = "Console client to download and upload files."
if end_point == "upload_split":
cmd = (
"curl -s %s | python - [-s split_size_in_Mb] file_to_upload.ext [second.file.ext]"
% (url,)
)
doc = "Upload files to the share. -s to set splitting size."
if end_point == "flip":
cmd = "curl -s %s > flip && ./flip" % (url,)
doc = "Use the share as a command line clipboard"
return {"cmd": cmd, "doc": doc}
def set_expiring_file(share, filename, expires):
connection = apsw.Connection(app.config["SQLITE_FILE"])
cursor = connection.cursor()
while True:
ehash = random_expiring_hash()
matches = len(
list(cursor.execute("SELECT file FROM expiring WHERE hash = ?", (ehash,)))
)
if matches == 0:
break
cursor.execute(
"INSERT INTO expiring (hash, file, expires) VALUES (?,?,?)",
(ehash, filename, expires),
)
return "/".join((app.config["PUBLIC_URL"], "e", ehash, os.path.basename(filename)))
def remove_expiring_file(share, filename):
connection = apsw.Connection(app.config["SQLITE_FILE"])
cursor = connection.cursor()
cursor.execute("DELETE FROM expiring WHERE file = ?", (filename,))