Mercurial > libervia-backend
diff libervia/backend/plugins/plugin_comp_file_sharing.py @ 4270:0d7bb4df2343
Reformatted code base using black.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 19 Jun 2024 18:44:57 +0200 |
parents | 6784d07b99c8 |
children |
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_comp_file_sharing.py Tue Jun 18 12:06:45 2024 +0200 +++ b/libervia/backend/plugins/plugin_comp_file_sharing.py Wed Jun 19 18:44:57 2024 +0200 @@ -82,10 +82,7 @@ "{used_space}, you can't upload {file_size} more." ) -HTTP_VERSION = unicodedata.normalize( - 'NFKD', - f"{C.APP_NAME} file sharing {C.APP_VERSION}" -) +HTTP_VERSION = unicodedata.normalize("NFKD", f"{C.APP_NAME} file sharing {C.APP_VERSION}") class HTTPFileServer(resource.Resource): @@ -94,32 +91,32 @@ def errorPage(self, request, code): request.setResponseCode(code) if code == http.BAD_REQUEST: - brief = 'Bad Request' + brief = "Bad Request" details = "Your request is invalid" elif code == http.FORBIDDEN: - brief = 'Forbidden' + brief = "Forbidden" details = "You're not allowed to use this resource" elif code == http.NOT_FOUND: - brief = 'Not Found' + brief = "Not Found" details = "No resource found at this URL" else: - brief = 'Error' + brief = "Error" details = "This resource can't be used" log.error(f"Unexpected return code used: {code}") log.warning( - f'Error returned while trying to access url {request.uri.decode()}: ' + f"Error returned while trying to access url {request.uri.decode()}: " f'"{brief}" ({code}): {details}' ) return resource.ErrorPage(code, brief, details).render(request) def get_disposition_type(self, media_type, media_subtype): - if media_type in ('image', 'video'): - return 'inline' - elif media_type == 'application' and media_subtype == 'pdf': - return 'inline' + if media_type in ("image", "video"): + return "inline" + elif media_type == "application" and media_subtype == "pdf": + return "inline" else: - return 'attachment' + return "attachment" def render(self, request): request.setHeader("server", HTTP_VERSION) @@ -127,13 +124,14 @@ request.setHeader("Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, PUT") request.setHeader( "Access-Control-Allow-Headers", - "Content-Type, Range, Xmpp-File-Path, Xmpp-File-No-Http") + "Content-Type, Range, Xmpp-File-Path, Xmpp-File-No-Http", + ) request.setHeader("Access-Control-Allow-Credentials", "true") request.setHeader("Accept-Ranges", "bytes") request.setHeader( - "Access-Control-Expose-Headers", - "Date, Content-Length, Content-Range") + "Access-Control-Expose-Headers", "Date, Content-Length, Content-Range" + ) return super().render(request) def render_OPTIONS(self, request): @@ -157,7 +155,8 @@ request.finish() return found_files = await request.file_sharing.host.memory.get_files( - client=None, peer_jid=None, perms_to_check=None, public_id=upload_id) + client=None, peer_jid=None, perms_to_check=None, public_id=upload_id + ) if not found_files: request.write(self.errorPage(request, http.NOT_FOUND)) request.finish() @@ -166,22 +165,22 @@ log.error(f"more that one files found for public id {upload_id!r}") found_file = found_files[0] - file_path = request.file_sharing.files_path/found_file['file_hash'] + file_path = request.file_sharing.files_path / found_file["file_hash"] file_res = static.File(file_path) file_res.type = f'{found_file["media_type"]}/{found_file["media_subtype"]}' - file_res.encoding = file_res.contentEncodings.get(Path(found_file['name']).suffix) + file_res.encoding = file_res.contentEncodings.get(Path(found_file["name"]).suffix) disp_type = self.get_disposition_type( - found_file['media_type'], found_file['media_subtype']) + found_file["media_type"], found_file["media_subtype"] + ) # the URL is percent encoded, and not all browsers/tools unquote the file name, # thus we add a content disposition header request.setHeader( - 'Content-Disposition', - f"{disp_type}; filename*=UTF-8''{quote(found_file['name'])}" + "Content-Disposition", + f"{disp_type}; filename*=UTF-8''{quote(found_file['name'])}", ) # cf. https://xmpp.org/extensions/xep-0363.html#server request.setHeader( - 'Content-Security-Policy', - "default-src 'none'; frame-ancestors 'none';" + "Content-Security-Policy", "default-src 'none'; frame-ancestors 'none';" ) ret = file_res.render(request) if ret != server.NOT_DONE_YET: @@ -215,7 +214,7 @@ if path: path = unquote(path) else: - path = "/uploads" + path = "/uploads" if request.getHeader("Xmpp-File-No-Http") is not None: public_id = None else: @@ -225,11 +224,14 @@ "name": unquote(upload_request.filename), "mime_type": upload_request.content_type, "size": upload_request.size, - "path": path + "path": path, } await request.file_sharing.register_received_file( - client, upload_request.from_, file_data, tmp_file_path, + client, + upload_request.from_, + file_data, + tmp_file_path, public_id=public_id, ) @@ -256,11 +258,13 @@ # we normalise the path path = urlparse(path.decode()).path try: - __, upload_id, filename = path.split('/') + __, upload_id, filename = path.split("/") except ValueError: raise exceptions.DataError("no enought path elements") if len(upload_id) < 10: - raise exceptions.DataError(f"invalid upload ID received for a PUT: {upload_id!r}") + raise exceptions.DataError( + f"invalid upload ID received for a PUT: {upload_id!r}" + ) self._upload_data = (upload_id, filename) return self._upload_data @@ -276,11 +280,11 @@ def refuse_request(self): if self.content is not None: self.content.close() - self.content = open(os.devnull, 'w+b') + self.content = open(os.devnull, "w+b") self.channel._respondToBadRequestAndDisconnect() def gotLength(self, length): - if self.channel._command.decode().upper() == 'PUT': + if self.channel._command.decode().upper() == "PUT": # for PUT we check early if upload_id is fine, to avoid buffering a file we'll refuse # we buffer the fileĀ in component's TMP_BUFFER_DIR, so we just have to rename it at the end try: @@ -289,9 +293,13 @@ log.warning(f"Invalid PUT request, we stop here: {e}") return self.refuse_request() try: - client, upload_request, timer = self.file_sharing.expected_uploads.pop(upload_id) + client, upload_request, timer = self.file_sharing.expected_uploads.pop( + upload_id + ) except KeyError: - log.warning(f"unknown (expired?) upload ID received for a PUT: {upload_id!r}") + log.warning( + f"unknown (expired?) upload ID received for a PUT: {upload_id!r}" + ) return self.refuse_request() if not timer.active: @@ -309,10 +317,9 @@ self.upload_request_data = (client, upload_request) - file_tmp_path = files_utils.get_unique_name( - self.file_tmp_dir/upload_id) + file_tmp_path = files_utils.get_unique_name(self.file_tmp_dir / upload_id) - self.content = open(file_tmp_path, 'w+b') + self.content = open(file_tmp_path, "w+b") else: return super().gotLength(length) @@ -331,8 +338,8 @@ super().__init__(HTTPFileServer()) def getContentFile(self, length): - file_tmp_path = self.file_tmp_dir/shortuuid.uuid() - return open(file_tmp_path, 'w+b') + file_tmp_path = self.file_tmp_dir / shortuuid.uuid() + return open(file_tmp_path, "w+b") class FileSharing: @@ -356,37 +363,53 @@ self._t = self.host.plugins["XEP-0264"] self._hu = self.host.plugins["XEP-0363"] self._hu.register_handler(self._on_http_upload) - self.host.trigger.add_with_check("FILE_getDestDir", self, self._get_dest_dir_trigger) + self.host.trigger.add_with_check( + "FILE_getDestDir", self, self._get_dest_dir_trigger + ) self.host.trigger.add_with_check( - "XEP-0234_fileSendingRequest", self, self._file_sending_request_trigger, priority=1000 + "XEP-0234_fileSendingRequest", + self, + self._file_sending_request_trigger, + priority=1000, ) - self.host.trigger.add_with_check("XEP-0234_buildFileElement", self, self._add_file_metadata_elts) - self.host.trigger.add_with_check("XEP-0234_parseFileElement", self, self._get_file_metadata_elts) - self.host.trigger.add_with_check("XEP-0329_compGetFilesFromNode", self, self._add_file_metadata) + self.host.trigger.add_with_check( + "XEP-0234_buildFileElement", self, self._add_file_metadata_elts + ) + self.host.trigger.add_with_check( + "XEP-0234_parseFileElement", self, self._get_file_metadata_elts + ) + self.host.trigger.add_with_check( + "XEP-0329_compGetFilesFromNode", self, self._add_file_metadata + ) self.host.trigger.add_with_check( "XEP-0329_compGetFilesFromNode_build_directory", self, - self._add_directory_metadata_elts) + self._add_directory_metadata_elts, + ) self.host.trigger.add_with_check( - "XEP-0329_parseResult_directory", - self, - self._get_directory_metadata_elts) + "XEP-0329_parseResult_directory", self, self._get_directory_metadata_elts + ) self.files_path = self.host.get_local_path(None, C.FILES_DIR) - self.http_port = int(self.host.memory.config_get( - 'component file-sharing', 'http_upload_port', 8888)) + self.http_port = int( + self.host.memory.config_get( + "component file-sharing", "http_upload_port", 8888 + ) + ) connection_type = self.host.memory.config_get( - 'component file-sharing', 'http_upload_connection_type', 'https') - if connection_type not in ('http', 'https'): + "component file-sharing", "http_upload_connection_type", "https" + ) + if connection_type not in ("http", "https"): raise exceptions.ConfigError( 'bad http_upload_connection_type, you must use one of "http" or "https"' ) self.server = FileSharingSite(self) self.expected_uploads = {} - if connection_type == 'http': + if connection_type == "http": reactor.listenTCP(self.http_port, self.server) else: options = tls.get_options_from_config( - self.host.memory.config, "component file-sharing") + self.host.memory.config, "component file-sharing" + ) tls.tls_options_check(options) context_factory = tls.get_tls_context_factory(options) reactor.listenSSL(self.http_port, self.server, context_factory) @@ -400,7 +423,8 @@ self.init() public_base_url = self.host.memory.config_get( - 'component file-sharing', 'http_upload_public_facing_url') + "component file-sharing", "http_upload_public_facing_url" + ) if public_base_url is None: client._file_sharing_base_url = f"https://{client.host}:{self.http_port}" else: @@ -441,7 +465,8 @@ thumbnails.append({"id": thumb_id, "size": thumb_size}) async def register_received_file( - self, client, peer_jid, file_data, file_path, public_id=None, extra=None): + self, client, peer_jid, file_data, file_path, public_id=None, extra=None + ): """Post file reception tasks once file is received, this method create hash/thumbnails if necessary @@ -463,9 +488,9 @@ file_hash = file_data["hash_hasher"].hexdigest() else: hasher = self._h.get_hasher(HASH_ALGO) - with file_path.open('rb') as f: + with file_path.open("rb") as f: file_hash = await self._h.calculate_hash(f, hasher) - final_path = self.files_path/file_hash + final_path = self.files_path / file_hash if final_path.is_file(): log.debug( @@ -490,8 +515,11 @@ try: await video.get_thumbnail(final_path, thumb_path) except Exception as e: - log.warning(_("Can't get thumbnail for {final_path}: {e}").format( - final_path=final_path, e=e)) + log.warning( + _("Can't get thumbnail for {final_path}: {e}").format( + final_path=final_path, e=e + ) + ) else: await self.generate_thumbnails(extra, thumb_path) @@ -534,14 +562,15 @@ text=OVER_QUOTA_TXT.format( quota=utils.get_human_size(quota), used_space=utils.get_human_size(used_space), - file_size=utils.get_human_size(file_data['size']) - ) + file_size=utils.get_human_size(file_data["size"]), + ), ) file_tmp_dir = self.host.get_local_path( None, C.FILES_TMP_DIR, peer_jid.userhost(), component=True ) - file_tmp_path = file_data['file_path'] = files_utils.get_unique_name( - file_tmp_dir/filename) + file_tmp_path = file_data["file_path"] = files_utils.get_unique_name( + file_tmp_dir / filename + ) transfer_data["finished_d"].addCallback( lambda __: defer.ensureDeferred( @@ -559,8 +588,8 @@ ): """This method retrieve a file on request, and send if after checking permissions""" peer_jid = session["peer_jid"] - if session['local_jid'].user: - owner = client.get_owner_from_jid(session['local_jid']) + if session["local_jid"].user: + owner = client.get_owner_from_jid(session["local_jid"]) else: owner = peer_jid try: @@ -592,9 +621,10 @@ # we only use the first found file found_file = found_files[0] - if found_file['type'] != C.FILE_TYPE_FILE: - raise TypeError("a file was expected, type is {type_}".format( - type_=found_file['type'])) + if found_file["type"] != C.FILE_TYPE_FILE: + raise TypeError( + "a file was expected, type is {type_}".format(type_=found_file["type"]) + ) file_hash = found_file["file_hash"] file_path = self.files_path / file_hash file_data["hash_hasher"] = hasher = self._h.get_hasher(found_file["hash_algo"]) @@ -624,9 +654,11 @@ else: return ( False, - defer.ensureDeferred(self._retrieve_files( - client, session, content_data, content_name, file_data, file_elt - )), + defer.ensureDeferred( + self._retrieve_files( + client, session, content_data, content_name, file_data, file_elt + ) + ), ) ## HTTP Upload ## @@ -639,7 +671,7 @@ async def _on_http_upload(self, client, request): # filename should be already cleaned, but it's better to double check - assert '/' not in request.filename + assert "/" not in request.filename # client._file_sharing_allowed_hosts is set in plugin XEP-0329 if request.from_.host not in client._file_sharing_allowed_hosts: raise error.StanzaError("forbidden") @@ -654,13 +686,15 @@ text=OVER_QUOTA_TXT.format( quota=utils.get_human_size(quota), used_space=utils.get_human_size(used_space), - file_size=utils.get_human_size(request.size) + file_size=utils.get_human_size(request.size), ), - appCondition = self._hu.get_file_too_large_elt(max(quota - used_space, 0)) + appCondition=self._hu.get_file_too_large_elt( + max(quota - used_space, 0) + ), ) upload_id = shortuuid.ShortUUID().random(length=30) - assert '/' not in upload_id + assert "/" not in upload_id timer = reactor.callLater(30, self._purge_slot, upload_id) self.expected_uploads[upload_id] = (client, request, timer) url = urljoin(client._file_sharing_base_url, f"{upload_id}/{request.filename}") @@ -675,7 +709,7 @@ def _add_file_metadata_elts(self, client, file_elt, extra_args): # affiliation - affiliation = extra_args.get('affiliation') + affiliation = extra_args.get("affiliation") if affiliation is not None: file_elt.addElement((NS_FS_AFFILIATION, "affiliation"), content=affiliation) @@ -715,7 +749,8 @@ return True def _add_file_metadata( - self, client, iq_elt, iq_result_elt, owner, node_path, files_data): + self, client, iq_elt, iq_result_elt, owner, node_path, files_data + ): for file_data in files_data: file_data["comments_url"] = uri.build_xmpp_uri( "pubsub", @@ -725,22 +760,21 @@ return True def _add_directory_metadata_elts( - self, client, file_data, directory_elt, owner, node_path): - affiliation = file_data.get('affiliation') + self, client, file_data, directory_elt, owner, node_path + ): + affiliation = file_data.get("affiliation") if affiliation is not None: directory_elt.addElement( - (NS_FS_AFFILIATION, "affiliation"), - content=affiliation + (NS_FS_AFFILIATION, "affiliation"), content=affiliation ) - def _get_directory_metadata_elts( - self, client, elt, file_data): + def _get_directory_metadata_elts(self, client, elt, file_data): try: affiliation_elt = next(elt.elements(NS_FS_AFFILIATION, "affiliation")) except StopIteration: pass else: - file_data['affiliation'] = str(affiliation_elt) + file_data["affiliation"] = str(affiliation_elt) class Comments_handler(pubsub.PubSubService): @@ -841,7 +875,9 @@ peer_jid = None else: peer_jid = requestor.userhost() - update_cb = partial(self.comments_update, new_comments=comments, peer_jid=peer_jid) + update_cb = partial( + self.comments_update, new_comments=comments, peer_jid=peer_jid + ) try: await self.host.memory.file_update(file_id, "extra", update_cb) except exceptions.PermissionError: