plugin XEP-0447: handle attachment and download:

- plugin XEP-0447 can now be used in message attachments and to retrieve an attachment
- plugin attach: `attachment` being processed is added to `extra` so the handler can inspect it
- plugin attach: `size` is added to attachment
- plugin download: a whole attachment dict is now used in `download` and
  `file_download`/`file_download_complete`. `download_uri` can be used as a shortcut when
  just a URI is used. In addition to URI scheme handler, whole attachment handlers can now
  be registered with `register_download_handler`
- plugin XEP-0363: `file_http_upload` `XEP-0363_upload_size` triggers have been renamed to
  `XEP-0363_upload_pre_slot` and is now using a dict with arguments, allowing for the size
  but also the filename to be modified, which is necessary for encryption (filename may
  be hidden from URL this way).
- plugin XEP-0446: fix wrong element name
- plugin XEP-0447: source handler can now be registered (`url-data` is registered by
  default)
- plugin XEP-0447: source parsing has been put in a separated `parse_sources_elt` method,
  as it may be useful to do it independently (notably with XEP-0448)
- plugin XEP-0447: parse received message and complete attachments when suitable
- plugin XEP-0447: can now be used with message attachments
- plugin XEP-0447: can now be used with attachments download
- renamed `options` arguments to `extra` for consistency
- some style change (progressive move from legacy camelCase to PEP8 snake_case)
- some typing

rel 379
This commit is contained in:
Goffi 2022-10-06 16:02:05 +02:00
commit a8fc70fca6

View file

@ -63,13 +63,13 @@ class AESGCM(object):
log.info(_("AESGCM plugin initialization"))
self._http_upload = host.plugins['XEP-0363']
self._attach = host.plugins["ATTACH"]
host.plugins["DOWNLOAD"].registerScheme(
host.plugins["DOWNLOAD"].register_scheme(
"aesgcm", self.download
)
self._attach.register(
self.canHandleAttachment, self.attach, encrypted=True)
host.trigger.add("XEP-0363_upload_size", self._uploadSizeTrigger)
host.trigger.add("XEP-0363_upload", self._uploadTrigger)
host.trigger.add("XEP-0363_upload_pre_slot", self._upload_pre_slot)
host.trigger.add("XEP-0363_upload", self._upload_trigger)
host.trigger.add("messageReceived", self._messageReceivedTrigger)
async def download(self, client, uri_parsed, dest_path, options):
@ -129,7 +129,7 @@ class AESGCM(object):
decryptor=decryptor))
else:
d = defer.Deferred()
self.host.plugins["DOWNLOAD"].errbackDownload(file_obj, d, resp)
self.host.plugins["DOWNLOAD"].errback_download(file_obj, d, resp)
return progress_id, d
async def canHandleAttachment(self, client, data):
@ -140,13 +140,13 @@ class AESGCM(object):
else:
return True
async def _uploadCb(self, client, filepath, filename, options):
options['encryption'] = C.ENC_AES_GCM
return await self._http_upload.fileHTTPUpload(
async def _upload_cb(self, client, filepath, filename, extra):
extra['encryption'] = C.ENC_AES_GCM
return await self._http_upload.file_http_upload(
client=client,
filepath=filepath,
filename=filename,
options=options
extra=extra
)
async def attach(self, client, data):
@ -160,14 +160,16 @@ class AESGCM(object):
if not data['message'] or data['message'] == {'': ''}:
extra_attachments = attachments[1:]
del attachments[1:]
await self._attach.uploadFiles(client, data, upload_cb=self._uploadCb)
await self._attach.upload_files(client, data, upload_cb=self._upload_cb)
else:
# we have a message, we must send first attachment separately
extra_attachments = attachments[:]
attachments.clear()
del data["extra"][C.MESS_KEY_ATTACHMENTS]
body_elt = next(data["xml"].elements(C.NS_CLIENT, "body"))
body_elt = data["xml"].body
if body_elt is None:
body_elt = data["xml"].addElement("body")
for attachment in attachments:
body_elt.addContent(attachment["url"])
@ -219,11 +221,11 @@ class AESGCM(object):
decrypted = decryptor.update(data)
file_obj.write(decrypted)
def _uploadSizeTrigger(self, client, options, file_path, size, size_adjust):
if options.get('encryption') != C.ENC_AES_GCM:
def _upload_pre_slot(self, client, extra, file_metadata):
if extra.get('encryption') != C.ENC_AES_GCM:
return True
# the tag is appended to the file
size_adjust.append(16)
file_metadata["size"] += 16
return True
def _encrypt(self, data, encryptor):
@ -239,8 +241,8 @@ class AESGCM(object):
# as we have already finalized, we can now send EOF
return b''
def _uploadTrigger(self, client, options, sat_file, file_producer, slot):
if options.get('encryption') != C.ENC_AES_GCM:
def _upload_trigger(self, client, extra, sat_file, file_producer, slot):
if extra.get('encryption') != C.ENC_AES_GCM:
return True
log.debug("encrypting file with AES-GCM")
iv = secrets.token_bytes(12)
@ -255,7 +257,7 @@ class AESGCM(object):
# so we need to check with final data length to avoid a warning on close()
sat_file.check_size_with_read = True
# file_producer get length directly from file, and this cause trouble has
# file_producer get length directly from file, and this cause trouble as
# we have to change the size because of encryption. So we adapt it here,
# else the producer would stop reading prematurely
file_producer.length = sat_file.size