plugin XEP-0329: download thumbnails:
- thumbnails are now downloaded directly when BoB is used, `filename` is then filled - `utils.asDeferred` is now used for Jingle methods, so `async` coroutines can be used - (XEP-0231): fixed `dumpData`
This commit is contained in:
parent
8854f96aec
commit
88174c2100
4 changed files with 75 additions and 61 deletions
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
# SAT plugin for Jingle (XEP-0166)
|
||||
# SàT plugin for Jingle (XEP-0166)
|
||||
# Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -17,25 +16,27 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import uuid
|
||||
import time
|
||||
from collections import namedtuple
|
||||
from zope.interface import implementer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.internet import defer
|
||||
from twisted.internet import reactor
|
||||
from twisted.words.protocols.jabber import error
|
||||
from twisted.words.protocols.jabber import xmlstream
|
||||
from twisted.python import failure
|
||||
from wokkel import disco, iwokkel
|
||||
from sat.core import exceptions
|
||||
from sat.core.i18n import _, D_
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core.log import getLogger
|
||||
from sat.tools import xml_tools
|
||||
from sat.tools import utils
|
||||
|
||||
|
||||
log = getLogger(__name__)
|
||||
from sat.core import exceptions
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.internet import defer
|
||||
from twisted.internet import reactor
|
||||
from wokkel import disco, iwokkel
|
||||
from twisted.words.protocols.jabber import error
|
||||
from twisted.words.protocols.jabber import xmlstream
|
||||
from twisted.python import failure
|
||||
from collections import namedtuple
|
||||
import uuid
|
||||
import time
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
|
||||
IQ_SET = '/iq[@type="set"]'
|
||||
|
|
@ -757,7 +758,7 @@ class XEP_0166(object):
|
|||
elt = content_data.pop(elt_name) if delete else content_data[elt_name]
|
||||
else:
|
||||
elt = force_element
|
||||
d = defer.maybeDeferred(
|
||||
d = utils.asDeferred(
|
||||
method, client, action, session, content_name, elt
|
||||
)
|
||||
defers_list.append(d)
|
||||
|
|
@ -846,7 +847,7 @@ class XEP_0166(object):
|
|||
application = content_data["application"]
|
||||
app_session_accept_cb = application.handler.jingleHandler
|
||||
|
||||
app_d = defer.maybeDeferred(
|
||||
app_d = utils.asDeferred(
|
||||
app_session_accept_cb,
|
||||
client,
|
||||
XEP_0166.A_SESSION_INITIATE,
|
||||
|
|
@ -860,7 +861,7 @@ class XEP_0166(object):
|
|||
transport = content_data["transport"]
|
||||
transport_session_accept_cb = transport.handler.jingleHandler
|
||||
|
||||
transport_d = defer.maybeDeferred(
|
||||
transport_d = utils.asDeferred(
|
||||
transport_session_accept_cb,
|
||||
client,
|
||||
XEP_0166.A_SESSION_INITIATE,
|
||||
|
|
|
|||
|
|
@ -17,23 +17,24 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sat.core.i18n import _
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core import exceptions
|
||||
from sat.core.log import getLogger
|
||||
|
||||
log = getLogger(__name__)
|
||||
from sat.tools import xml_tools
|
||||
from wokkel import disco, iwokkel
|
||||
import base64
|
||||
import time
|
||||
from pathlib import Path
|
||||
from functools import partial
|
||||
from zope.interface import implementer
|
||||
from twisted.python import failure
|
||||
from twisted.words.protocols.jabber import xmlstream
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.protocols.jabber import error as jabber_error
|
||||
from twisted.internet import defer
|
||||
from functools import partial
|
||||
import base64
|
||||
import time
|
||||
from wokkel import disco, iwokkel
|
||||
from sat.tools import xml_tools
|
||||
from sat.core.i18n import _
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core import exceptions
|
||||
from sat.core.log import getLogger
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
|
|
@ -90,7 +91,7 @@ class XEP_0231(object):
|
|||
PLUGIN_INFO[C.PI_IMPORT_NAME], cid, data_elt.getAttribute("type"), max_age
|
||||
) as f:
|
||||
|
||||
file_path = f.name
|
||||
file_path = Path(f.name)
|
||||
f.write(base64.b64decode(str(data_elt)))
|
||||
|
||||
return file_path
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# SAT plugin for Jingle File Transfer (XEP-0234)
|
||||
# SàT plugin for Jingle File Transfer (XEP-0234)
|
||||
# Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -181,7 +181,7 @@ class XEP_0234(object):
|
|||
file_data.update(kwargs)
|
||||
return self.buildFileElement(client, **file_data)
|
||||
|
||||
def parseFileElement(
|
||||
async def parseFileElement(
|
||||
self, client, file_elt, file_data=None, given=False, parent_elt=None,
|
||||
keep_empty_range=False):
|
||||
"""Parse a <file> element and file dictionary accordingly
|
||||
|
|
@ -451,7 +451,9 @@ class XEP_0234(object):
|
|||
|
||||
return desc_elt
|
||||
|
||||
def jingleRequestConfirmation(self, client, action, session, content_name, desc_elt):
|
||||
async def jingleRequestConfirmation(
|
||||
self, client, action, session, content_name, desc_elt
|
||||
):
|
||||
"""This method request confirmation for a jingle session"""
|
||||
content_data = session["contents"][content_name]
|
||||
senders = content_data["senders"]
|
||||
|
|
@ -467,21 +469,20 @@ class XEP_0234(object):
|
|||
|
||||
if senders == self._j.ROLE_RESPONDER:
|
||||
# we send the file
|
||||
return self._fileSendingRequestConf(
|
||||
return await self._fileSendingRequestConf(
|
||||
client, session, content_data, content_name, file_data, file_elt
|
||||
)
|
||||
else:
|
||||
# we receive the file
|
||||
return self._fileReceivingRequestConf(
|
||||
return await self._fileReceivingRequestConf(
|
||||
client, session, content_data, content_name, file_data, file_elt
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _fileSendingRequestConf(
|
||||
async def _fileSendingRequestConf(
|
||||
self, client, session, content_data, content_name, file_data, file_elt
|
||||
):
|
||||
"""parse file_elt, and handle file retrieving/permission checking"""
|
||||
self.parseFileElement(client, file_elt, file_data)
|
||||
await self.parseFileElement(client, file_elt, file_data)
|
||||
content_data["application_data"]["file_data"] = file_data
|
||||
finished_d = content_data["finished_d"] = defer.Deferred()
|
||||
|
||||
|
|
@ -496,22 +497,22 @@ class XEP_0234(object):
|
|||
file_elt,
|
||||
)
|
||||
if not cont:
|
||||
confirmed = yield confirmed_d
|
||||
confirmed = await confirmed_d
|
||||
if confirmed:
|
||||
args = [client, session, content_name, content_data]
|
||||
finished_d.addCallbacks(
|
||||
self._finishedCb, self._finishedEb, args, None, args
|
||||
)
|
||||
defer.returnValue(confirmed)
|
||||
return confirmed
|
||||
|
||||
log.warning(_("File continue is not implemented yet"))
|
||||
defer.returnValue(False)
|
||||
return False
|
||||
|
||||
def _fileReceivingRequestConf(
|
||||
async def _fileReceivingRequestConf(
|
||||
self, client, session, content_data, content_name, file_data, file_elt
|
||||
):
|
||||
"""parse file_elt, and handle user permission/file opening"""
|
||||
self.parseFileElement(client, file_elt, file_data, given=True)
|
||||
await self.parseFileElement(client, file_elt, file_data, given=True)
|
||||
try:
|
||||
hash_algo, file_data["given_file_hash"] = self._hash.parseHashElt(file_elt)
|
||||
except exceptions.NotFound:
|
||||
|
|
@ -540,21 +541,18 @@ class XEP_0234(object):
|
|||
content_data["application_data"]["file_data"] = file_data
|
||||
|
||||
# now we actualy request permission to user
|
||||
def gotConfirmation(confirmed):
|
||||
if confirmed:
|
||||
args = [client, session, content_name, content_data]
|
||||
finished_d.addCallbacks(
|
||||
self._finishedCb, self._finishedEb, args, None, args
|
||||
)
|
||||
return confirmed
|
||||
|
||||
# deferred to track end of transfer
|
||||
finished_d = content_data["finished_d"] = defer.Deferred()
|
||||
d = self._f.getDestDir(
|
||||
confirmed = await self._f.getDestDir(
|
||||
client, session["peer_jid"], content_data, file_data, stream_object=True
|
||||
)
|
||||
d.addCallback(gotConfirmation)
|
||||
return d
|
||||
if confirmed:
|
||||
args = [client, session, content_name, content_data]
|
||||
finished_d.addCallbacks(
|
||||
self._finishedCb, self._finishedEb, args, None, args
|
||||
)
|
||||
return confirmed
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def jingleHandler(self, client, action, session, content_name, desc_elt):
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ PLUGIN_INFO = {
|
|||
C.PI_TYPE: "XEP",
|
||||
C.PI_MODES: C.PLUG_MODE_BOTH,
|
||||
C.PI_PROTOCOLS: ["XEP-0329"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0234", "XEP-0300", "XEP-0106"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0231", "XEP-0234", "XEP-0300", "XEP-0106"],
|
||||
C.PI_MAIN: "XEP_0329",
|
||||
C.PI_HANDLER: "yes",
|
||||
C.PI_DESCRIPTION: _("""Implementation of File Information Sharing"""),
|
||||
|
|
@ -272,6 +272,7 @@ class XEP_0329(object):
|
|||
log.info(_("File Information Sharing initialization"))
|
||||
self.host = host
|
||||
ShareNode.host = host
|
||||
self._b = host.plugins["XEP-0231"]
|
||||
self._h = host.plugins["XEP-0300"]
|
||||
self._jf = host.plugins["XEP-0234"]
|
||||
host.bridge.addMethod(
|
||||
|
|
@ -672,7 +673,7 @@ class XEP_0329(object):
|
|||
client, iq_elt, self._compGetRootNodesCb, self._compGetFilesFromNodeCb
|
||||
)
|
||||
|
||||
def _parseResult(self, iq_elt, client):
|
||||
async def _parseResult(self, client, peer_jid, iq_elt):
|
||||
query_elt = next(iq_elt.elements(NS_FIS, "query"))
|
||||
files = []
|
||||
|
||||
|
|
@ -680,10 +681,24 @@ class XEP_0329(object):
|
|||
if elt.name == "file":
|
||||
# we have a file
|
||||
try:
|
||||
file_data = self._jf.parseFileElement(client, elt)
|
||||
file_data = await self._jf.parseFileElement(client, elt)
|
||||
except exceptions.DataError:
|
||||
continue
|
||||
file_data["type"] = C.FILE_TYPE_FILE
|
||||
try:
|
||||
thumbs = file_data['extra'][C.KEY_THUMBNAILS]
|
||||
except KeyError:
|
||||
log.debug(f"No thumbnail found for {file_data}")
|
||||
else:
|
||||
for thumb in thumbs:
|
||||
if 'url' not in thumb and "id" in thumb:
|
||||
try:
|
||||
file_path = await self._b.getFile(client, peer_jid, thumb['id'])
|
||||
except Exception as e:
|
||||
log.warning(f"Can't get thumbnail {thumb['id']!r} for {file_data}: {e}")
|
||||
else:
|
||||
thumb['filename'] = file_path.name
|
||||
|
||||
elif elt.name == "directory" and elt.uri == NS_FIS:
|
||||
# we have a directory
|
||||
|
||||
|
|
@ -1018,27 +1033,26 @@ class XEP_0329(object):
|
|||
def _listFiles(self, target_jid, path, extra, profile):
|
||||
client = self.host.getClient(profile)
|
||||
target_jid = client.jid if not target_jid else jid.JID(target_jid)
|
||||
d = self.listFiles(client, target_jid, path or None)
|
||||
d = defer.ensureDeferred(self.listFiles(client, target_jid, path or None))
|
||||
d.addCallback(self._serializeData)
|
||||
return d
|
||||
|
||||
def listFiles(self, client, target_jid, path=None, extra=None):
|
||||
async def listFiles(self, client, peer_jid, path=None, extra=None):
|
||||
"""List file shared by an entity
|
||||
|
||||
@param target_jid(jid.JID): jid of the sharing entity
|
||||
@param peer_jid(jid.JID): jid of the sharing entity
|
||||
@param path(unicode, None): path to the directory containing shared files
|
||||
None to get root directories
|
||||
@param extra(dict, None): extra data
|
||||
@return list(dict): shared files
|
||||
"""
|
||||
iq_elt = client.IQ("get")
|
||||
iq_elt["to"] = target_jid.full()
|
||||
iq_elt["to"] = peer_jid.full()
|
||||
query_elt = iq_elt.addElement((NS_FIS, "query"))
|
||||
if path:
|
||||
query_elt["node"] = path
|
||||
d = iq_elt.send()
|
||||
d.addCallback(self._parseResult, client)
|
||||
return d
|
||||
iq_result_elt = await iq_elt.send()
|
||||
return await self._parseResult(client, peer_jid, iq_result_elt)
|
||||
|
||||
def _localSharesGet(self, profile):
|
||||
client = self.host.getClient(profile)
|
||||
|
|
|
|||
Loading…
Reference in a new issue