Files
flees/code/app.py

963 lines
30 KiB
Python

# -*- coding: utf-8 -*-
import os, sys, time
import json
from datetime import datetime
from flask import Flask, render_template, jsonify, current_app, Response, \
redirect, url_for, request, g, session, send_file, send_from_directory, \
abort
from werkzeug.utils import secure_filename
import zipfile
from multiprocessing import Process
from revprox import ReverseProxied
from utils.utils import *
from utils.crypt import *
__FLEES_VERSION__ = "20181124.0"
app = Flask(__name__)
app.config.from_object(__name__)
config_values = read_config(app)
if 'notifier' in config_values:
if len(config_values['notifier']) > 0:
notifier_config = config_values['notifier'].split(":")
imported = getattr(__import__(
notifier_config[0],
fromlist=[notifier_config[1]]
),
notifier_config[1]
)
app.config['notifier'] = imported()
app.secret_key = config_values['app_secret_key']
app.wsgi_app = ReverseProxied(app.wsgi_app)
@app.before_request
def before_request():
g.shares = json.load(open(app.config['SHARES_FILE'],'rt'))
g.version = __FLEES_VERSION__
g.site_name = app.config['SITE_NAME']
g.max_zip_size = app.config['MAX_ZIP_SIZE']
g.logger = app.config['LOGGER']
@app.route("/")
def index():
public_shares = []
for share in g.shares:
public = get_or_none('public', share)
expired = is_expired(share)
authenticated_share = get_share(share['name'])
password_set = False
if authenticated_share[0]:
password_set = authenticated_share[1]['authenticated'] == 'hash'
if not expired:
if public or password_set:
public_shares.append({
'name': share['name'],
'expire': get_or_none('expire', share),
'upload': get_or_none('upload', share),
'password_set': password_set,
'description': get_or_none('description', share, '')
})
return render_template("index.html", entries=public_shares)
@app.route('/authenticate/<name>', methods=['GET','POST'])
def authenticate(name):
if request.method == 'GET':
return render_template('authenticate.html', name = name)
if request.method == 'POST':
user_password = request.form['password'].encode('utf-8')
session[name] = password_hash(user_password, app.secret_key)
if name + 'Token' in session:
del session[name + 'Token']
return redirect(url_for('list_view', name = name))
@app.route('/upload/<name>/<token>', methods=['POST'])
@app.route('/upload', methods=['POST'])
def upload(name = None, token = None):
if request.method == 'POST':
file = request.files['file']
if name == None:
name = request.form['name']
(ok,share) = get_share(name, token = token)
if not ok:
notify({
"share": name,
"operation": "unauthorized_upload"
})
return share
if not get_or_none('upload', share) == True:
return "Upload not allowed\n",400
if file:
secure_filename = secure_filename_hidden(
file.filename
)
filename = os.path.join(
share['path'],
secure_filename
)
if get_or_none('overwrite', share) == False:
if os.path.exists(filename):
file_versionize(filename)
#~ return "Overwrite forbidden", 403
print_debug("Saving " + filename)
file.save(filename)
set_rights(filename)
notify({
"recipient": get_or_none('recipient', share),
"share": name,
"filename": filename,
"operation": "upload"
})
if 'from_gui' in request.form:
if request.form['from_gui'] == "true":
return redirect(url_for('list_view',name=name))
download_url = get_download_url(share, secure_filename, token)
return "File uploaded\n%s\n"%( download_url, ), 200
else:
return "Use the 'file' variable to upload\n",400
@app.route('/upload_join/<name>/<token>', methods=['POST'])
def upload_join_splitted(name, token):
if request.method == 'POST':
(ok,share) = get_share(name, token = token)
if not ok:
notify({
"share": name,
"operation": "unauthorized_split_upload"
})
return share
if not get_or_none('upload', share) == True:
return "Upload not allowed",400
if not 'filename' in request.form:
return "No filename given", 400
if not 'parts' in request.form:
return "No parts count given", 400
try:
no_parts = int(request.form['parts'])
except:
return "Parts not parseable", 400
parts = []
for part in range(no_parts):
filename = os.path.join(
share['path'],
secure_filename_hidden(
".%s.part.%03d"%(
request.form['filename'],
part
)
)
)
print_debug("Checking for join: " + filename)
if os.path.exists(filename):
parts.append(filename)
part_existed = part
if len(parts) == 0:
return "Invalid partial filename %s -> %s\n"%( request.form['filename'], filename), 400
if not len(parts) == part_existed + 1:
return "Parts missing\n", 400
target_name = os.path.join(
share['path'],
request.form['filename']
)
if get_or_none('overwrite', share) == False:
if os.path.exists(target_name):
file_versionize(target_name)
try:
begin = uploadJoiner(target_name, parts)
except:
return "Joining failed\n", 400
download_url = get_download_url(share, request.form['filename'], token)
return "Joining started\n%s\n"%( download_url, ), 200
@app.route('/upload/url', methods=['POST'])
def upload_url():
if request.method == 'POST':
name = request.form['name']
url = request.form['url']
if url == "https://...":
return "", 200
if not is_valid_url(url):
return "URL not valid", 400
(ok,share) = get_share(name)
if not ok:
notify({
"share": name,
"operation": "unauthorized_URL_upload"
})
return share
if not get_or_none('upload', share) == True:
return "Upload not allowed\n",400
filename = os.path.join(
share['path'],
secure_filename(
safe_string(url, ".[]()- ", no_repeat = True)
)
)
if os.path.exists(filename):
file_versionize(filename)
ok,error = download_url(url, filename)
if not ok:
return error
set_rights(filename)
notify({
"recipient": get_or_none('recipient', share),
"share": name,
"filename": filename,
"operation": "upload"
})
if 'from_gui' in request.form:
if request.form['from_gui'] == "true":
return redirect(url_for('list_view',name=name))
return "File uploaded\n", 200
@app.route('/editor', methods = ['GET','POST'])
@app.route('/editor/<name>', methods = ['GET','POST'])
def editor(name = None):
filename = 'paste.txt'
if request.method == 'POST':
name = request.form['editor_name']
filename = request.form['editor_filename']
(ok,share) = get_share(name)
if not ok:
notify({
"share": name,
"operation": "unauthorized_editor"
})
return share
pathname = os.path.join(
share['path'],
filename
)
content = ""
if os.path.isfile(pathname):
if pathname.endswith(".txt"):
content = open(pathname, 'rt').read(65536)
return render_template(
'editor.html',
name = name,
filename = filename,
content = content
)
@app.route('/paste/<name>/<token>', methods=['POST'])
@app.route('/paste/<name>', methods=['POST'])
def paste(name = None, token = None):
if request.method == 'POST':
file = request.form['filename']
paste = request.form['paste']
if name == None:
name = request.form['name']
(ok,share) = get_share(name, token = token)
if not ok:
notify({
"share": name,
"operation": "unauthorized_paste"
})
return share
if not get_or_none('upload', share) == True:
return "Upload not allowed\n",400
if file:
filename = os.path.join(
share['path'],
secure_filename(
file
)
)
if get_or_none('overwrite', share) == False:
if os.path.exists(filename):
file_versionize(filename)
#~ return "Overwrite forbidden", 403
print_debug("Saving " + filename)
with open(filename, 'wt') as fp:
fp.write(paste)
fp.close()
set_rights(filename)
notify({
"recipient": get_or_none('recipient', share),
"share": name,
"filename": filename,
"operation": "paste"
})
if 'from_gui' in request.form:
if request.form['from_gui'] == "true":
return redirect(url_for('list_view',name=name))
return "File uploaded\n", 200
else:
return "Use the 'file' variable to paste\n",400
@app.route('/file/list/<name>/<token>', methods=['GET'])
def file_list(name, token):
(ok,share) = get_share(name, token = token)
if not ok:
return share
files = []
for file in iter_folder_files(share['path'], version_folder = app.config['VERSION_FOLDER']):
if file_autoremove(file, share, notify):
continue
files.append(path2url(file))
files.append("")
return "\n".join(files), 200
@app.route('/file/details/<name>/<token>', methods=['GET'])
def file_details(name, token):
(ok,share) = get_share(name, token = token)
if not ok:
return share
files = []
for file in iter_folder_files(share['path'], version_folder = app.config['VERSION_FOLDER']):
status = file_stat(share['path'],file)
files.append(status)
return jsonify(files), 200
@app.route('/file/delete/<name>/<token>/<path:filename>', methods=['GET'])
def file_delete(name, token, filename):
(ok,share) = get_share(name, token = token)
if not ok:
return share
full_path = os.path.join(
share['path'],
secure_filename_hidden(filename)
)
if not os.path.exists(full_path):
return "-1", 403
allow_direct = get_or_none('direct_links', share) if get_or_none('pass_hash', share) else False
if not allow_direct:
return "Not allowed", 403
upload = get_or_none('upload', share)
if not upload:
return "Not allowed", 403
overwrite = get_or_none('overwrite', share)
if overwrite:
os.remove(full_path)
else:
file_versionize(full_path)
return "OK", 200
@app.route('/file/direct/<name>/<token>/<path:filename>', methods=['GET'])
def file_direct(name, token, filename):
(ok,share) = get_share(name, token = token)
if not ok:
return share
full_path = os.path.join(
share['path'],
secure_filename_hidden(filename)
)
if not os.path.exists(full_path):
return "-1", 403
allow_direct = get_or_none('direct_links', share) if get_or_none('pass_hash', share) else False
if not allow_direct:
return "-1", 403
token = get_direct_token(share, filename)
# url_for returns extra level of /../path if proxying with nginx
return "/".join((
app.config['PUBLIC_URL'],
'direct',
name,
token,
path2url(filename)
)), 200
@app.route('/file/ls/<name>/<token>', methods=['GET'])
def file_ls(name, token):
(ok,share) = get_share(name, token = token)
if not ok:
return share
files = []
maxlen = 4
for file in iter_folder_files(share['path'], version_folder = app.config['VERSION_FOLDER']):
if file_autoremove(file, share, notify):
continue
files.append(file)
maxlen = max(maxlen, len(file))
details = []
details.append(
"%%16s %%8s %%-%ds %%s"%( maxlen, )%(
'Modified',
'Size',
'Name',
'Type',
)
)
details.append("="*80)
for file in files:
status = file_stat(share['path'],file)
details.append(
"%%16s %%8s %%-%ds %%s"%( maxlen, )%(
status['mtime'],
status['hsize'],
status['name'],
status['mime'],
)
)
return "\n".join(details), 200
@app.route('/file/size/<name>/<token>/<path:filename>', methods=['GET'])
def file_size(name, token, filename):
(ok,share) = get_share(name, token = token)
if not ok:
return share
full_path = os.path.join(
share['path'],
secure_filename_hidden(filename)
)
if not os.path.exists(full_path):
return "-1", 200
size = os.stat(full_path).st_size
return str(size), 200
@app.route('/list/<name>/<token>', methods=['GET'])
@app.route('/list/<name>', methods=['GET'])
def list_view(name, token = None):
(ok,share) = get_share(name, token = token)
if not ok:
notify({
"share": name,
"operation": "unauthorized_list"
})
return share
if token != None and 'pass_hash' in share:
session[name] = share['pass_hash']
session[name + 'Token'] = token
return redirect(url_for('list_view',name=name))
files = []
for file in iter_folder_files(share['path'], version_folder = app.config['VERSION_FOLDER']):
if file_autoremove(file, share, notify):
continue
status = file_stat(share['path'],file)
status.update({
'token': get_direct_token(share, file),
})
files.append(status)
# direct share links not allowed if password isnt set
allow_direct = get_or_none('direct_links', share) if get_or_none('pass_hash', share) else False
upload = get_or_none('upload', share)
overwrite = get_or_none('overwrite', share)
if name + 'Token' in session:
used_token = session[name + 'Token']
else:
used_token = '[TOKEN]'
script_api = [get_script_url(app.config['PUBLIC_URL'], share, x, used_token) for x in ('client', 'download', 'upload_split', 'flip')]
if not upload:
overwrite = False
return render_template(
"list.html",
name = share['name'],
entries = files,
password = get_or_none('pass_hash', share),
public = get_or_none('public', share),
upload = upload,
overwrite = overwrite,
direct = allow_direct,
expire = get_or_none('expire', share),
description = get_or_none('description', share, ""),
script_api = script_api
)
@app.route('/logout/<name>', methods=['GET'])
def logout(name):
if name in session:
del session[name]
if name + 'Token' in session:
del session[name + 'Token']
return render_template(
"logout.html",
name = name
)
@app.route('/direct/<name>/<token>/<path:filename>', methods=['GET'])
def download_direct(name,token,filename):
(ok,share) = get_share(name, require_auth = False)
if not ok:
return share
allow_direct = get_or_none('direct_links', share)
if allow_direct != True:
return 'Direct download not allowed', 403
if not is_path_safe(filename):
return 'Incorrect relative path'+filename, 403
file_token = get_direct_token(share, filename)
if file_token == None:
return 'Cannot generate token', 400
if file_token != token:
notify({
"recipient": get_or_none('recipient', share),
"share": name,
"operation": "unauthorized_direct_download"
})
return 'Incorrect token', 403
file_autoremove(filename, share, notify)
file_path = os.path.join(share['path'], filename)
if not os.path.exists(file_path):
return 'No such file', 404
notify({
"recipient": get_or_none('recipient', share),
"share": name,
"filename": file_path,
"operation": "direct_download"
})
return send_from_directory(directory=share['path'], filename=filename)
@app.route('/download/gui/<name>/<path:filename>', methods=['GET'])
def download_gui(name, filename):
return download_file(name, filename, token = None)
@app.route('/download/<name>/<token>/<path:filename>', methods=['GET'])
def download_token(name, filename, token):
return download_file(name, filename, token = token)
@app.route('/zip/<name>/<token>', methods=['GET'])
@app.route('/zip/<name>', methods=['GET'])
def download_zip(name, token = None):
(ok,share) = get_share(name, token = token)
if not ok:
notify({
"share": name,
"operation": "unauthorized_zip_download"
})
return share
folder_size = get_folder_size(share['path'])
if folder_size/(1024*1024) > app.config['MAX_ZIP_SIZE']:
return "Maximum ZIP size exceeded", 400
zip_clean()
zip_path = zip_share(share)
notify({
"recipient": get_or_none('recipient', share),
"share": name,
"filename": name + ".zip",
"operation": "zip_download"
})
return send_file(
zip_path,
as_attachment = True,
attachment_filename = name + ".zip"
)
@app.route('/script/client', methods=['GET'])
@app.route('/script/client/<name>/<token>', methods=['GET'])
def script_client(name = "", token = ""):
notify({
'share': name,
'operation': 'script_client'
})
return render_template(
"client.py",
name = name,
token = token,
rooturl = request.url_root
)
@app.route('/script/flip/<name>/<token>', methods=['GET'])
@app.route('/script/flip', methods=['GET'])
def script_flip(name = "", token = ""):
notify({
'share': name,
'operation': 'script_flip'
})
return render_template(
"flip",
name = name,
token = token,
rooturl = request.url_root,
version = __FLEES_VERSION__
)
@app.route('/script/upload/<name>/<token>', methods=['GET'])
def script_upload(name = None, token = None):
(ok,share) = get_share(name, token = token)
if not ok:
return share
if not get_or_none('upload', share) == True:
return "Upload not allowed",400
return render_template(
"upload.sh",
name = name,
token = token,
rooturl = request.url_root
)
@app.route('/script/download/<name>/<token>', methods=['GET'])
def script_download(name = None, token = None):
(ok,share) = get_share(name, token = token)
if not ok:
return share
commands = []
for file in iter_folder_files(share['path'], version_folder = app.config['VERSION_FOLDER']):
status = file_stat(share['path'], file)
commands.append('get_file "%s"'%(
status['url'],
))
return render_template(
"download.sh",
name = name,
token = token,
rooturl = request.url_root,
commands = "\n".join(commands)
)
@app.route('/script/direct/<name>/<token>', methods=['GET'])
def script_direct(name = None, token = None):
(ok,share) = get_share(name, token = token)
if not ok:
return share
commands = []
for file in iter_folder_files(share['path'], version_folder = app.config['VERSION_FOLDER']):
status = file_stat(share['path'], file)
commands.append('get_file "%s" "%s"'%(
status['url'],
get_direct_token(share, file)
))
return render_template(
"download_direct.sh",
name = name,
rooturl = request.url_root,
commands = "\n".join(commands)
)
@app.route('/script/upload_split/<name>/<token>', methods=['GET'])
def script_upload_split(name = None, token = None):
(ok,share) = get_share(name, token = token)
if not ok:
return share
if not get_or_none('upload', share) == True:
return "Upload not allowed",400
return render_template(
"upload_split.py",
name = name,
token = token,
rooturl = request.url_root
)
#~ mmmmm # ""#
#~ # "# mmm mmm m m mmmmm mmm #mmm # mmm
#~ #mmmm" #" # # " # # # # # " # #" "# # #" #
#~ # "m #"""" """m # # # # # m"""# # # # #""""
#~ # " "#mm" "mmm" "mm"# # # # "mm"# ##m#" "mm "#mm"
#~ @app.route("/resumable/send")
#~ def resumable_example():
#~ return render_template("resumable_upload.html")
#~ # resumable.js uses a GET request to check if it uploaded the file already.
#~ # NOTE: your validation here needs to match whatever you do in the POST (otherwise it will NEVER find the files)
#~ @app.route("/resumable/get", methods=['GET'])
#~ def resumable():
#~ resumableIdentfier = request.args.get('resumableIdentifier', type=str)
#~ resumableFilename = request.args.get('resumableFilename', type=str)
#~ resumableChunkNumber = request.args.get('resumableChunkNumber', type=int)
#~ if not resumableIdentfier or not resumableFilename or not resumableChunkNumber:
#~ # Parameters are missing or invalid
#~ abort(500, 'Parameter error')
#~ # chunk folder path based on the parameters
#~ temp_dir = os.path.join(temp_base, resumableIdentfier)
#~ # chunk path based on the parameters
#~ chunk_file = os.path.join(temp_dir, get_chunk_name(resumableFilename, resumableChunkNumber))
#~ app.logger.debug('Getting chunk: %s', chunk_file)
#~ if os.path.isfile(chunk_file):
#~ # Let resumable.js know this chunk already exists
#~ return 'OK'
#~ else:
#~ # Let resumable.js know this chunk does not exists and needs to be uploaded
#~ abort(404, 'Not found')
#~ # if it didn't already upload, resumable.js sends the file here
#~ @app.route("/resumable/post", methods=['POST'])
#~ def resumable_post():
#~ resumableTotalChunks = request.form.get('resumableTotalChunks', type=int)
#~ resumableChunkNumber = request.form.get('resumableChunkNumber', default=1, type=int)
#~ resumableFilename = request.form.get('resumableFilename', default='error', type=str)
#~ resumableIdentfier = request.form.get('resumableIdentifier', default='error', type=str)
#~ # get the chunk data
#~ chunk_data = request.files['file']
#~ # make our temp directory
#~ temp_dir = os.path.join(temp_base, resumableIdentfier)
#~ if not os.path.isdir(temp_dir):
#~ os.makedirs(temp_dir, 0777)
#~ # save the chunk data
#~ chunk_name = get_chunk_name(resumableFilename, resumableChunkNumber)
#~ chunk_file = os.path.join(temp_dir, chunk_name)
#~ chunk_data.save(chunk_file)
#~ app.logger.debug('Saved chunk: %s', chunk_file)
#~ # check if the upload is complete
#~ chunk_paths = [os.path.join(temp_dir, get_chunk_name(resumableFilename, x)) for x in range(1, resumableTotalChunks+1)]
#~ upload_complete = all([os.path.exists(p) for p in chunk_paths])
#~ # combine all the chunks to create the final file
#~ if upload_complete:
#~ target_file_name = os.path.join(temp_base, resumableFilename)
#~ with open(target_file_name, "ab") as target_file:
#~ for p in chunk_paths:
#~ stored_chunk_file_name = p
#~ stored_chunk_file = open(stored_chunk_file_name, 'rb')
#~ target_file.write(stored_chunk_file.read())
#~ stored_chunk_file.close()
#~ os.unlink(stored_chunk_file_name)
#~ target_file.close()
#~ os.rmdir(temp_dir)
#~ app.logger.debug('File saved to: %s', target_file_name)
#~ return 'OK'
#~ def get_chunk_name(uploaded_filename, chunk_number):
#~ return uploaded_filename + "_part_%03d" % chunk_number
class uploadJoiner:
def __init__(self, target_name, parts):
self.target_name = target_name
self.parts = parts
self.chunk_size = 10 * 1024 * 1024
p = Process(target=self.run, args=())
p.daemon = True
p.start()
def run(self):
with open(self.target_name,'wb') as writer:
for part in self.parts:
with open(part,'rb') as reader:
for chunk in iter(lambda: reader.read(self.chunk_size), b""):
writer.write(chunk)
set_rights(self.target_name)
for part in self.parts:
os.remove(part)
def download_file(name, filename, token = None):
(ok,share) = get_share(name, token = token)
if not ok:
notify({
"share": name,
"operation": "unauthorized_download"
})
return share
if not is_path_safe(filename):
return "Incorrect path", 403
file_autoremove(filename, share, notify)
file_path = os.path.join(share['path'], filename)
if not os.path.exists(file_path):
return 'No such file, '+file_path, 404
notify({
"recipient": get_or_none('recipient', share),
"share": name,
"filename": file_path,
"operation": "download"
})
return send_from_directory(directory=share['path'], filename=filename)
def file_versionize(full_path):
""" Move file to versioned with integer """
file_dir = os.path.dirname(full_path)
file_name = os.path.basename(full_path)
new_name = file_name_version(full_path)
new_path = os.path.join(
file_dir,
app.config['VERSION_FOLDER'],
new_name
)
if os.path.exists(new_path):
return
makedirs_rights(os.path.join(
file_dir,
app.config['VERSION_FOLDER']
))
os.rename(full_path, new_path)
def get_share(name, require_auth = True, token = None):
share = [x for x in g.shares if x['name'] == name]
if len(share) < 1:
return (False,redirect(url_for('authenticate',name=name)))
share = share[0]
if is_expired(share):
return (False, 'Share has expired')
authenticated = "no-pass"
if not token == None:
require_auth = False
if has_token(token, share):
authenticated = "token"
else:
authenticated = False
if require_auth:
if 'pass_hash' in share:
authenticated = False
if name in session:
if session[name] == share['pass_hash']:
authenticated = "hash"
if not authenticated:
return (False,redirect(url_for('authenticate',name=name)))
if not 'path' in share:
return (False,'no path defined')
share.update({
"path": os.path.join(
app.config['UPLOAD_FOLDER'],
share['path']
),
"authenticated": authenticated
})
if not os.path.exists(share['path']):
makedirs_rights(share['path'])
return (True,share)
def is_expired(share):
expires = get_or_none('expire', share)
if expires:
if datetime.now() > datetime.strptime(expires, app.config['DATE_FORMAT']):
return True
return False
def print_debug(s):
if app.config['DEBUG']:
sys.stderr.write(str(s)+"\n")
sys.stderr.flush()
def makedirs_rights(path):
# os.makedirs with chown
path_list = path.split(os.sep)
for p in range(len(path_list)):
current_path = os.sep.join(path_list[0:(p+1)])
if not os.path.exists(current_path):
os.mkdir(current_path)
set_rights(current_path)
def notify(msg):
""" Log notifier messages, and run the custom notifier """
address = request.environ['REMOTE_ADDR']
if 'HTTP_X_FORWARDED_FOR' in request.environ:
address += ':' + request.environ['HTTP_X_FORWARDED_FOR']
log_msg = "%s: %s"%(
address,
', '.join(["%s:%s"%( k, msg[k] ) for k in sorted(msg)])
)
g.logger.info(log_msg)
if 'notifier' in app.config:
msg['environment'] = request.environ
app.config['notifier'].notify(msg)
def secure_filename_hidden(filename):
secure = secure_filename(filename)
if filename.startswith("."):
secure = "." + secure
return secure
def zip_share(share):
if not os.path.exists(app.config['ZIP_FOLDER']):
os.makedirs(app.config['ZIP_FOLDER'])
set_rights(app.config['ZIP_FOLDER'])
zip_path = os.path.join(
app.config['ZIP_FOLDER'],
"%s-%d.zip"%(
share['name'],
time.time()
)
)
zf = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED)
for file in iter_folder_files(share['path'], version_folder = app.config['VERSION_FOLDER']):
if file_autoremove(file, share, notify):
continue
fp = os.path.join(share['path'], file)
if os.path.isdir(fp):
continue
print_debug(fp)
zf.write(
fp,
arcname = os.path.join(share['name'],file)
)
zf.close()
set_rights(zip_path)
return zip_path
def zip_clean():
""" delete zip files older than 1 hour """
if not os.path.exists(app.config['ZIP_FOLDER']):
return
for file in os.listdir(app.config['ZIP_FOLDER']):
if not file.endswith("zip"):
continue
mtime = os.stat(
os.path.join(app.config['ZIP_FOLDER'],file)
).st_mtime
if mtime + 3600 < time.time():
os.remove(os.path.join(app.config['ZIP_FOLDER'],file))
def get_download_url(share, file, token):
direct = get_or_none('direct_links', share, False)
if direct:
return request.url_root + url_for(
'download_direct',
name = share['name'],
token = get_direct_token(share, file),
filename = file
)
else:
return request.url_root + url_for(
'download_token',
name = share['name'],
filename = file,
token = token
)
if __name__ == "__main__":
zip_clean()
app.run(debug=True)