Reformatted code base using black.

This commit is contained in:
Goffi 2024-06-19 18:44:57 +02:00
commit 53fdaa9d09

View file

@ -82,10 +82,7 @@ OVER_QUOTA_TXT = D_(
"{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 @@ class HTTPFileServer(resource.Resource):
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 @@ class HTTPFileServer(resource.Resource):
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 @@ class HTTPFileServer(resource.Resource):
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 @@ class HTTPFileServer(resource.Resource):
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 @@ class HTTPFileServer(resource.Resource):
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 @@ class HTTPFileServer(resource.Resource):
"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 @@ class FileSharingRequest(server.Request):
# 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 @@ class FileSharingRequest(server.Request):
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 @@ class FileSharingRequest(server.Request):
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 @@ class FileSharingRequest(server.Request):
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 @@ class FileSharingSite(server.Site):
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 @@ class FileSharing:
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(
"XEP-0234_fileSendingRequest", self, self._file_sending_request_trigger, priority=1000
"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,
)
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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class FileSharing:
):
"""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 @@ class FileSharing:
# 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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class FileSharing:
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 @@ class Comments_handler(pubsub.PubSubService):
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: