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)