3181
|
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 |