Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0234.py @ 3403:404d4b29de52
plugin file, XEP-0234: registering is now done by class + use of async:
- instead of registering a callback, a file sending manager now register itself and must
implement some well known method (`fileSend`, `canHandleFileSend`) and optionally a
`name` attribute
- `utils.asDeferred` is now used for callbacks, so all type of methods including
coroutines can be used.
- feature checking is now handled by `canHandleFileSend` method instead of simple
namespace check, this allows to use a method when namespace can't be checked (this is
the case when a file is sent to a bare jid with jingle)
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 12 Nov 2020 14:53:15 +0100 |
parents | 3dc8835d96cc |
children | 26a0af6e32c1 |
comparison
equal
deleted
inserted
replaced
3402:08a3e34aead1 | 3403:404d4b29de52 |
---|---|
14 # GNU Affero General Public License for more details. | 14 # GNU Affero General Public License for more details. |
15 | 15 |
16 # You should have received a copy of the GNU Affero General Public License | 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/>. | 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | 18 |
19 from sat.core.i18n import _ | 19 import os.path |
20 from sat.core.constants import Const as C | 20 import mimetypes |
21 from sat.core.log import getLogger | 21 from collections import namedtuple |
22 | |
23 log = getLogger(__name__) | |
24 from sat.core import exceptions | |
25 from wokkel import disco, iwokkel | |
26 from zope.interface import implementer | 22 from zope.interface import implementer |
27 from sat.tools import utils | |
28 from sat.tools import stream | |
29 from sat.tools.common import date_utils | |
30 import os.path | |
31 from twisted.words.xish import domish | 23 from twisted.words.xish import domish |
32 from twisted.words.protocols.jabber import jid | 24 from twisted.words.protocols.jabber import jid |
33 from twisted.python import failure | 25 from twisted.python import failure |
34 from twisted.words.protocols.jabber.xmlstream import XMPPHandler | 26 from twisted.words.protocols.jabber.xmlstream import XMPPHandler |
35 from twisted.internet import defer | 27 from twisted.internet import defer |
36 from twisted.internet import reactor | 28 from twisted.internet import reactor |
37 from twisted.internet import error as internet_error | 29 from twisted.internet import error as internet_error |
38 from collections import namedtuple | 30 from wokkel import disco, iwokkel |
31 from sat.core.i18n import _, D_ | |
32 from sat.core.constants import Const as C | |
33 from sat.core.log import getLogger | |
34 from sat.core import exceptions | |
35 from sat.tools import utils | |
36 from sat.tools import stream | |
37 from sat.tools.common import date_utils | |
39 from sat.tools.common import regex | 38 from sat.tools.common import regex |
40 import mimetypes | 39 |
41 | 40 |
41 log = getLogger(__name__) | |
42 | 42 |
43 NS_JINGLE_FT = "urn:xmpp:jingle:apps:file-transfer:5" | 43 NS_JINGLE_FT = "urn:xmpp:jingle:apps:file-transfer:5" |
44 | 44 |
45 PLUGIN_INFO = { | 45 PLUGIN_INFO = { |
46 C.PI_NAME: "Jingle File Transfer", | 46 C.PI_NAME: "Jingle File Transfer", |
56 | 56 |
57 EXTRA_ALLOWED = {"path", "namespace", "file_desc", "file_hash", "hash_algo"} | 57 EXTRA_ALLOWED = {"path", "namespace", "file_desc", "file_hash", "hash_algo"} |
58 Range = namedtuple("Range", ("offset", "length")) | 58 Range = namedtuple("Range", ("offset", "length")) |
59 | 59 |
60 | 60 |
61 class XEP_0234(object): | 61 class XEP_0234: |
62 # TODO: assure everything is closed when file is sent or session terminate is received | 62 # TODO: assure everything is closed when file is sent or session terminate is received |
63 # TODO: call self._f.unregister when unloading order will be managing (i.e. when dependencies will be unloaded at the end) | 63 # TODO: call self._f.unregister when unloading order will be managing (i.e. when |
64 # dependencies will be unloaded at the end) | |
64 Range = Range # we copy the class here, so it can be used by other plugins | 65 Range = Range # we copy the class here, so it can be used by other plugins |
66 name = PLUGIN_INFO[C.PI_NAME] | |
67 human_name = D_("file transfer") | |
65 | 68 |
66 def __init__(self, host): | 69 def __init__(self, host): |
67 log.info(_("plugin Jingle File Transfer initialization")) | 70 log.info(_("plugin Jingle File Transfer initialization")) |
68 self.host = host | 71 self.host = host |
69 host.registerNamespace("jingle-ft", NS_JINGLE_FT) | 72 host.registerNamespace("jingle-ft", NS_JINGLE_FT) |
70 self._j = host.plugins["XEP-0166"] # shortcut to access jingle | 73 self._j = host.plugins["XEP-0166"] # shortcut to access jingle |
71 self._j.registerApplication(NS_JINGLE_FT, self) | 74 self._j.registerApplication(NS_JINGLE_FT, self) |
72 self._f = host.plugins["FILE"] | 75 self._f = host.plugins["FILE"] |
73 self._f.register( | 76 self._f.register(self, priority=10000) |
74 NS_JINGLE_FT, self.fileJingleSend, priority=10000, method_name="Jingle" | |
75 ) | |
76 self._hash = self.host.plugins["XEP-0300"] | 77 self._hash = self.host.plugins["XEP-0300"] |
77 host.bridge.addMethod( | 78 host.bridge.addMethod( |
78 "fileJingleSend", | 79 "fileJingleSend", |
79 ".plugin", | 80 ".plugin", |
80 in_sign="ssssa{ss}s", | 81 in_sign="ssssa{ss}s", |
81 out_sign="", | 82 out_sign="", |
82 method=self._fileJingleSend, | 83 method=self._fileSend, |
83 async_=True, | 84 async_=True, |
84 ) | 85 ) |
85 host.bridge.addMethod( | 86 host.bridge.addMethod( |
86 "fileJingleRequest", | 87 "fileJingleRequest", |
87 ".plugin", | 88 ".plugin", |
100 @param session(dict): jingle session | 101 @param session(dict): jingle session |
101 @param content_name(unicode): name of the content | 102 @param content_name(unicode): name of the content |
102 @return (unicode): unique progress id | 103 @return (unicode): unique progress id |
103 """ | 104 """ |
104 return "{}_{}".format(session["id"], content_name) | 105 return "{}_{}".format(session["id"], content_name) |
106 | |
107 async def canHandleFileSend(self, client, peer_jid, filepath): | |
108 if peer_jid.resource: | |
109 return await self.host.hasFeature(client, NS_JINGLE_FT, peer_jid) | |
110 else: | |
111 # if we have a bare jid, Jingle Message Initiation will be tried | |
112 return True | |
105 | 113 |
106 # generic methods | 114 # generic methods |
107 | 115 |
108 def buildFileElement( | 116 def buildFileElement( |
109 self, client, name=None, file_hash=None, hash_algo=None, size=None, | 117 self, client, name=None, file_hash=None, hash_algo=None, size=None, |
284 | 292 |
285 return file_data | 293 return file_data |
286 | 294 |
287 # bridge methods | 295 # bridge methods |
288 | 296 |
289 def _fileJingleSend( | 297 def _fileSend( |
290 self, | 298 self, |
291 peer_jid, | 299 peer_jid, |
292 filepath, | 300 filepath, |
293 name="", | 301 name="", |
294 file_desc="", | 302 file_desc="", |
295 extra=None, | 303 extra=None, |
296 profile=C.PROF_KEY_NONE, | 304 profile=C.PROF_KEY_NONE, |
297 ): | 305 ): |
298 client = self.host.getClient(profile) | 306 client = self.host.getClient(profile) |
299 return self.fileJingleSend( | 307 return defer.ensureDeferred(self.fileSend( |
300 client, | 308 client, |
301 jid.JID(peer_jid), | 309 jid.JID(peer_jid), |
302 filepath, | 310 filepath, |
303 name or None, | 311 name or None, |
304 file_desc or None, | 312 file_desc or None, |
305 extra or None, | 313 extra or None, |
306 ) | 314 )) |
307 | 315 |
308 @defer.inlineCallbacks | 316 async def fileSend( |
309 def fileJingleSend( | |
310 self, client, peer_jid, filepath, name, file_desc=None, extra=None | 317 self, client, peer_jid, filepath, name, file_desc=None, extra=None |
311 ): | 318 ): |
312 """Send a file using jingle file transfer | 319 """Send a file using jingle file transfer |
313 | 320 |
314 @param peer_jid(jid.JID): destinee jid | 321 @param peer_jid(jid.JID): destinee jid |
320 progress_id_d = defer.Deferred() | 327 progress_id_d = defer.Deferred() |
321 if extra is None: | 328 if extra is None: |
322 extra = {} | 329 extra = {} |
323 if file_desc is not None: | 330 if file_desc is not None: |
324 extra["file_desc"] = file_desc | 331 extra["file_desc"] = file_desc |
325 yield self._j.initiate( | 332 await self._j.initiate( |
326 client, | 333 client, |
327 peer_jid, | 334 peer_jid, |
328 [ | 335 [ |
329 { | 336 { |
330 "app_ns": NS_JINGLE_FT, | 337 "app_ns": NS_JINGLE_FT, |
336 "progress_id_d": progress_id_d, | 343 "progress_id_d": progress_id_d, |
337 }, | 344 }, |
338 } | 345 } |
339 ], | 346 ], |
340 ) | 347 ) |
341 progress_id = yield progress_id_d | 348 return await progress_id_d |
342 defer.returnValue(progress_id) | |
343 | 349 |
344 def _fileJingleRequest( | 350 def _fileJingleRequest( |
345 self, peer_jid, filepath, name="", file_hash="", hash_algo="", extra=None, | 351 self, peer_jid, filepath, name="", file_hash="", hash_algo="", extra=None, |
346 profile=C.PROF_KEY_NONE): | 352 profile=C.PROF_KEY_NONE): |
347 client = self.host.getClient(profile) | 353 client = self.host.getClient(profile) |