new short expiring url for files
This commit is contained in:
65
code/app.py
65
code/app.py
@@ -13,7 +13,7 @@ from revprox import ReverseProxied
|
||||
from utils import *
|
||||
|
||||
|
||||
__FLEES_VERSION__ = "20191024.0"
|
||||
__FLEES_VERSION__ = "20191031.0"
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(__name__)
|
||||
config_values = read_config(app)
|
||||
@@ -30,7 +30,8 @@ if 'notifier' in config_values:
|
||||
|
||||
app.secret_key = config_values['app_secret_key']
|
||||
app.wsgi_app = ReverseProxied(app.wsgi_app)
|
||||
|
||||
with app.app_context():
|
||||
expire_database_create()
|
||||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
@@ -351,6 +352,42 @@ def file_delete(name, token, filename):
|
||||
return "OK", 200
|
||||
|
||||
|
||||
@app.route('/file/expiring/<name>/<token>/<expire>/<path:filename>', methods=['GET'])
|
||||
def file_expiring(name, token, expire, 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
|
||||
expires = 24 * 3600 * float(expire) + time.time()
|
||||
return set_expiring_file(share, full_path, expires), 200
|
||||
|
||||
|
||||
@app.route('/file/expiring_remove/<name>/<token>/<path:filename>', methods=['GET'])
|
||||
def file_expiring_remove(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
|
||||
remove_expiring_file(share, 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)
|
||||
@@ -485,6 +522,27 @@ def logout(name):
|
||||
)
|
||||
|
||||
|
||||
@app.route('/e/<ehash>', methods=['GET'])
|
||||
@app.route('/e/<ehash>/<filename>', methods=['GET'])
|
||||
def download_expiring(ehash, filename = None):
|
||||
file_path, expiring = get_expiring_file(ehash)
|
||||
if not os.path.exists(file_path):
|
||||
return 'No such file', 404
|
||||
if expiring - time.time() < 0:
|
||||
return 'Expired', 404
|
||||
notify({
|
||||
"filename": file_path,
|
||||
"operation": "expiring_download"
|
||||
})
|
||||
if filename == None:
|
||||
filename = os.path.basename(file_path)
|
||||
return send_file(
|
||||
file_path,
|
||||
as_attachment = True,
|
||||
attachment_filename = filename
|
||||
)
|
||||
|
||||
|
||||
@app.route('/direct/<name>/<token>/<path:filename>', methods=['GET'])
|
||||
def download_direct(name,token,filename):
|
||||
(ok,share) = get_share(name, require_auth = False)
|
||||
@@ -932,9 +990,10 @@ def zip_clean():
|
||||
os.remove(os.path.join(app.config['ZIP_FOLDER'],file))
|
||||
|
||||
|
||||
zip_clean()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
zip_clean()
|
||||
app.run(debug=True)
|
||||
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@ pycrypto
|
||||
requests
|
||||
python-magic
|
||||
python-dateutil
|
||||
apsw
|
||||
|
||||
@@ -15,6 +15,9 @@ _help() {
|
||||
(c)opy Copy file/folder
|
||||
(p)aste Paste file/folder (will overwrite)
|
||||
url Get the direct share url
|
||||
short Get a short share url. Add argument as days of expiration
|
||||
Default: 14 days
|
||||
short-del Delete short share url for a file.
|
||||
upload Get URL for uploads [no arguments]
|
||||
update Update flip client [no arguments]
|
||||
self Get URL to install this client to another computer
|
||||
@@ -194,6 +197,14 @@ _url() { # name
|
||||
curl -L -s "$FLEES_ROOTURL/file/direct/$FLEES_SHARE/$FLEES_TOKEN/$1"
|
||||
echo ''
|
||||
}
|
||||
_short() {
|
||||
curl -L -s "$FLEES_ROOTURL/file/expiring/$FLEES_SHARE/$FLEES_TOKEN/$2/$1"
|
||||
echo ''
|
||||
}
|
||||
_short_del() {
|
||||
curl -L -s "$FLEES_ROOTURL/file/expiring_remove/$FLEES_SHARE/$FLEES_TOKEN/$1"
|
||||
echo ''
|
||||
}
|
||||
_upload_url() {
|
||||
echo "This information is a security risk, watch where it's shared"
|
||||
echo "# python2 <( curl -L -s $FLEES_ROOTURL/script/upload_split/$FLEES_SHARE/$FLEES_TOKEN ) file_to_upload.ext"
|
||||
@@ -283,6 +294,8 @@ CMD=list
|
||||
[[ "$1" = "simplelist" ]] && { CMD=simple_list; ARG1=$CMD; }
|
||||
[[ "$1" = "autocomplete" ]] && { _get_completer; }
|
||||
[[ "$1" = "url" ]] && { CMD=url; ARG1=$CMD; }
|
||||
[[ "$1" = "short" ]] && { CMD=short; ARG1=$CMD; }
|
||||
[[ "$1" = "short-del" ]] && { CMD=short-del; ARG1=$CMD; }
|
||||
[[ "$1" = "update" ]] && { _update_client; }
|
||||
[[ "$1" = "upload" ]] && { _upload_url; }
|
||||
[[ "$1" = "self" ]] && { _self_url; }
|
||||
@@ -324,3 +337,15 @@ _get_file
|
||||
_url "$NAME"
|
||||
exit $?
|
||||
}
|
||||
[[ "$CMD" = short ]] && {
|
||||
EXPIRY=14
|
||||
if [ -n "$3" ]; then
|
||||
EXPIRY="$3"
|
||||
fi
|
||||
_short "$NAME" $EXPIRY
|
||||
exit $?
|
||||
}
|
||||
[[ "$CMD" = short-del ]] && {
|
||||
_short_del "$NAME"
|
||||
exit $?
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import base64
|
||||
#~ from Crypto.Cipher import AES
|
||||
import hashlib
|
||||
|
||||
|
||||
#~ class Crypto:
|
||||
#~ def __init__(self, secret):
|
||||
#~ self.secret = add_pad(secret[0:16])
|
||||
@@ -68,6 +69,14 @@ def random_token():
|
||||
return token
|
||||
|
||||
|
||||
def random_expiring_hash():
|
||||
codes = []
|
||||
for i in range(3):
|
||||
codes.append("".join([random.choice(string.ascii_letters + string.digits) for n in range(4)]))
|
||||
ehash = "-".join(codes)
|
||||
return ehash
|
||||
|
||||
|
||||
def remove_pad(string):
|
||||
""" Remove spaces from right """
|
||||
return string.rstrip(" ")
|
||||
|
||||
@@ -5,6 +5,11 @@ import requests
|
||||
import re
|
||||
import json
|
||||
import stat
|
||||
import time
|
||||
try:
|
||||
import apsw
|
||||
except ImportError as e:
|
||||
pass
|
||||
from .misc import *
|
||||
from .crypt import *
|
||||
|
||||
@@ -76,6 +81,26 @@ def download_url(url, filename):
|
||||
return (True, ("OK", 200 ))
|
||||
|
||||
|
||||
def expire_database_create():
|
||||
connection = apsw.Connection(app.config['SQLITE_FILE'])
|
||||
cursor = connection.cursor()
|
||||
try:
|
||||
cursor.execute("""CREATE TABLE IF NOT EXISTS expiring (
|
||||
hash text PRIMARY KEY,
|
||||
file text NOT NULL,
|
||||
expires integer NOT NULL
|
||||
);""")
|
||||
cursor.execute("DELETE FROM expiring WHERE expires < ?",
|
||||
(
|
||||
time.time(),
|
||||
)
|
||||
)
|
||||
except apsw.BusyError as e:
|
||||
# Other thread is creating the database
|
||||
pass
|
||||
set_rights(app.config['SQLITE_FILE'])
|
||||
|
||||
|
||||
def file_autoremove(path, share, notifier = None):
|
||||
autoremove = get_or_none('autoremove', share, 0)
|
||||
if autoremove == 0:
|
||||
@@ -207,6 +232,17 @@ def get_download_url(share, file, token):
|
||||
))
|
||||
|
||||
|
||||
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
|
||||
@@ -267,6 +303,7 @@ def read_config(app):
|
||||
app.config['SITE_NAME'] = config_values['site_name']
|
||||
app.config['UPLOAD_FOLDER'] = config_values['data_folder']
|
||||
app.config['SHARES_FILE'] = config_values['shares_file']
|
||||
app.config['SQLITE_FILE'] = config_values['sqlite_file']
|
||||
if 'log_file' in config_values:
|
||||
app.config['LOG_FILE'] = config_values['log_file']
|
||||
else:
|
||||
@@ -294,3 +331,41 @@ def set_rights(path):
|
||||
os.chmod(path, st.st_mode | stat.S_IRUSR | stat.S_IWUSR)
|
||||
if app.config['GID'] > 0:
|
||||
os.chmod(path, st.st_mode | stat.S_IRGRP | stat.S_IWGRP)
|
||||
|
||||
|
||||
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,
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user