|
|
|
|
@@ -1,19 +1,14 @@
|
|
|
|
|
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 *
|
|
|
|
|
from .misc import file_size_MB, file_size_human, file_date_human
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_db():
|
|
|
|
|
db = sqlite3.connect(os.getenv("FLASK_DB"), timeout=5)
|
|
|
|
|
db = sqlite3.connect(app.config["DB"], timeout=5)
|
|
|
|
|
c = db.cursor()
|
|
|
|
|
return db, c
|
|
|
|
|
|
|
|
|
|
@@ -49,8 +44,8 @@ def db_get_files():
|
|
|
|
|
"""
|
|
|
|
|
SELECT token,name,added,expires,downloads,max_downloads
|
|
|
|
|
FROM files
|
|
|
|
|
WHERE expires > ? and downloads < max_downloads
|
|
|
|
|
""",
|
|
|
|
|
WHERE expires > ? and (downloads < max_downloads or max_downloads = -1)
|
|
|
|
|
""",
|
|
|
|
|
(time.time(),),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@@ -82,14 +77,15 @@ def db_add_download(token, name):
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
""",
|
|
|
|
|
where expires < ? or (downloads >= max_downloads and max_downloads != -1)
|
|
|
|
|
""",
|
|
|
|
|
(int(time.time()),),
|
|
|
|
|
)
|
|
|
|
|
deleted_tokens = []
|
|
|
|
|
@@ -101,7 +97,7 @@ def db_maintenance():
|
|
|
|
|
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:
|
|
|
|
|
@@ -111,13 +107,35 @@ def db_maintenance():
|
|
|
|
|
).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))
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -133,7 +151,7 @@ def file_age(path):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def file_details(token, name):
|
|
|
|
|
full_path = os.path.join(os.getenv("FLASK_DATAFOLDER"), token, name)
|
|
|
|
|
full_path = file_full_path(token, name)
|
|
|
|
|
try:
|
|
|
|
|
s = os.stat(full_path)
|
|
|
|
|
db_stat = db_get_file(token, name)
|
|
|
|
|
@@ -146,7 +164,7 @@ def file_details(token, name):
|
|
|
|
|
"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}",
|
|
|
|
|
"url": file_full_url(token, name),
|
|
|
|
|
"expires": file_date_human(expires),
|
|
|
|
|
"downloaded": downloads,
|
|
|
|
|
"max-dl": max_dl,
|
|
|
|
|
@@ -155,76 +173,23 @@ def file_details(token, name):
|
|
|
|
|
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']}/dl/{token}/{name}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def file_list():
|
|
|
|
|
files = list(db_get_files())
|
|
|
|
|
details = []
|
|
|
|
|
details.append(" Added/Expiry DL/MaxDL URL")
|
|
|
|
|
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}"
|
|
|
|
|
)
|
|
|
|
|
url = file_full_url(file[0], file[1])
|
|
|
|
|
added = file_date_human(file[2])
|
|
|
|
|
expiry = file_date_human(file[3])
|
|
|
|
|
details.append(f"{added}/{expiry} {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,))
|
|
|
|
|
|