diff --git a/moonraker/app.py b/moonraker/app.py index 2d8949a..652e778 100644 --- a/moonraker/app.py +++ b/moonraker/app.py @@ -9,6 +9,7 @@ import mimetypes import logging import tornado from inspect import isclass +from tornado.escape import url_unescape from tornado.routing import Rule, PathMatches, AnyMatches from utils import ServerError from websockets import WebsocketManager, WebSocket @@ -186,8 +187,7 @@ class MoonrakerApp: self.wsm.register_local_handler(api_def, callback) logging.info(msg) - def register_static_file_handler(self, pattern, file_path, - can_delete=False): + def register_static_file_handler(self, pattern, file_path): if pattern[0] != "/": pattern = "/server/files/" + pattern if os.path.isfile(file_path): @@ -200,12 +200,7 @@ class MoonrakerApp: logging.info(f"Invalid file path: {file_path}") return logging.debug(f"Registering static file: ({pattern}) {file_path}") - methods = ['GET'] - if can_delete: - methods.append('DELETE') - params = { - 'server': self.server, 'auth': self.auth, - 'path': file_path, 'methods': methods} + params = {'server': self.server, 'auth': self.auth, 'path': file_path} self.mutable_router.add_handler(pattern, FileRequestHandler, params) def register_upload_handler(self, pattern): @@ -321,12 +316,6 @@ class LocalRequestHandler(AuthorizedRequestHandler): class FileRequestHandler(AuthorizedFileHandler): - def initialize(self, server, auth, path, methods, - default_filename=None): - super(FileRequestHandler, self).initialize( - server, auth, path, default_filename) - self.methods = methods - def set_extra_headers(self, path): # The call below shold never return an empty string, # as the path should have already been validated to be @@ -336,10 +325,8 @@ class FileRequestHandler(AuthorizedFileHandler): "Content-Disposition", f"attachment; filename={basename}") async def delete(self, path): - if 'DELETE' not in self.methods: - raise tornado.web.HTTPError(405) - path = self.request.path.lstrip("/").split("/", 2)[-1] + path = url_unescape(path) file_manager = self.server.lookup_plugin('file_manager') try: filename = await file_manager.delete_file(path) @@ -348,7 +335,7 @@ class FileRequestHandler(AuthorizedFileHandler): raise tornado.web.HTTPError( 403, "File is loaded, DELETE not permitted") else: - raise tornado.web.HTTPError(400, str(e)) + raise tornado.web.HTTPError(e.status_code, str(e)) self.finish({'result': filename}) class FileUploadHandler(AuthorizedRequestHandler): diff --git a/moonraker/moonraker.py b/moonraker/moonraker.py index c0c495e..06dc1eb 100644 --- a/moonraker/moonraker.py +++ b/moonraker/moonraker.py @@ -297,8 +297,7 @@ class Server: vsd_path = vsd_config.get('path', None) if vsd_path is not None: file_manager = self.lookup_plugin('file_manager') - file_manager.register_directory( - 'gcodes', vsd_path, can_delete=True) + file_manager.register_directory('gcodes', vsd_path) else: logging.info( "Configuration for [virtual_sdcard] not found," diff --git a/moonraker/plugins/file_manager.py b/moonraker/plugins/file_manager.py index 1796df6..bdff829 100644 --- a/moonraker/plugins/file_manager.py +++ b/moonraker/plugins/file_manager.py @@ -22,7 +22,6 @@ class FileManager: self.server = config.get_server() self.file_paths = {} self.file_lists = {} - self.mutable_paths = set() self.gcode_metadata = MetadataStorage(self.server) self.fixed_path_args = {} @@ -48,8 +47,7 @@ class FileManager: # Register Klippy Configuration Path config_path = config.get('config_path', None) if config_path is not None: - ret = self.register_directory( - 'config', config_path, can_delete=True) + ret = self.register_directory('config', config_path) if not ret: raise config.error( "Option 'config_path' is not a valid directory") @@ -73,7 +71,7 @@ class FileManager: log_path = os.path.normpath(os.path.expanduser(log_file)) self.server.register_static_file_handler("klippy.log", log_path) - def register_directory(self, base, path, can_delete=False): + def register_directory(self, base, path): if path is None: return False home = os.path.expanduser('~') @@ -87,10 +85,7 @@ class FileManager: return False if path != self.file_paths.get(base, ""): self.file_paths[base] = path - if can_delete: - self.mutable_paths.add(base) - self.server.register_static_file_handler( - base, path, can_delete=can_delete) + self.server.register_static_file_handler(base, path) try: self._update_file_list(base=base) except Exception: @@ -449,14 +444,15 @@ class FileManager: return await self.delete_file(file_path) async def delete_file(self, path): - parts = path.split("/", 1) + parts = path.lstrip("/").split("/", 1) + if len(parts) != 2: + raise self.server.error( + f"Path not available for DELETE: {path}", 405) root = parts[0] filename = parts[1] - if root not in self.file_paths or len(parts) != 2: - raise self.server.error(f"Invalid file path: {path}") - if root not in self.mutable_paths: + if root not in self.file_paths or root not in FULL_ACCESS_ROOTS: raise self.server.error( - f"Path not mutable, Cannot delete file: {path}") + f"Path not available for DELETE: {path}", 405) root_path = self.file_paths[root] full_path = os.path.join(root_path, filename) if not os.path.isfile(full_path):