2016-05-12 13:34:28 -05:00
|
|
|
##########################################################################
|
|
|
|
#
|
|
|
|
# pgAdmin 4 - PostgreSQL Tools
|
|
|
|
#
|
2024-01-01 02:43:48 -06:00
|
|
|
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
2016-05-12 13:34:28 -05:00
|
|
|
# This software is released under the PostgreSQL Licence
|
|
|
|
#
|
|
|
|
##########################################################################
|
|
|
|
|
|
|
|
"""Implements File Manager"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import os.path
|
2022-08-12 06:40:26 -05:00
|
|
|
import secrets
|
2016-05-12 13:34:28 -05:00
|
|
|
import string
|
2016-06-21 08:12:14 -05:00
|
|
|
import time
|
2020-07-27 05:03:13 -05:00
|
|
|
from urllib.parse import unquote
|
2016-05-12 13:34:28 -05:00
|
|
|
from sys import platform as _platform
|
2023-03-06 05:33:47 -06:00
|
|
|
from flask_security import current_user
|
2024-06-10 07:34:32 -05:00
|
|
|
from pgadmin.utils.constants import ACCESS_DENIED_MESSAGE, TWO_PARAM_STRING
|
2017-02-03 03:51:36 -06:00
|
|
|
import config
|
2017-05-09 06:06:49 -05:00
|
|
|
import codecs
|
2020-09-11 09:25:19 -05:00
|
|
|
import pathlib
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2023-02-14 23:40:12 -06:00
|
|
|
import json
|
2017-12-05 22:42:05 -06:00
|
|
|
from flask import render_template, Response, session, request as req, \
|
2020-10-23 05:44:55 -05:00
|
|
|
url_for, current_app, send_from_directory
|
2021-11-24 05:52:57 -06:00
|
|
|
from flask_babel import gettext
|
2024-04-29 03:11:02 -05:00
|
|
|
from pgadmin.user_login_check import pga_login_required
|
2016-06-21 08:12:14 -05:00
|
|
|
from pgadmin.utils import PgAdminModule
|
|
|
|
from pgadmin.utils import get_storage_directory
|
2022-07-19 04:57:47 -05:00
|
|
|
from pgadmin.utils.ajax import make_json_response, unauthorized, \
|
|
|
|
internal_server_error
|
2017-07-26 07:09:52 -05:00
|
|
|
from pgadmin.utils.preferences import Preferences
|
2023-03-06 05:33:47 -06:00
|
|
|
from pgadmin.utils.constants import PREF_LABEL_OPTIONS, MIMETYPE_APP_JS, \
|
|
|
|
MY_STORAGE
|
2022-07-19 04:57:47 -05:00
|
|
|
from pgadmin.settings.utils import get_file_type_setting
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
# Checks if platform is Windows
|
|
|
|
if _platform == "win32":
|
|
|
|
import ctypes
|
2017-07-10 08:04:57 -05:00
|
|
|
oldmode = ctypes.c_uint()
|
|
|
|
kernel32 = ctypes.WinDLL('kernel32')
|
|
|
|
SEM_FAILCRITICALERRORS = 1
|
|
|
|
SEM_NOOPENFILEERRORBOX = 0x8000
|
|
|
|
SEM_FAIL = SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS
|
2016-05-12 13:34:28 -05:00
|
|
|
file_root = ""
|
|
|
|
|
|
|
|
MODULE_NAME = 'file_manager'
|
|
|
|
global transid
|
|
|
|
|
|
|
|
path_exists = os.path.exists
|
|
|
|
split_path = os.path.split
|
|
|
|
encode_json = json.JSONEncoder().encode
|
|
|
|
|
|
|
|
|
|
|
|
# utility functions
|
|
|
|
# convert bytes type to human readable format
|
|
|
|
def sizeof_fmt(num, suffix='B'):
|
2019-11-01 10:00:34 -05:00
|
|
|
for unit in ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
2016-05-12 13:34:28 -05:00
|
|
|
if abs(num) < 1024.0:
|
|
|
|
return "%3.1f %s%s" % (num, unit, suffix)
|
|
|
|
num /= 1024.0
|
|
|
|
return "%.1f %s%s" % (num, 'Y', suffix)
|
|
|
|
|
|
|
|
|
|
|
|
# return size of file
|
2020-07-06 01:18:23 -05:00
|
|
|
def getsize(path):
|
2016-05-12 13:34:28 -05:00
|
|
|
st = os.stat(path)
|
|
|
|
return st.st_size
|
|
|
|
|
|
|
|
|
2020-07-06 01:18:23 -05:00
|
|
|
def getdrivesize(path):
|
2016-05-12 13:34:28 -05:00
|
|
|
if _platform == "win32":
|
|
|
|
free_bytes = ctypes.c_ulonglong(0)
|
|
|
|
ctypes.windll.kernel32.GetDiskFreeSpaceExW(
|
|
|
|
ctypes.c_wchar_p(path), None, None, ctypes.pointer(free_bytes))
|
|
|
|
return free_bytes.value
|
|
|
|
|
|
|
|
|
|
|
|
# split extension for files
|
|
|
|
def splitext(path):
|
|
|
|
for ext in ['.tar.gz', '.tar.bz2']:
|
|
|
|
if path.endswith(ext):
|
|
|
|
path, ext = path[:-len(ext)], path[-len(ext):]
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
path, ext = os.path.splitext(path)
|
|
|
|
return ext[1:]
|
|
|
|
|
|
|
|
|
|
|
|
# check if file is hidden in windows platform
|
|
|
|
def is_folder_hidden(filepath):
|
|
|
|
if _platform == "win32":
|
|
|
|
try:
|
2020-04-30 06:52:48 -05:00
|
|
|
attrs = ctypes.windll.kernel32.GetFileAttributesW(filepath)
|
2016-05-12 13:34:28 -05:00
|
|
|
assert attrs != -1
|
|
|
|
result = bool(attrs & 2)
|
|
|
|
except (AttributeError, AssertionError):
|
|
|
|
result = False
|
|
|
|
return result
|
2020-09-23 01:29:14 -05:00
|
|
|
else:
|
|
|
|
return os.path.basename(filepath).startswith('.')
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
|
|
|
|
class FileManagerModule(PgAdminModule):
|
|
|
|
"""
|
|
|
|
FileManager lists files and folders and does
|
|
|
|
following operations:
|
|
|
|
- File selection
|
|
|
|
- Folder selection
|
|
|
|
- Open file
|
|
|
|
- Create File
|
|
|
|
and also supports:
|
|
|
|
- Rename file
|
|
|
|
- Delete file
|
|
|
|
- Upload file
|
|
|
|
- Create folder
|
|
|
|
"""
|
|
|
|
|
|
|
|
LABEL = gettext("Storage")
|
|
|
|
|
|
|
|
def get_own_menuitems(self):
|
|
|
|
return {
|
|
|
|
'file_items': []
|
|
|
|
}
|
|
|
|
|
2017-07-18 09:13:16 -05:00
|
|
|
def get_exposed_url_endpoints(self):
|
|
|
|
"""
|
|
|
|
Returns:
|
|
|
|
list: a list of url endpoints exposed to the client.
|
|
|
|
"""
|
|
|
|
return [
|
2022-07-19 04:57:47 -05:00
|
|
|
'file_manager.init',
|
2017-07-18 09:13:16 -05:00
|
|
|
'file_manager.filemanager',
|
|
|
|
'file_manager.index',
|
|
|
|
'file_manager.delete_trans_id',
|
2017-07-26 08:35:43 -05:00
|
|
|
'file_manager.save_last_dir',
|
2017-09-28 04:02:33 -05:00
|
|
|
'file_manager.save_file_dialog_view',
|
|
|
|
'file_manager.save_show_hidden_file_option'
|
2017-07-18 09:13:16 -05:00
|
|
|
]
|
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
def get_file_size_preference(self):
|
|
|
|
return self.file_upload_size
|
|
|
|
|
|
|
|
def register_preferences(self):
|
|
|
|
# Register 'file upload size' preference
|
|
|
|
self.file_upload_size = self.preference.register(
|
|
|
|
'options', 'file_upload_size',
|
2016-08-17 09:10:33 -05:00
|
|
|
gettext("Maximum file upload size (MB)"), 'integer', 50,
|
2020-08-20 07:28:37 -05:00
|
|
|
category_label=PREF_LABEL_OPTIONS
|
2016-06-21 08:21:06 -05:00
|
|
|
)
|
2017-01-07 22:40:38 -06:00
|
|
|
self.last_directory_visited = self.preference.register(
|
|
|
|
'options', 'last_directory_visited',
|
|
|
|
gettext("Last directory visited"), 'text', '/',
|
2020-08-20 07:28:37 -05:00
|
|
|
category_label=PREF_LABEL_OPTIONS
|
2017-01-07 22:40:38 -06:00
|
|
|
)
|
2023-03-06 05:33:47 -06:00
|
|
|
self.last_storage = self.preference.register(
|
|
|
|
'options', 'last_storage',
|
|
|
|
gettext("Last storage"), 'text', '',
|
|
|
|
category_label=PREF_LABEL_OPTIONS,
|
|
|
|
hidden=True
|
|
|
|
)
|
2017-07-26 07:09:52 -05:00
|
|
|
self.file_dialog_view = self.preference.register(
|
|
|
|
'options', 'file_dialog_view',
|
2022-03-21 02:59:26 -05:00
|
|
|
gettext("File dialog view"), 'select', 'list',
|
2020-08-20 07:28:37 -05:00
|
|
|
category_label=PREF_LABEL_OPTIONS,
|
2017-07-26 07:09:52 -05:00
|
|
|
options=[{'label': gettext('List'), 'value': 'list'},
|
2022-03-21 02:59:26 -05:00
|
|
|
{'label': gettext('Grid'), 'value': 'grid'}],
|
|
|
|
control_props={
|
|
|
|
'allowClear': False,
|
|
|
|
'tags': False
|
|
|
|
},
|
2017-07-26 07:09:52 -05:00
|
|
|
)
|
2017-09-28 04:02:33 -05:00
|
|
|
self.show_hidden_files = self.preference.register(
|
|
|
|
'options', 'show_hidden_files',
|
|
|
|
gettext("Show hidden files and folders?"), 'boolean', False,
|
2020-08-20 07:28:37 -05:00
|
|
|
category_label=PREF_LABEL_OPTIONS
|
2017-09-28 04:02:33 -05:00
|
|
|
)
|
2016-06-21 08:21:06 -05:00
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
# Initialise the module
|
|
|
|
blueprint = FileManagerModule(MODULE_NAME, __name__)
|
|
|
|
|
|
|
|
|
2017-07-18 09:13:16 -05:00
|
|
|
@blueprint.route("/", endpoint='index')
|
2024-04-29 03:11:02 -05:00
|
|
|
@pga_login_required
|
2016-05-12 13:34:28 -05:00
|
|
|
def index():
|
2023-02-09 22:58:39 -06:00
|
|
|
return bad_request(
|
|
|
|
errormsg=gettext("This URL cannot be called directly.")
|
|
|
|
)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/utility.js")
|
2024-04-29 03:11:02 -05:00
|
|
|
@pga_login_required
|
2016-05-12 13:34:28 -05:00
|
|
|
def utility():
|
|
|
|
"""render the required javascript"""
|
|
|
|
return Response(response=render_template(
|
|
|
|
"file_manager/js/utility.js", _=gettext),
|
|
|
|
status=200,
|
2020-08-20 09:56:51 -05:00
|
|
|
mimetype=MIMETYPE_APP_JS)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
|
2017-07-18 09:13:16 -05:00
|
|
|
@blueprint.route(
|
2022-07-19 04:57:47 -05:00
|
|
|
"/init", methods=["POST"], endpoint='init'
|
2017-07-18 09:13:16 -05:00
|
|
|
)
|
2024-04-29 03:11:02 -05:00
|
|
|
@pga_login_required
|
2022-07-19 04:57:47 -05:00
|
|
|
def init_filemanager():
|
2016-05-12 13:34:28 -05:00
|
|
|
if len(req.data) != 0:
|
|
|
|
configs = json.loads(req.data)
|
|
|
|
trans_id = Filemanager.create_new_transaction(configs)
|
2022-07-19 04:57:47 -05:00
|
|
|
data = Filemanager.get_trasaction_selection(trans_id)
|
|
|
|
pref = Preferences.module('file_manager')
|
|
|
|
file_dialog_view = pref.preference('file_dialog_view').get()
|
2023-07-31 07:44:39 -05:00
|
|
|
if isinstance(file_dialog_view, list):
|
2022-07-19 04:57:47 -05:00
|
|
|
file_dialog_view = file_dialog_view[0]
|
|
|
|
|
|
|
|
last_selected_format = get_file_type_setting(data['supported_types'])
|
|
|
|
# in some cases, the setting may not match with available types
|
|
|
|
if last_selected_format not in data['supported_types']:
|
|
|
|
last_selected_format = data['supported_types'][0]
|
|
|
|
|
|
|
|
res_data = {
|
|
|
|
'transId': trans_id,
|
|
|
|
"options": {
|
|
|
|
"culture": "en",
|
|
|
|
"lang": "py",
|
|
|
|
"defaultViewMode": file_dialog_view,
|
|
|
|
"autoload": True,
|
|
|
|
"showFullPath": False,
|
|
|
|
"dialog_type": data['dialog_type'],
|
|
|
|
"show_hidden_files":
|
|
|
|
pref.preference('show_hidden_files').get(),
|
|
|
|
"fileRoot": data['fileroot'],
|
|
|
|
"capabilities": data['capabilities'],
|
|
|
|
"allowed_file_types": data['supported_types'],
|
|
|
|
"platform_type": data['platform_type'],
|
|
|
|
"show_volumes": data['show_volumes'],
|
|
|
|
"homedir": data['homedir'],
|
2023-03-06 05:33:47 -06:00
|
|
|
'storage_folder': data['storage_folder'],
|
2022-07-19 04:57:47 -05:00
|
|
|
"last_selected_format": last_selected_format
|
|
|
|
},
|
|
|
|
"security": {
|
|
|
|
"uploadPolicy": data['security']['uploadPolicy'],
|
|
|
|
"uploadRestrictions": data['security']['uploadRestrictions'],
|
|
|
|
},
|
|
|
|
"upload": {
|
|
|
|
"multiple": data['upload']['multiple'],
|
|
|
|
"number": 20,
|
|
|
|
"fileSizeLimit": data['upload']['fileSizeLimit'],
|
|
|
|
"imagesOnly": False
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return make_json_response(data=res_data)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
|
2017-07-18 09:13:16 -05:00
|
|
|
@blueprint.route(
|
2022-07-19 04:57:47 -05:00
|
|
|
"/delete_trans_id/<int:trans_id>",
|
|
|
|
methods=["DELETE"], endpoint='delete_trans_id'
|
2017-07-18 09:13:16 -05:00
|
|
|
)
|
2024-04-29 03:11:02 -05:00
|
|
|
@pga_login_required
|
2016-05-12 13:34:28 -05:00
|
|
|
def delete_trans_id(trans_id):
|
|
|
|
Filemanager.release_transaction(trans_id)
|
|
|
|
return make_json_response(
|
|
|
|
data={'status': True}
|
|
|
|
)
|
|
|
|
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2017-07-18 09:13:16 -05:00
|
|
|
@blueprint.route(
|
|
|
|
"/save_last_dir/<int:trans_id>", methods=["POST"], endpoint='save_last_dir'
|
|
|
|
)
|
2024-04-29 03:11:02 -05:00
|
|
|
@pga_login_required
|
2017-01-07 22:40:38 -06:00
|
|
|
def save_last_directory_visited(trans_id):
|
|
|
|
blueprint.last_directory_visited.set(req.json['path'])
|
2023-03-06 05:33:47 -06:00
|
|
|
blueprint.last_storage.set(req.json['storage_folder'])
|
2022-07-19 04:57:47 -05:00
|
|
|
return make_json_response(status=200)
|
2017-01-07 22:40:38 -06:00
|
|
|
|
2018-02-09 06:57:37 -06:00
|
|
|
|
2017-07-26 08:35:43 -05:00
|
|
|
@blueprint.route(
|
|
|
|
"/save_file_dialog_view/<int:trans_id>", methods=["POST"],
|
|
|
|
endpoint='save_file_dialog_view'
|
|
|
|
)
|
2024-04-29 03:11:02 -05:00
|
|
|
@pga_login_required
|
2017-07-26 08:35:43 -05:00
|
|
|
def save_file_dialog_view(trans_id):
|
|
|
|
blueprint.file_dialog_view.set(req.json['view'])
|
2022-07-19 04:57:47 -05:00
|
|
|
return make_json_response(status=200)
|
2017-07-26 08:35:43 -05:00
|
|
|
|
2018-02-09 06:57:37 -06:00
|
|
|
|
2017-09-28 04:02:33 -05:00
|
|
|
@blueprint.route(
|
|
|
|
"/save_show_hidden_file_option/<int:trans_id>", methods=["PUT"],
|
|
|
|
endpoint='save_show_hidden_file_option'
|
|
|
|
)
|
2024-04-29 03:11:02 -05:00
|
|
|
@pga_login_required
|
2017-09-28 04:02:33 -05:00
|
|
|
def save_show_hidden_file_option(trans_id):
|
|
|
|
blueprint.show_hidden_files.set(req.json['show_hidden'])
|
2022-07-19 04:57:47 -05:00
|
|
|
return make_json_response(status=200)
|
2017-09-28 04:02:33 -05:00
|
|
|
|
2016-06-21 08:21:06 -05:00
|
|
|
|
2022-11-18 22:43:41 -06:00
|
|
|
class Filemanager():
|
2016-05-12 13:34:28 -05:00
|
|
|
"""FileManager Class."""
|
2016-06-21 08:21:06 -05:00
|
|
|
|
2020-08-03 02:48:04 -05:00
|
|
|
# Stores list of dict for filename & its encoding
|
|
|
|
loaded_file_encoding_list = []
|
|
|
|
|
2020-09-11 09:25:19 -05:00
|
|
|
ERROR_NOT_ALLOWED = {
|
|
|
|
'Error': gettext('Not allowed'),
|
|
|
|
'Code': 0
|
|
|
|
}
|
|
|
|
|
2023-03-06 05:33:47 -06:00
|
|
|
def __init__(self, trans_id, ss=''):
|
2016-05-12 13:34:28 -05:00
|
|
|
self.trans_id = trans_id
|
|
|
|
self.dir = get_storage_directory()
|
2023-06-12 08:14:31 -05:00
|
|
|
self.shared_dir = get_storage_directory(shared_storage=ss)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2017-02-03 03:51:36 -06:00
|
|
|
if self.dir is not None and isinstance(self.dir, list):
|
2016-06-17 12:19:51 -05:00
|
|
|
self.dir = ""
|
|
|
|
|
2020-09-23 01:29:14 -05:00
|
|
|
@staticmethod
|
|
|
|
def get_closest_parent(storage_dir, last_dir):
|
|
|
|
"""
|
|
|
|
Check if path exists and if not then get closest parent which exists
|
|
|
|
:param storage_dir: Base dir
|
|
|
|
:param last_dir: check dir
|
|
|
|
:return: exist dir
|
|
|
|
"""
|
|
|
|
if len(last_dir) > 1 and \
|
|
|
|
(last_dir.endswith('/') or last_dir.endswith('\\')):
|
|
|
|
last_dir = last_dir[:-1]
|
|
|
|
while last_dir:
|
|
|
|
if os.path.exists(storage_dir or '' + last_dir):
|
|
|
|
break
|
|
|
|
index = max(last_dir.rfind('\\'), last_dir.rfind('/')) \
|
|
|
|
if _platform == 'win32' else last_dir.rfind('/')
|
|
|
|
last_dir = last_dir[0:index]
|
|
|
|
|
|
|
|
if _platform == 'win32':
|
|
|
|
if not last_dir.endswith('\\'):
|
|
|
|
last_dir += "\\"
|
|
|
|
|
|
|
|
return last_dir
|
|
|
|
|
|
|
|
if not last_dir.endswith('/'):
|
|
|
|
last_dir += "/"
|
|
|
|
|
|
|
|
return last_dir
|
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
@staticmethod
|
|
|
|
def create_new_transaction(params):
|
|
|
|
"""
|
|
|
|
It will also create a unique transaction id and
|
|
|
|
store the information into session variable.
|
|
|
|
Args:
|
|
|
|
capabilities: Allow/Disallow user to perform
|
|
|
|
selection, rename, delete etc.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Define configs for dialog types
|
|
|
|
# select file, select folder, create mode
|
2017-07-10 08:04:57 -05:00
|
|
|
Filemanager.suspend_windows_warning()
|
2016-05-12 13:34:28 -05:00
|
|
|
fm_type = params['dialog_type']
|
|
|
|
storage_dir = get_storage_directory()
|
|
|
|
|
|
|
|
# It is used in utitlity js to decide to
|
|
|
|
# show or hide select file type options
|
2016-05-13 14:08:58 -05:00
|
|
|
show_volumes = isinstance(storage_dir, list) or not storage_dir
|
2020-09-23 01:29:14 -05:00
|
|
|
supp_types = allow_upload_files = params.get('supported_types', [])
|
|
|
|
|
|
|
|
# tuples with (capabilities, files_only, folders_only, title)
|
|
|
|
capability_map = {
|
|
|
|
'select_file': (
|
2022-07-19 04:57:47 -05:00
|
|
|
['select_file', 'rename', 'upload', 'delete'],
|
2020-09-23 01:29:14 -05:00
|
|
|
True,
|
|
|
|
False,
|
|
|
|
gettext("Select File")
|
|
|
|
),
|
|
|
|
'select_folder': (
|
|
|
|
['select_folder', 'rename', 'create'],
|
|
|
|
False,
|
|
|
|
True,
|
|
|
|
gettext("Select Folder")
|
|
|
|
),
|
|
|
|
'create_file': (
|
|
|
|
['select_file', 'rename', 'create'],
|
|
|
|
True,
|
|
|
|
False,
|
|
|
|
gettext("Create File")
|
|
|
|
),
|
|
|
|
'storage_dialog': (
|
|
|
|
['select_folder', 'select_file', 'download',
|
|
|
|
'rename', 'delete', 'upload', 'create'],
|
|
|
|
True,
|
|
|
|
False,
|
|
|
|
gettext("Storage Manager")
|
|
|
|
),
|
|
|
|
}
|
|
|
|
|
|
|
|
capabilities, files_only, folders_only, title = capability_map[fm_type]
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2020-05-04 02:10:19 -05:00
|
|
|
# Using os.path.join to make sure we have trailing '/' or '\'
|
|
|
|
homedir = '/' if (config.SERVER_MODE) \
|
|
|
|
else os.path.join(os.path.expanduser('~'), '')
|
|
|
|
|
2017-02-03 03:51:36 -06:00
|
|
|
# get last visited directory, if not present then traverse in reverse
|
|
|
|
# order to find closest parent directory
|
2022-07-19 04:57:47 -05:00
|
|
|
if 'init_path' in params:
|
|
|
|
blueprint.last_directory_visited.get(params['init_path'])
|
2017-01-07 22:40:38 -06:00
|
|
|
last_dir = blueprint.last_directory_visited.get()
|
2023-03-06 05:33:47 -06:00
|
|
|
last_ss_name = blueprint.last_storage.get()
|
|
|
|
if last_ss_name and last_ss_name != MY_STORAGE \
|
|
|
|
and len(config.SHARED_STORAGE) > 0:
|
2023-06-12 08:14:31 -05:00
|
|
|
selected_dir = [sdir for sdir in config.SHARED_STORAGE if
|
|
|
|
sdir['name'] == last_ss_name]
|
|
|
|
last_ss = selected_dir[0]['path'] if len(
|
|
|
|
selected_dir) == 1 else storage_dir
|
2023-03-06 05:33:47 -06:00
|
|
|
else:
|
|
|
|
if last_ss_name != MY_STORAGE:
|
|
|
|
last_dir = '/'
|
|
|
|
blueprint.last_storage.set(MY_STORAGE)
|
|
|
|
|
|
|
|
last_ss = storage_dir
|
|
|
|
|
2017-02-03 08:19:21 -06:00
|
|
|
check_dir_exists = False
|
2020-09-23 01:29:14 -05:00
|
|
|
if last_dir is None:
|
|
|
|
last_dir = "/"
|
2017-01-07 22:40:38 -06:00
|
|
|
else:
|
2020-09-23 01:29:14 -05:00
|
|
|
check_dir_exists = True
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2020-08-31 06:15:31 -05:00
|
|
|
if not config.SERVER_MODE and last_dir == "/" or last_dir == "/":
|
2020-05-04 02:10:19 -05:00
|
|
|
last_dir = homedir
|
|
|
|
|
2017-02-03 08:19:21 -06:00
|
|
|
if check_dir_exists:
|
2023-03-06 05:33:47 -06:00
|
|
|
last_dir = Filemanager.get_closest_parent(last_ss, last_dir)
|
2017-02-03 08:19:21 -06:00
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
# create configs using above configs
|
|
|
|
configs = {
|
2022-07-19 04:57:47 -05:00
|
|
|
"fileroot": last_dir,
|
|
|
|
"homedir": homedir,
|
2023-03-06 05:33:47 -06:00
|
|
|
'storage_folder': last_ss_name,
|
2016-06-21 08:21:06 -05:00
|
|
|
"dialog_type": fm_type,
|
|
|
|
"title": title,
|
|
|
|
"upload": {
|
|
|
|
"multiple": True
|
|
|
|
},
|
|
|
|
"capabilities": capabilities,
|
|
|
|
"security": {
|
|
|
|
"uploadPolicy": "",
|
|
|
|
"uploadRestrictions": allow_upload_files
|
|
|
|
},
|
|
|
|
"files_only": files_only,
|
|
|
|
"folders_only": folders_only,
|
|
|
|
"supported_types": supp_types,
|
|
|
|
"platform_type": _platform,
|
|
|
|
"show_volumes": show_volumes
|
2016-05-12 13:34:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
# Create a unique id for the transaction
|
2022-08-12 06:40:26 -05:00
|
|
|
trans_id = str(secrets.choice(range(1, 9999999)))
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
if 'fileManagerData' not in session:
|
|
|
|
file_manager_data = dict()
|
|
|
|
else:
|
|
|
|
file_manager_data = session['fileManagerData']
|
|
|
|
|
|
|
|
file_upload_size = blueprint.get_file_size_preference().get()
|
|
|
|
configs['upload']['fileSizeLimit'] = file_upload_size
|
|
|
|
file_manager_data[trans_id] = configs
|
|
|
|
session['fileManagerData'] = file_manager_data
|
2017-07-10 08:04:57 -05:00
|
|
|
Filemanager.resume_windows_warning()
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
return trans_id
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_trasaction_selection(trans_id):
|
|
|
|
"""
|
|
|
|
This method returns the information of unique transaction
|
|
|
|
id from the session variable.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
trans_id: unique transaction id
|
|
|
|
"""
|
|
|
|
file_manager_data = session['fileManagerData']
|
|
|
|
|
|
|
|
# Return from the function if transaction id not found
|
|
|
|
if str(trans_id) in file_manager_data:
|
|
|
|
return file_manager_data[str(trans_id)]
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def release_transaction(trans_id):
|
|
|
|
"""
|
|
|
|
This method is to remove the information of unique transaction
|
|
|
|
id from the session variable.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
trans_id: unique transaction id
|
|
|
|
"""
|
|
|
|
file_manager_data = session['fileManagerData']
|
|
|
|
# Return from the function if transaction id not found
|
|
|
|
if str(trans_id) not in file_manager_data:
|
2022-07-19 04:57:47 -05:00
|
|
|
return make_json_response(status=200)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
# Remove the information of unique transaction id
|
|
|
|
# from the session variable.
|
|
|
|
file_manager_data.pop(str(trans_id), None)
|
|
|
|
session['fileManagerData'] = file_manager_data
|
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
return make_json_response(status=200)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2016-05-13 14:08:58 -05:00
|
|
|
@staticmethod
|
2020-09-23 01:29:14 -05:00
|
|
|
def _get_drives_with_size(drive_name=None):
|
2016-05-13 14:08:58 -05:00
|
|
|
"""
|
|
|
|
This is a generic function which returns the default path for storage
|
|
|
|
manager dialog irrespective of any Platform type to list all
|
|
|
|
files and directories.
|
|
|
|
Platform windows:
|
|
|
|
if no path is given, it will list volumes, else list directory
|
|
|
|
Platform unix:
|
|
|
|
it returns path to root directory if no path is specified.
|
|
|
|
"""
|
2020-09-23 01:29:14 -05:00
|
|
|
def _get_drive_size(path):
|
|
|
|
try:
|
|
|
|
drive_size = getdrivesize(path)
|
|
|
|
return sizeof_fmt(drive_size)
|
|
|
|
except Exception:
|
|
|
|
return 0
|
|
|
|
|
2016-05-13 14:08:58 -05:00
|
|
|
if _platform == "win32":
|
|
|
|
try:
|
|
|
|
drives = []
|
|
|
|
bitmask = ctypes.windll.kernel32.GetLogicalDrives()
|
2020-07-27 05:03:13 -05:00
|
|
|
for letter in string.ascii_uppercase:
|
2016-05-13 14:08:58 -05:00
|
|
|
if bitmask & 1:
|
2020-09-23 01:29:14 -05:00
|
|
|
drives.append((letter, _get_drive_size(letter)))
|
2016-05-13 14:08:58 -05:00
|
|
|
bitmask >>= 1
|
|
|
|
if (drive_name != '' and drive_name is not None and
|
2018-02-09 06:57:37 -06:00
|
|
|
drive_name in drives):
|
2020-09-23 01:29:14 -05:00
|
|
|
letter = "{0}{1}".format(drive_name, ':')
|
|
|
|
return (letter, _get_drive_size(letter))
|
2016-05-13 14:08:58 -05:00
|
|
|
else:
|
|
|
|
return drives # return drives if no argument is passed
|
|
|
|
except Exception:
|
2020-09-23 01:29:14 -05:00
|
|
|
return [('C:', _get_drive_size('C:'))]
|
2016-05-13 14:08:58 -05:00
|
|
|
else:
|
2020-08-31 06:15:31 -05:00
|
|
|
return '/'
|
2016-05-13 14:08:58 -05:00
|
|
|
|
2017-07-10 08:04:57 -05:00
|
|
|
@staticmethod
|
|
|
|
def suspend_windows_warning():
|
|
|
|
"""
|
|
|
|
Prevents 'there is no disk in drive' waning on windows
|
|
|
|
"""
|
|
|
|
# StackOverflow Ref: https://goo.gl/9gYdef
|
|
|
|
if _platform == "win32":
|
|
|
|
kernel32.SetThreadErrorMode(SEM_FAIL, ctypes.byref(oldmode))
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def resume_windows_warning():
|
|
|
|
"""
|
|
|
|
Resumes waning on windows
|
|
|
|
"""
|
|
|
|
if _platform == "win32":
|
|
|
|
# Resume windows error
|
|
|
|
kernel32.SetThreadErrorMode(oldmode, ctypes.byref(oldmode))
|
|
|
|
|
2020-09-23 01:29:14 -05:00
|
|
|
@staticmethod
|
|
|
|
def _skip_file_extension(
|
|
|
|
file_type, supported_types, folders_only, file_extension):
|
|
|
|
"""
|
|
|
|
Used internally by get_files_in_path to check if
|
|
|
|
the file extn to be skipped
|
|
|
|
"""
|
|
|
|
return file_type is not None and file_type != "*" and (
|
|
|
|
folders_only or len(supported_types) > 0 and
|
|
|
|
file_extension not in supported_types or
|
|
|
|
file_type != file_extension)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_files_in_path(
|
|
|
|
show_hidden_files, files_only, folders_only, supported_types,
|
|
|
|
file_type, user_dir, orig_path):
|
|
|
|
"""
|
|
|
|
Get list of files and dirs in the path
|
|
|
|
:param show_hidden_files: boolean
|
|
|
|
:param files_only: boolean
|
|
|
|
:param folders_only: boolean
|
|
|
|
:param supported_types: array of supported types
|
|
|
|
:param file_type: file type
|
|
|
|
:param user_dir: base user dir
|
|
|
|
:param orig_path: path after user dir
|
|
|
|
:return:
|
|
|
|
"""
|
2022-07-19 04:57:47 -05:00
|
|
|
files = []
|
2020-09-23 01:29:14 -05:00
|
|
|
|
|
|
|
for f in sorted(os.listdir(orig_path)):
|
|
|
|
system_path = os.path.join(os.path.join(orig_path, f))
|
|
|
|
|
|
|
|
# continue if file/folder is hidden (based on user preference)
|
|
|
|
if not show_hidden_files and is_folder_hidden(system_path):
|
|
|
|
continue
|
2021-03-04 01:18:56 -06:00
|
|
|
try:
|
|
|
|
user_path = os.path.join(os.path.join(user_dir, f))
|
|
|
|
created = time.ctime(os.path.getctime(system_path))
|
|
|
|
modified = time.ctime(os.path.getmtime(system_path))
|
|
|
|
file_extension = str(splitext(system_path))
|
2021-03-19 01:26:12 -05:00
|
|
|
except Exception:
|
2021-03-04 01:18:56 -06:00
|
|
|
continue
|
2020-09-23 01:29:14 -05:00
|
|
|
|
|
|
|
# set protected to 1 if no write or read permission
|
|
|
|
protected = 0
|
|
|
|
if (not os.access(system_path, os.R_OK) or
|
|
|
|
not os.access(system_path, os.W_OK)):
|
|
|
|
protected = 1
|
|
|
|
|
|
|
|
# list files only or folders only
|
|
|
|
if os.path.isdir(system_path):
|
|
|
|
if files_only == 'true':
|
|
|
|
continue
|
|
|
|
file_extension = "dir"
|
|
|
|
# filter files based on file_type
|
|
|
|
elif Filemanager._skip_file_extension(
|
|
|
|
file_type, supported_types, folders_only, file_extension):
|
|
|
|
continue
|
|
|
|
|
|
|
|
# create a list of files and folders
|
2022-07-19 04:57:47 -05:00
|
|
|
files.append({
|
2020-09-23 01:29:14 -05:00
|
|
|
"Filename": f,
|
|
|
|
"Path": user_path,
|
|
|
|
"file_type": file_extension,
|
|
|
|
"Protected": protected,
|
|
|
|
"Properties": {
|
|
|
|
"Date Created": created,
|
|
|
|
"Date Modified": modified,
|
|
|
|
"Size": sizeof_fmt(getsize(system_path))
|
|
|
|
}
|
2022-07-19 04:57:47 -05:00
|
|
|
})
|
2020-09-23 01:29:14 -05:00
|
|
|
|
|
|
|
return files
|
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
@staticmethod
|
2020-07-28 05:50:26 -05:00
|
|
|
def list_filesystem(in_dir, path, trans_data, file_type, show_hidden):
|
2016-05-12 13:34:28 -05:00
|
|
|
"""
|
|
|
|
It lists all file and folders within the given
|
|
|
|
directory.
|
|
|
|
"""
|
2017-07-10 08:04:57 -05:00
|
|
|
Filemanager.suspend_windows_warning()
|
2017-09-28 04:02:33 -05:00
|
|
|
is_show_hidden_files = show_hidden
|
|
|
|
|
2016-12-04 22:10:56 -06:00
|
|
|
path = unquote(path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
Filemanager.check_access_permission(in_dir, path)
|
|
|
|
Filemanager.resume_windows_warning()
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
files = []
|
2017-02-03 03:51:36 -06:00
|
|
|
if (_platform == "win32" and (path == '/' or path == '\\'))\
|
2020-07-28 05:50:26 -05:00
|
|
|
and in_dir is None:
|
2020-09-23 01:29:14 -05:00
|
|
|
drives = Filemanager._get_drives_with_size()
|
|
|
|
for drive, drive_size in drives:
|
2020-08-31 06:15:31 -05:00
|
|
|
path = file_name = "{0}:".format(drive)
|
2022-07-19 04:57:47 -05:00
|
|
|
files.append({
|
2016-05-12 13:34:28 -05:00
|
|
|
"Filename": file_name,
|
|
|
|
"Path": path,
|
|
|
|
"file_type": 'drive',
|
2020-09-23 01:29:14 -05:00
|
|
|
"Protected": 1 if drive_size == 0 else 0,
|
2016-05-12 13:34:28 -05:00
|
|
|
"Properties": {
|
|
|
|
"Date Created": "",
|
|
|
|
"Date Modified": "",
|
2020-09-23 01:29:14 -05:00
|
|
|
"Size": drive_size
|
2016-06-21 08:21:06 -05:00
|
|
|
}
|
2022-07-19 04:57:47 -05:00
|
|
|
})
|
2017-07-10 08:04:57 -05:00
|
|
|
Filemanager.resume_windows_warning()
|
2016-05-12 13:34:28 -05:00
|
|
|
return files
|
|
|
|
|
2020-07-28 05:50:26 -05:00
|
|
|
orig_path = Filemanager.get_abs_path(in_dir, path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
|
|
|
if not path_exists(orig_path):
|
2017-07-10 08:04:57 -05:00
|
|
|
Filemanager.resume_windows_warning()
|
2022-07-19 04:57:47 -05:00
|
|
|
return make_json_response(
|
|
|
|
status=404,
|
|
|
|
errormsg=gettext("'{0}' file does not exist.").format(path))
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
user_dir = path
|
2020-09-23 01:29:14 -05:00
|
|
|
folders_only = trans_data.get('folders_only', '')
|
|
|
|
files_only = trans_data.get('files_only', '')
|
|
|
|
supported_types = trans_data.get('supported_types', [])
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
orig_path = unquote(orig_path)
|
|
|
|
try:
|
2020-09-23 01:29:14 -05:00
|
|
|
files = Filemanager.get_files_in_path(
|
|
|
|
is_show_hidden_files, files_only, folders_only,
|
|
|
|
supported_types, file_type, user_dir, orig_path
|
|
|
|
)
|
2016-05-12 13:34:28 -05:00
|
|
|
except Exception as e:
|
2017-07-10 08:04:57 -05:00
|
|
|
Filemanager.resume_windows_warning()
|
2020-09-23 01:29:14 -05:00
|
|
|
err_msg = str(e)
|
2016-10-21 06:43:02 -05:00
|
|
|
if (hasattr(e, 'strerror') and
|
|
|
|
e.strerror == gettext('Permission denied')):
|
2020-09-03 08:05:58 -05:00
|
|
|
err_msg = str(e.strerror)
|
2022-07-19 04:57:47 -05:00
|
|
|
return unauthorized(err_msg)
|
2017-07-10 08:04:57 -05:00
|
|
|
Filemanager.resume_windows_warning()
|
2016-05-12 13:34:28 -05:00
|
|
|
return files
|
|
|
|
|
2017-02-03 03:51:36 -06:00
|
|
|
@staticmethod
|
2022-08-31 03:58:48 -05:00
|
|
|
def check_access_permission(in_dir, path, skip_permission_check=False):
|
|
|
|
if not config.SERVER_MODE or skip_permission_check:
|
2020-09-03 07:40:57 -05:00
|
|
|
return
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2020-09-03 07:40:57 -05:00
|
|
|
in_dir = '' if in_dir is None else in_dir
|
|
|
|
orig_path = Filemanager.get_abs_path(in_dir, path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2020-09-03 07:40:57 -05:00
|
|
|
# This translates path with relative path notations
|
|
|
|
# like ./ and ../ to absolute path.
|
|
|
|
orig_path = os.path.abspath(orig_path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2020-09-03 07:40:57 -05:00
|
|
|
if in_dir:
|
|
|
|
if _platform == 'win32':
|
|
|
|
if in_dir[-1] == '\\' or in_dir[-1] == '/':
|
|
|
|
in_dir = in_dir[:-1]
|
|
|
|
else:
|
|
|
|
if in_dir[-1] == '/':
|
|
|
|
in_dir = in_dir[:-1]
|
|
|
|
|
|
|
|
# Do not allow user to access outside his storage dir
|
|
|
|
# in server mode.
|
2022-07-19 04:57:47 -05:00
|
|
|
try:
|
|
|
|
pathlib.Path(orig_path).relative_to(in_dir)
|
|
|
|
except ValueError:
|
|
|
|
raise PermissionError(gettext("Access denied ({0})").format(path))
|
2017-02-03 03:51:36 -06:00
|
|
|
|
|
|
|
@staticmethod
|
2020-07-28 05:50:26 -05:00
|
|
|
def get_abs_path(in_dir, path):
|
2017-02-03 03:51:36 -06:00
|
|
|
|
|
|
|
if (path.startswith('\\\\') and _platform == 'win32')\
|
2020-07-28 05:50:26 -05:00
|
|
|
or config.SERVER_MODE is False or in_dir is None:
|
2020-08-31 06:15:31 -05:00
|
|
|
return "{}".format(path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
|
|
|
if path == '/' or path == '\\':
|
|
|
|
if _platform == 'win32':
|
2020-07-28 05:50:26 -05:00
|
|
|
if in_dir.endswith('\\') or in_dir.endswith('/'):
|
2020-08-31 06:15:31 -05:00
|
|
|
return "{}".format(in_dir)
|
2017-02-03 03:51:36 -06:00
|
|
|
else:
|
2020-08-31 06:15:31 -05:00
|
|
|
return "{}{}".format(in_dir, '\\')
|
2017-02-03 03:51:36 -06:00
|
|
|
else:
|
2020-07-28 05:50:26 -05:00
|
|
|
if in_dir.endswith('/'):
|
2020-08-31 06:15:31 -05:00
|
|
|
return "{}".format(in_dir)
|
2017-02-03 03:51:36 -06:00
|
|
|
else:
|
2020-08-31 06:15:31 -05:00
|
|
|
return "{}{}".format(in_dir, '/')
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2020-07-28 05:50:26 -05:00
|
|
|
if in_dir.endswith('/') or in_dir.endswith('\\'):
|
2017-02-03 03:51:36 -06:00
|
|
|
if path.startswith('/') or path.startswith('\\'):
|
2020-08-31 06:15:31 -05:00
|
|
|
return "{}{}".format(in_dir[:-1], path)
|
2017-02-03 03:51:36 -06:00
|
|
|
else:
|
2024-06-10 07:34:32 -05:00
|
|
|
return TWO_PARAM_STRING.format(in_dir, path)
|
2017-02-03 03:51:36 -06:00
|
|
|
else:
|
|
|
|
if path.startswith('/') or path.startswith('\\'):
|
2020-08-31 06:15:31 -05:00
|
|
|
return "{}{}".format(in_dir, path)
|
2017-02-03 03:51:36 -06:00
|
|
|
else:
|
2024-06-10 07:34:32 -05:00
|
|
|
return TWO_PARAM_STRING.format(in_dir, path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
def validate_request(self, capability):
|
|
|
|
"""
|
|
|
|
It validates the capability with the capabilities
|
|
|
|
stored in the session
|
|
|
|
"""
|
|
|
|
trans_data = Filemanager.get_trasaction_selection(self.trans_id)
|
|
|
|
return False if capability not in trans_data['capabilities'] else True
|
|
|
|
|
2022-09-09 08:06:51 -05:00
|
|
|
def getfolder(self, path=None, file_type="", show_hidden=False):
|
2016-05-12 13:34:28 -05:00
|
|
|
"""
|
|
|
|
Returns files and folders in give path
|
|
|
|
"""
|
|
|
|
trans_data = Filemanager.get_trasaction_selection(self.trans_id)
|
2020-07-28 05:50:26 -05:00
|
|
|
the_dir = None
|
2017-02-03 03:51:36 -06:00
|
|
|
if config.SERVER_MODE:
|
2023-06-12 08:14:31 -05:00
|
|
|
if self.shared_dir and len(config.SHARED_STORAGE) > 0:
|
|
|
|
the_dir = self.shared_dir
|
2023-03-06 05:33:47 -06:00
|
|
|
else:
|
|
|
|
the_dir = self.dir
|
|
|
|
|
2020-07-28 05:50:26 -05:00
|
|
|
if the_dir is not None and not the_dir.endswith('/'):
|
2020-08-31 06:15:31 -05:00
|
|
|
the_dir += '/'
|
2017-01-07 22:40:38 -06:00
|
|
|
|
2018-02-09 06:57:37 -06:00
|
|
|
filelist = self.list_filesystem(
|
2020-07-28 05:50:26 -05:00
|
|
|
the_dir, path, trans_data, file_type, show_hidden)
|
2016-05-12 13:34:28 -05:00
|
|
|
return filelist
|
|
|
|
|
2023-06-12 08:14:31 -05:00
|
|
|
def check_access(self, ss):
|
|
|
|
if self.shared_dir:
|
|
|
|
selected_dir_list = [sdir for sdir in config.SHARED_STORAGE if
|
|
|
|
sdir['name'] == ss]
|
|
|
|
selected_dir = selected_dir_list[0] if len(
|
|
|
|
selected_dir_list) == 1 else None
|
2023-03-06 05:33:47 -06:00
|
|
|
|
2023-06-12 08:14:31 -05:00
|
|
|
if selected_dir and selected_dir['restricted_access'] and \
|
|
|
|
not current_user.has_role("Administrator"):
|
|
|
|
raise PermissionError(ACCESS_DENIED_MESSAGE)
|
2023-03-06 05:33:47 -06:00
|
|
|
|
2022-09-09 08:06:51 -05:00
|
|
|
def rename(self, old=None, new=None):
|
2016-05-12 13:34:28 -05:00
|
|
|
"""
|
|
|
|
Rename file or folder
|
|
|
|
"""
|
|
|
|
if not self.validate_request('rename'):
|
2022-07-19 04:57:47 -05:00
|
|
|
return unauthorized(self.ERROR_NOT_ALLOWED['Error'])
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2023-06-12 08:14:31 -05:00
|
|
|
if self.shared_dir:
|
|
|
|
the_dir = self.shared_dir
|
2023-03-06 05:33:47 -06:00
|
|
|
else:
|
|
|
|
the_dir = self.dir if self.dir is not None else ''
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
Filemanager.check_access_permission(the_dir, old)
|
|
|
|
Filemanager.check_access_permission(the_dir, new)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
# check if it's dir
|
|
|
|
if old[-1] == '/':
|
|
|
|
old = old[:-1]
|
|
|
|
|
|
|
|
# extract filename
|
|
|
|
oldname = split_path(old)[-1]
|
2016-12-04 22:10:56 -06:00
|
|
|
path = old
|
2016-05-12 13:34:28 -05:00
|
|
|
path = split_path(path)[0] # extract path
|
|
|
|
|
2022-08-05 08:28:03 -05:00
|
|
|
if path[-1] != '/':
|
2020-08-31 06:15:31 -05:00
|
|
|
path += '/'
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
newname = new
|
|
|
|
newpath = path + newname
|
|
|
|
|
|
|
|
# make system old path
|
2020-08-31 06:15:31 -05:00
|
|
|
oldpath_sys = "{0}{1}".format(the_dir, old)
|
|
|
|
newpath_sys = "{0}{1}".format(the_dir, newpath)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
try:
|
|
|
|
os.rename(oldpath_sys, newpath_sys)
|
2023-09-18 03:31:11 -05:00
|
|
|
except OSError as e:
|
2022-07-19 04:57:47 -05:00
|
|
|
return internal_server_error("{0} {1}".format(
|
2023-09-18 03:31:11 -05:00
|
|
|
gettext('There was an error renaming the file:'), e.strerror))
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
return {
|
2016-05-12 13:34:28 -05:00
|
|
|
'Old Path': old,
|
|
|
|
'Old Name': oldname,
|
|
|
|
'New Path': newpath,
|
|
|
|
'New Name': newname,
|
|
|
|
}
|
|
|
|
|
2022-09-09 08:06:51 -05:00
|
|
|
def delete(self, path=None):
|
2016-05-12 13:34:28 -05:00
|
|
|
"""
|
|
|
|
Delete file or folder
|
|
|
|
"""
|
|
|
|
if not self.validate_request('delete'):
|
2022-07-19 04:57:47 -05:00
|
|
|
return unauthorized(self.ERROR_NOT_ALLOWED['Error'])
|
2023-06-12 08:14:31 -05:00
|
|
|
if self.shared_dir:
|
|
|
|
the_dir = self.shared_dir
|
2023-03-06 05:33:47 -06:00
|
|
|
else:
|
|
|
|
the_dir = self.dir if self.dir is not None else ''
|
2020-08-31 06:15:31 -05:00
|
|
|
orig_path = "{0}{1}".format(the_dir, path)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
Filemanager.check_access_permission(the_dir, path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
try:
|
|
|
|
if os.path.isdir(orig_path):
|
|
|
|
os.rmdir(orig_path)
|
|
|
|
else:
|
|
|
|
os.remove(orig_path)
|
2023-09-18 03:31:11 -05:00
|
|
|
except OSError as e:
|
2022-07-19 04:57:47 -05:00
|
|
|
return internal_server_error("{0} {1}".format(
|
2023-09-18 03:31:11 -05:00
|
|
|
gettext('There was an error deleting the file:'), e.strerror))
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
return make_json_response(status=200)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
def add(self, req=None):
|
|
|
|
"""
|
|
|
|
File upload functionality
|
|
|
|
"""
|
|
|
|
if not self.validate_request('upload'):
|
2022-07-19 04:57:47 -05:00
|
|
|
return unauthorized(self.ERROR_NOT_ALLOWED['Error'])
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2023-06-12 08:14:31 -05:00
|
|
|
if self.shared_dir:
|
|
|
|
the_dir = self.shared_dir
|
2023-03-06 05:33:47 -06:00
|
|
|
else:
|
|
|
|
the_dir = self.dir if self.dir is not None else ''
|
2022-08-05 08:28:03 -05:00
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
try:
|
|
|
|
path = req.form.get('currentpath')
|
2016-12-04 22:10:56 -06:00
|
|
|
|
|
|
|
file_obj = req.files['newfile']
|
|
|
|
file_name = file_obj.filename
|
2020-08-31 06:15:31 -05:00
|
|
|
orig_path = "{0}{1}".format(the_dir, path)
|
|
|
|
new_name = "{0}{1}".format(orig_path, file_name)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2020-09-11 09:25:19 -05:00
|
|
|
try:
|
|
|
|
# Check if the new file is inside the users directory
|
2022-02-04 03:40:13 -06:00
|
|
|
if config.SERVER_MODE:
|
2022-03-11 06:50:16 -06:00
|
|
|
pathlib.Path(
|
|
|
|
os.path.abspath(
|
|
|
|
os.path.join(the_dir, new_name)
|
|
|
|
)
|
|
|
|
).relative_to(the_dir)
|
2022-01-12 03:23:19 -06:00
|
|
|
except ValueError:
|
2022-07-19 04:57:47 -05:00
|
|
|
return unauthorized(self.ERROR_NOT_ALLOWED['Error'])
|
2020-09-11 09:25:19 -05:00
|
|
|
|
2020-07-06 01:18:23 -05:00
|
|
|
with open(new_name, 'wb') as f:
|
2017-07-17 04:51:26 -05:00
|
|
|
while True:
|
2018-02-09 06:57:37 -06:00
|
|
|
# 4MB chunk (4 * 1024 * 1024 Bytes)
|
|
|
|
data = file_obj.read(4194304)
|
2017-07-17 04:51:26 -05:00
|
|
|
if not data:
|
|
|
|
break
|
|
|
|
f.write(data)
|
2023-09-18 03:31:11 -05:00
|
|
|
except OSError as e:
|
2022-07-19 04:57:47 -05:00
|
|
|
return internal_server_error("{0} {1}".format(
|
2023-09-18 03:31:11 -05:00
|
|
|
gettext('There was an error adding the file:'), e.strerror))
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
Filemanager.check_access_permission(the_dir, path)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
return {
|
2016-05-12 13:34:28 -05:00
|
|
|
'Path': path,
|
2020-07-06 01:18:23 -05:00
|
|
|
'Name': new_name,
|
2016-05-12 13:34:28 -05:00
|
|
|
}
|
|
|
|
|
2022-09-09 08:06:51 -05:00
|
|
|
def is_file_exist(self, path, name):
|
2016-05-12 13:34:28 -05:00
|
|
|
"""
|
|
|
|
Checks whether given file exists or not
|
|
|
|
"""
|
2020-07-28 05:50:26 -05:00
|
|
|
the_dir = self.dir if self.dir is not None else ''
|
2016-05-12 13:34:28 -05:00
|
|
|
code = 1
|
2016-12-04 22:10:56 -06:00
|
|
|
|
2016-05-21 04:31:47 -05:00
|
|
|
name = unquote(name)
|
2016-12-04 22:10:56 -06:00
|
|
|
path = unquote(path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
orig_path = "{0}{1}".format(the_dir, path)
|
|
|
|
Filemanager.check_access_permission(
|
|
|
|
the_dir, "{}{}".format(path, name))
|
|
|
|
|
|
|
|
new_name = "{0}{1}".format(orig_path, name)
|
|
|
|
if not os.path.exists(new_name):
|
2017-02-03 03:51:36 -06:00
|
|
|
code = 0
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
return {
|
2016-05-12 13:34:28 -05:00
|
|
|
'Path': path,
|
2016-05-21 04:31:47 -05:00
|
|
|
'Name': name,
|
2022-07-19 04:57:47 -05:00
|
|
|
'Code': code,
|
2016-05-12 13:34:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@staticmethod
|
2022-07-19 04:57:47 -05:00
|
|
|
def get_new_name(in_dir, path, name):
|
2016-05-12 13:34:28 -05:00
|
|
|
"""
|
|
|
|
Utility to provide new name for folder if file
|
|
|
|
with same name already exists
|
|
|
|
"""
|
2022-07-19 04:57:47 -05:00
|
|
|
new_name = name
|
|
|
|
count = 0
|
|
|
|
while True:
|
2023-07-10 08:02:21 -05:00
|
|
|
if not (path.endswith("/") or name.startswith("/")):
|
|
|
|
path = path + "/"
|
2022-07-19 04:57:47 -05:00
|
|
|
file_path = "{}{}/".format(path, new_name)
|
|
|
|
create_path = file_path
|
|
|
|
if in_dir != "":
|
2024-06-10 07:34:32 -05:00
|
|
|
create_path = TWO_PARAM_STRING.format(in_dir, file_path)
|
2022-07-19 04:57:47 -05:00
|
|
|
|
|
|
|
if not path_exists(create_path):
|
|
|
|
return create_path, file_path, new_name
|
2016-05-12 13:34:28 -05:00
|
|
|
else:
|
2022-07-19 04:57:47 -05:00
|
|
|
count += 1
|
|
|
|
new_name = "{}_{}".format(name, count)
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2017-05-09 06:06:49 -05:00
|
|
|
@staticmethod
|
|
|
|
def check_file_for_bom_and_binary(filename, enc="utf-8"):
|
|
|
|
"""
|
|
|
|
This utility function will check if file is Binary file
|
|
|
|
and/or if it startswith BOM character
|
|
|
|
|
|
|
|
Args:
|
|
|
|
filename: File
|
|
|
|
enc: Encoding for the file
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Status(Error?), Error message, Binary file flag,
|
|
|
|
BOM character flag and Encoding to open file
|
|
|
|
"""
|
|
|
|
status = True
|
|
|
|
err_msg = None
|
|
|
|
is_startswith_bom = False
|
2017-12-05 22:42:05 -06:00
|
|
|
is_binary = False
|
2017-05-09 06:06:49 -05:00
|
|
|
|
|
|
|
# check if file type is text or binary
|
|
|
|
text_chars = bytearray([7, 8, 9, 10, 12, 13, 27]) \
|
2018-02-09 06:57:37 -06:00
|
|
|
+ bytearray(range(0x20, 0x7f)) \
|
|
|
|
+ bytearray(range(0x80, 0x100))
|
2017-05-09 06:06:49 -05:00
|
|
|
|
|
|
|
def is_binary_string(bytes_data):
|
|
|
|
"""Checks if string data is binary"""
|
|
|
|
return bool(
|
2018-02-09 06:57:37 -06:00
|
|
|
bytes_data.translate(None, text_chars)
|
|
|
|
)
|
2017-05-09 06:06:49 -05:00
|
|
|
|
|
|
|
# read the file
|
|
|
|
try:
|
|
|
|
|
|
|
|
with open(filename, 'rb') as f:
|
|
|
|
file_data = f.read(1024)
|
|
|
|
|
|
|
|
# Check for BOM in file data
|
|
|
|
for encoding, boms in \
|
|
|
|
('utf-8-sig', (codecs.BOM_UTF8,)), \
|
|
|
|
('utf-16', (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE)), \
|
|
|
|
('utf-32', (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)):
|
|
|
|
if any(file_data.startswith(bom) for bom in boms):
|
|
|
|
is_startswith_bom = True
|
|
|
|
enc = encoding
|
|
|
|
|
2020-04-17 02:10:09 -05:00
|
|
|
# No need to check for binary file, a BOM marker already
|
|
|
|
# indicates that text stream afterwards
|
|
|
|
if not is_startswith_bom:
|
|
|
|
# Check if string is binary
|
|
|
|
is_binary = is_binary_string(file_data)
|
2017-05-09 06:06:49 -05:00
|
|
|
|
2020-08-03 02:48:04 -05:00
|
|
|
# Store encoding for future use
|
|
|
|
Filemanager.loaded_file_encoding_list.\
|
|
|
|
append({os.path.basename(filename): enc})
|
|
|
|
|
2017-05-09 06:06:49 -05:00
|
|
|
except IOError as ex:
|
|
|
|
# we don't want to expose real path of file
|
|
|
|
# so only show error message.
|
|
|
|
if ex.strerror == 'Permission denied':
|
2022-07-19 04:57:47 -05:00
|
|
|
return unauthorized(str(ex.strerror))
|
2017-05-09 06:06:49 -05:00
|
|
|
else:
|
2023-09-18 03:31:11 -05:00
|
|
|
return internal_server_error(str(ex.strerror))
|
2017-05-09 06:06:49 -05:00
|
|
|
|
|
|
|
except Exception as ex:
|
2023-09-18 03:31:11 -05:00
|
|
|
return internal_server_error(str(ex.strerror))
|
2017-05-09 06:06:49 -05:00
|
|
|
|
2017-12-05 22:42:05 -06:00
|
|
|
# Remove root storage path from error message
|
|
|
|
# when running in Server mode
|
|
|
|
if not status and not current_app.PGADMIN_RUNTIME:
|
2017-12-13 09:35:08 -06:00
|
|
|
storage_directory = get_storage_directory()
|
|
|
|
if storage_directory:
|
|
|
|
err_msg = err_msg.replace(storage_directory, '')
|
2017-12-05 22:42:05 -06:00
|
|
|
|
2017-05-09 06:06:49 -05:00
|
|
|
return status, err_msg, is_binary, is_startswith_bom, enc
|
|
|
|
|
2022-09-09 08:06:51 -05:00
|
|
|
def addfolder(self, path, name):
|
2016-05-12 13:34:28 -05:00
|
|
|
"""
|
|
|
|
Functionality to create new folder
|
|
|
|
"""
|
|
|
|
if not self.validate_request('create'):
|
2022-07-19 04:57:47 -05:00
|
|
|
return unauthorized(self.ERROR_NOT_ALLOWED['Error'])
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2023-06-12 08:14:31 -05:00
|
|
|
if self.shared_dir and len(config.SHARED_STORAGE) > 0:
|
|
|
|
user_dir = self.shared_dir
|
2023-03-06 05:33:47 -06:00
|
|
|
else:
|
|
|
|
user_dir = self.dir if self.dir is not None else ''
|
2022-07-19 04:57:47 -05:00
|
|
|
|
|
|
|
Filemanager.check_access_permission(user_dir, "{}{}".format(
|
|
|
|
path, name))
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
create_path, new_path, new_name = \
|
|
|
|
self.get_new_name(user_dir, path, name)
|
2017-02-03 03:51:36 -06:00
|
|
|
try:
|
2022-07-19 04:57:47 -05:00
|
|
|
os.mkdir(create_path)
|
2023-09-18 03:31:11 -05:00
|
|
|
except OSError as e:
|
|
|
|
return internal_server_error(str(e.strerror))
|
2016-05-12 13:34:28 -05:00
|
|
|
|
|
|
|
result = {
|
|
|
|
'Parent': path,
|
2022-07-19 04:57:47 -05:00
|
|
|
'Path': new_path,
|
2020-07-06 01:18:23 -05:00
|
|
|
'Name': new_name,
|
2022-07-19 04:57:47 -05:00
|
|
|
'Date Modified': time.ctime(time.time())
|
2016-05-12 13:34:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
2022-09-09 08:06:51 -05:00
|
|
|
def download(self, path=None):
|
2016-05-12 13:34:28 -05:00
|
|
|
"""
|
|
|
|
Functionality to download file
|
|
|
|
"""
|
|
|
|
if not self.validate_request('download'):
|
2022-07-19 04:57:47 -05:00
|
|
|
return unauthorized(self.ERROR_NOT_ALLOWED['Error'])
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2023-06-12 08:14:31 -05:00
|
|
|
if self.shared_dir and len(config.SHARED_STORAGE) > 0:
|
|
|
|
the_dir = self.shared_dir
|
2023-03-06 05:33:47 -06:00
|
|
|
else:
|
|
|
|
the_dir = self.dir if self.dir is not None else ''
|
|
|
|
|
2020-08-31 06:15:31 -05:00
|
|
|
orig_path = "{0}{1}".format(the_dir, path)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2022-07-19 04:57:47 -05:00
|
|
|
Filemanager.check_access_permission(
|
|
|
|
the_dir, "{}{}".format(path, path)
|
|
|
|
)
|
2017-02-03 03:51:36 -06:00
|
|
|
|
2022-08-05 08:28:03 -05:00
|
|
|
filename = os.path.basename(path)
|
2020-10-23 05:44:55 -05:00
|
|
|
if orig_path and len(orig_path) > 0:
|
|
|
|
dir_path = os.path.dirname(orig_path)
|
|
|
|
else:
|
|
|
|
dir_path = os.path.dirname(path)
|
|
|
|
|
2022-08-05 08:28:03 -05:00
|
|
|
response = send_from_directory(dir_path, filename,
|
2022-01-25 08:47:50 -06:00
|
|
|
mimetype='application/octet-stream',
|
|
|
|
as_attachment=True)
|
2022-08-05 08:28:03 -05:00
|
|
|
response.headers["filename"] = filename
|
2020-10-23 05:44:55 -05:00
|
|
|
|
|
|
|
return response
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2022-09-09 08:06:51 -05:00
|
|
|
def permission(self, path=None):
|
2020-07-28 05:50:26 -05:00
|
|
|
the_dir = self.dir if self.dir is not None else ''
|
2017-02-03 03:51:36 -06:00
|
|
|
res = {'Code': 1}
|
2022-07-19 04:57:47 -05:00
|
|
|
Filemanager.check_access_permission(the_dir, path)
|
2017-02-03 03:51:36 -06:00
|
|
|
return res
|
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
|
2017-07-18 09:13:16 -05:00
|
|
|
@blueprint.route(
|
|
|
|
"/filemanager/<int:trans_id>/",
|
2022-09-13 08:43:33 -05:00
|
|
|
methods=["POST"], endpoint='filemanager'
|
2017-07-18 09:13:16 -05:00
|
|
|
)
|
2024-04-29 03:11:02 -05:00
|
|
|
@pga_login_required
|
2016-05-12 13:34:28 -05:00
|
|
|
def file_manager(trans_id):
|
|
|
|
"""
|
|
|
|
It is the common function for every call which is made
|
|
|
|
and takes function name from post request and calls it.
|
|
|
|
It gets unique transaction id from post request and
|
|
|
|
rotate it into Filemanager class.
|
|
|
|
"""
|
|
|
|
mode = ''
|
|
|
|
kwargs = {}
|
|
|
|
if req.method == 'POST':
|
|
|
|
if req.files:
|
|
|
|
mode = 'add'
|
2023-03-06 05:33:47 -06:00
|
|
|
kwargs = {'req': req,
|
|
|
|
'storage_folder': req.form.get('storage_folder', None)}
|
2016-05-12 13:34:28 -05:00
|
|
|
else:
|
|
|
|
kwargs = json.loads(req.data)
|
|
|
|
kwargs['req'] = req
|
|
|
|
mode = kwargs['mode']
|
|
|
|
del kwargs['mode']
|
|
|
|
elif req.method == 'GET':
|
|
|
|
kwargs = {
|
2016-06-21 08:21:06 -05:00
|
|
|
'path': req.args['path'],
|
|
|
|
'name': req.args['name'] if 'name' in req.args else ''
|
2016-05-12 13:34:28 -05:00
|
|
|
}
|
|
|
|
mode = req.args['mode']
|
2023-03-06 05:33:47 -06:00
|
|
|
ss = kwargs['storage_folder'] if 'storage_folder' in kwargs else None
|
|
|
|
my_fm = Filemanager(trans_id, ss)
|
|
|
|
|
2023-03-29 03:19:32 -05:00
|
|
|
if ss and mode in ['upload', 'rename', 'delete', 'addfolder', 'add',
|
|
|
|
'permission']:
|
2023-06-12 08:14:31 -05:00
|
|
|
my_fm.check_access(ss)
|
2022-07-19 04:57:47 -05:00
|
|
|
func = getattr(my_fm, mode)
|
2016-05-12 13:34:28 -05:00
|
|
|
try:
|
2022-09-09 08:06:51 -05:00
|
|
|
if mode in ['getfolder', 'download']:
|
|
|
|
kwargs.pop('name', None)
|
|
|
|
|
2023-03-06 05:33:47 -06:00
|
|
|
if mode in ['add']:
|
|
|
|
kwargs.pop('storage_folder', None)
|
|
|
|
|
2022-09-09 08:06:51 -05:00
|
|
|
if mode in ['addfolder', 'getfolder', 'rename', 'delete',
|
2022-09-26 01:40:56 -05:00
|
|
|
'is_file_exist', 'req', 'permission', 'download']:
|
2022-09-09 08:06:51 -05:00
|
|
|
kwargs.pop('req', None)
|
2023-03-06 05:33:47 -06:00
|
|
|
kwargs.pop('storage_folder', None)
|
2022-09-09 08:06:51 -05:00
|
|
|
|
2016-05-12 13:34:28 -05:00
|
|
|
res = func(**kwargs)
|
2022-07-19 04:57:47 -05:00
|
|
|
except PermissionError as e:
|
|
|
|
return unauthorized(str(e))
|
|
|
|
|
2023-07-31 07:44:39 -05:00
|
|
|
if isinstance(res, Response):
|
2022-07-19 04:57:47 -05:00
|
|
|
return res
|
|
|
|
return make_json_response(data={'result': res, 'status': True})
|