comparison sat/plugins/plugin_misc_attach.py @ 3181:5ff2cf7f0aba

plugin attach: first draft: this plugin handles the uploading of files attached in message data.
author Goffi <goffi@goffi.org>
date Sun, 23 Feb 2020 17:48:26 +0100
parents
children f2bb57348587
comparison
equal deleted inserted replaced
3180:826bca181226 3181:5ff2cf7f0aba
1 #!/usr/bin/env python3
2
3 # SàT plugin for attaching files
4 # Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org)
5
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
15
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 from functools import partial
20 from pathlib import Path
21 from twisted.internet import defer
22 from sat.core.i18n import _
23 from sat.core.constants import Const as C
24 from sat.core.log import getLogger
25
26 log = getLogger(__name__)
27
28
29 PLUGIN_INFO = {
30 C.PI_NAME: "File Attach",
31 C.PI_IMPORT_NAME: "ATTACH",
32 C.PI_TYPE: C.PLUG_TYPE_MISC,
33 C.PI_DEPENDENCIES: ["UPLOAD"],
34 C.PI_MAIN: "AttachPlugin",
35 C.PI_HANDLER: "no",
36 C.PI_DESCRIPTION: _("""Attachments handler"""),
37 }
38
39
40 class AttachPlugin:
41
42 def __init__(self, host):
43 log.info(_("plugin Attach initialization"))
44 self.host = host
45 self._u = host.plugins["UPLOAD"]
46 host.trigger.add("sendMessage", self._sendMessageTrigger)
47
48 def _attachFiles(self, client, data):
49 # TODO: handle xhtml-im
50 body_elt = next(data["xml"].elements((C.NS_CLIENT, "body")))
51 for attachment in data["extra"][C.MESS_KEY_ATTACHMENTS]:
52 body_elt.addContent(f'\n{attachment["url"]}')
53 return data
54
55 async def uploadFiles(self, client, data):
56 uploads_d = []
57 to_delete = []
58 attachments = data["extra"]["attachments"]
59
60 for attachment in attachments:
61 try:
62 path = Path(attachment["path"])
63 except KeyError:
64 log.warning("no path in attachment: {attachment}")
65 to_delete.append(attachment)
66 continue
67
68 if "url" in attachment:
69 log.warning("invalid attachment: unknown URL")
70 to_delete.append(attachment)
71
72 try:
73 name = attachment["name"]
74 except KeyError:
75 name = attachment["name"] = path.name
76
77 options = {}
78 if client.encryption.isEncryptionRequested(data):
79 # FIXME: we should not use implementation specific value here
80 # but for not it's the only file encryption method available with
81 # with upload.
82 options['encryption'] = C.ENC_AES_GCM
83
84 __, upload_d = await self._u.upload(
85 client,
86 filepath=path,
87 filename=name,
88 )
89 uploads_d.append(upload_d)
90
91 for attachment in to_delete:
92 attachments.remove(attachment)
93
94 upload_results = await defer.DeferredList(uploads_d)
95 for idx, (success, ret) in enumerate(upload_results):
96 attachment = attachments[idx]
97
98 if not success:
99 # ret is a failure here
100 log.warning(f"error while uploading {attachment}: {ret}")
101 continue
102
103 attachment["url"] = ret
104
105 return data
106
107 def _uploadFiles(self, client, data):
108 return defer.ensureDeferred(self.uploadFiles(client, data))
109
110 def _sendMessageTrigger(
111 self, client, mess_data, pre_xml_treatments, post_xml_treatments):
112 if mess_data['extra'].get(C.MESS_KEY_ATTACHMENTS):
113 pre_xml_treatments.addCallback(partial(self._uploadFiles, client))
114 post_xml_treatments.addCallback(partial(self._attachFiles, client))
115 return True