Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_misc_upload.py @ 4071:4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 11:49:51 +0200 |
parents | sat/plugins/plugin_misc_upload.py@524856bd7b19 |
children | 0d7bb4df2343 |
comparison
equal
deleted
inserted
replaced
4070:d10748475025 | 4071:4b842c1fb686 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 # SAT plugin for uploading files | |
4 # Copyright (C) 2009-2021 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 import os | |
20 import os.path | |
21 from pathlib import Path | |
22 from typing import Optional, Tuple, Union | |
23 | |
24 from twisted.internet import defer | |
25 from twisted.words.protocols.jabber import jid | |
26 from twisted.words.protocols.jabber import error as jabber_error | |
27 | |
28 from libervia.backend.core import exceptions | |
29 from libervia.backend.core.constants import Const as C | |
30 from libervia.backend.core.core_types import SatXMPPEntity | |
31 from libervia.backend.core.i18n import D_, _ | |
32 from libervia.backend.core.log import getLogger | |
33 from libervia.backend.tools import xml_tools | |
34 from libervia.backend.tools.common import data_format | |
35 | |
36 log = getLogger(__name__) | |
37 | |
38 | |
39 PLUGIN_INFO = { | |
40 C.PI_NAME: "File Upload", | |
41 C.PI_IMPORT_NAME: "UPLOAD", | |
42 C.PI_TYPE: C.PLUG_TYPE_MISC, | |
43 C.PI_MODES: C.PLUG_MODE_BOTH, | |
44 C.PI_MAIN: "UploadPlugin", | |
45 C.PI_HANDLER: "no", | |
46 C.PI_DESCRIPTION: _("""File upload management"""), | |
47 } | |
48 | |
49 | |
50 UPLOADING = D_("Please select a file to upload") | |
51 UPLOADING_TITLE = D_("File upload") | |
52 | |
53 | |
54 class UploadPlugin(object): | |
55 # TODO: plugin unload | |
56 | |
57 def __init__(self, host): | |
58 log.info(_("plugin Upload initialization")) | |
59 self.host = host | |
60 host.bridge.add_method( | |
61 "file_upload", | |
62 ".plugin", | |
63 in_sign="sssss", | |
64 out_sign="a{ss}", | |
65 method=self._file_upload, | |
66 async_=True, | |
67 ) | |
68 self._upload_callbacks = [] | |
69 | |
70 def _file_upload( | |
71 self, filepath, filename, upload_jid_s="", options='', profile=C.PROF_KEY_NONE | |
72 ): | |
73 client = self.host.get_client(profile) | |
74 upload_jid = jid.JID(upload_jid_s) if upload_jid_s else None | |
75 options = data_format.deserialise(options) | |
76 | |
77 return defer.ensureDeferred(self.file_upload( | |
78 client, filepath, filename or None, upload_jid, options | |
79 )) | |
80 | |
81 async def file_upload(self, client, filepath, filename, upload_jid, options): | |
82 """Send a file using best available method | |
83 | |
84 parameters are the same as for [upload] | |
85 @return (dict): action dictionary, with progress id in case of success, else xmlui | |
86 message | |
87 """ | |
88 try: | |
89 progress_id, __ = await self.upload( | |
90 client, filepath, filename, upload_jid, options) | |
91 except Exception as e: | |
92 if (isinstance(e, jabber_error.StanzaError) | |
93 and e.condition == 'not-acceptable'): | |
94 reason = e.text | |
95 else: | |
96 reason = str(e) | |
97 msg = D_("Can't upload file: {reason}").format(reason=reason) | |
98 log.warning(msg) | |
99 return { | |
100 "xmlui": xml_tools.note( | |
101 msg, D_("Can't upload file"), C.XMLUI_DATA_LVL_WARNING | |
102 ).toXml() | |
103 } | |
104 else: | |
105 return {"progress": progress_id} | |
106 | |
107 async def upload( | |
108 self, | |
109 client: SatXMPPEntity, | |
110 filepath: Union[Path, str], | |
111 filename: Optional[str] = None, | |
112 upload_jid: Optional[jid.JID] = None, | |
113 extra: Optional[dict]=None | |
114 ) -> Tuple[str, defer.Deferred]: | |
115 """Send a file using best available method | |
116 | |
117 @param filepath: absolute path to the file | |
118 @param filename: name to use for the upload | |
119 None to use basename of the path | |
120 @param upload_jid: upload capable entity jid, | |
121 or None to use autodetected, if possible | |
122 @param extra: extra data/options to use for the upload, may be: | |
123 - ignore_tls_errors(bool): True to ignore SSL/TLS certificate verification | |
124 used only if HTTPS transport is needed | |
125 - progress_id(str): id to use for progression | |
126 if not specified, one will be generated | |
127 @param profile: %(doc_profile)s | |
128 @return: progress_id and a Deferred which fire download URL when upload is | |
129 finished | |
130 """ | |
131 if extra is None: | |
132 extra = {} | |
133 if not os.path.isfile(filepath): | |
134 raise exceptions.DataError("The given path doesn't link to a file") | |
135 for method_name, available_cb, upload_cb, priority in self._upload_callbacks: | |
136 if upload_jid is None: | |
137 try: | |
138 upload_jid = await available_cb(client, upload_jid) | |
139 except exceptions.NotFound: | |
140 continue # no entity managing this extension found | |
141 | |
142 log.info( | |
143 "{name} method will be used to upload the file".format(name=method_name) | |
144 ) | |
145 progress_id, download_d = await upload_cb( | |
146 client, filepath, filename, upload_jid, extra | |
147 ) | |
148 return progress_id, download_d | |
149 | |
150 raise exceptions.NotFound("Can't find any method to upload a file") | |
151 | |
152 def register(self, method_name, available_cb, upload_cb, priority=0): | |
153 """Register a fileUploading method | |
154 | |
155 @param method_name(unicode): short name for the method, must be unique | |
156 @param available_cb(callable): method to call to check if this method is usable | |
157 the callback must take two arguments: upload_jid (can be None) and profile | |
158 the callback must return the first entity found (being upload_jid or one of its | |
159 components) | |
160 exceptions.NotFound must be raised if no entity has been found | |
161 @param upload_cb(callable): method to upload a file | |
162 must have the same signature as [file_upload] | |
163 must return a tuple with progress_id and a Deferred which fire download URL | |
164 when upload is finished | |
165 @param priority(int): pririoty of this method, the higher available will be used | |
166 """ | |
167 assert method_name | |
168 for data in self._upload_callbacks: | |
169 if method_name == data[0]: | |
170 raise exceptions.ConflictError( | |
171 "A method with this name is already registered" | |
172 ) | |
173 self._upload_callbacks.append((method_name, available_cb, upload_cb, priority)) | |
174 self._upload_callbacks.sort(key=lambda data: data[3], reverse=True) | |
175 | |
176 def unregister(self, method_name): | |
177 for idx, data in enumerate(self._upload_callbacks): | |
178 if data[0] == method_name: | |
179 del [idx] | |
180 return | |
181 raise exceptions.NotFound("The name to unregister doesn't exist") |