plugin XEP-0353: update according to XEP changes + <description> fix:
- `XEP-0166_initiate_elt_built` trigger is now used to have the `<description>` element built, so they can be used with there correct arguments in the jingle initiation message - add message processing hint as it is now specified in the XEP
This commit is contained in:
parent
391112a059
commit
43a0621182
1 changed files with 77 additions and 63 deletions
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# SàT plugin for Jingle Message Initiation (XEP-0353)
|
||||
# Libervia plugin for Jingle Message Initiation (XEP-0353)
|
||||
# Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -16,17 +16,19 @@
|
|||
# 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 zope.interface import implementer
|
||||
from twisted.internet import defer
|
||||
from twisted.internet import reactor
|
||||
from twisted.words.protocols.jabber import xmlstream, jid, error
|
||||
from twisted.words.protocols.jabber import error, jid
|
||||
from twisted.words.protocols.jabber import xmlstream
|
||||
from twisted.words.xish import domish
|
||||
from wokkel import disco, iwokkel
|
||||
from sat.core.i18n import _, D_
|
||||
from sat.core.constants import Const as C
|
||||
from zope.interface import implementer
|
||||
|
||||
from sat.core import exceptions
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core.core_types import SatXMPPEntity
|
||||
from sat.core.i18n import D_, _
|
||||
from sat.core.log import getLogger
|
||||
from sat.tools import utils
|
||||
from sat.tools import xml_tools
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
|
@ -40,7 +42,7 @@ PLUGIN_INFO = {
|
|||
C.PI_TYPE: "XEP",
|
||||
C.PI_MODES: [C.PLUG_MODE_CLIENT],
|
||||
C.PI_PROTOCOLS: ["XEP-0353"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0166"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0166", "XEP-0334"],
|
||||
C.PI_MAIN: "XEP_0353",
|
||||
C.PI_HANDLER: "yes",
|
||||
C.PI_DESCRIPTION: _("""Implementation of Jingle Message Initiation"""),
|
||||
|
|
@ -48,13 +50,19 @@ PLUGIN_INFO = {
|
|||
|
||||
|
||||
class XEP_0353:
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_("plugin {name} initialization").format(name=PLUGIN_INFO[C.PI_NAME]))
|
||||
self.host = host
|
||||
host.register_namespace("jingle-message", NS_JINGLE_MESSAGE)
|
||||
self._j = host.plugins["XEP-0166"]
|
||||
host.trigger.add("XEP-0166_initiate", self._on_initiate_trigger)
|
||||
self._h = host.plugins["XEP-0334"]
|
||||
host.trigger.add(
|
||||
"XEP-0166_initiate_elt_built",
|
||||
self._on_initiate_trigger,
|
||||
# this plugin set the resource, we want it to happen first to other trigger
|
||||
# can get the full peer JID
|
||||
priority=host.trigger.MAX_PRIORITY,
|
||||
)
|
||||
host.trigger.add("message_received", self._on_message_received)
|
||||
|
||||
def get_handler(self, client):
|
||||
|
|
@ -66,22 +74,29 @@ class XEP_0353:
|
|||
|
||||
def build_message_data(self, client, peer_jid, verb, session_id):
|
||||
mess_data = {
|
||||
'from': client.jid,
|
||||
'to': peer_jid,
|
||||
'uid': '',
|
||||
'message': {},
|
||||
'type': C.MESS_TYPE_CHAT,
|
||||
'subject': {},
|
||||
'extra': {}
|
||||
"from": client.jid,
|
||||
"to": peer_jid,
|
||||
"uid": "",
|
||||
"message": {},
|
||||
"type": C.MESS_TYPE_CHAT,
|
||||
"subject": {},
|
||||
"extra": {},
|
||||
}
|
||||
client.generate_message_xml(mess_data)
|
||||
verb_elt = mess_data["xml"].addElement((NS_JINGLE_MESSAGE, verb))
|
||||
message_elt = mess_data["xml"]
|
||||
verb_elt = message_elt.addElement((NS_JINGLE_MESSAGE, verb))
|
||||
verb_elt["id"] = session_id
|
||||
self._h.add_hint_elements(message_elt, [self._h.HINT_STORE])
|
||||
return mess_data
|
||||
|
||||
async def _on_initiate_trigger(self, client, session, contents):
|
||||
# FIXME: check that at least one resource of the peer_jid can handle the feature
|
||||
peer_jid = session['peer_jid']
|
||||
async def _on_initiate_trigger(
|
||||
self,
|
||||
client: SatXMPPEntity,
|
||||
session: dict,
|
||||
iq_elt: domish.Element,
|
||||
jingle_elt: domish.Element,
|
||||
) -> bool:
|
||||
peer_jid = session["peer_jid"]
|
||||
if peer_jid.resource:
|
||||
return True
|
||||
|
||||
|
|
@ -103,43 +118,43 @@ class XEP_0353:
|
|||
# according to XEP-0353 §3.1
|
||||
await client.presence.available(peer_jid)
|
||||
|
||||
mess_data = self.build_message_data(client, peer_jid, "propose", session['id'])
|
||||
for content in contents:
|
||||
content_data = self._j.get_content_data(
|
||||
content)
|
||||
try:
|
||||
jingle_description_elt = (
|
||||
content_data.application.handler.jingle_description_elt
|
||||
)
|
||||
except AttributeError:
|
||||
log.debug(
|
||||
"no jingle_description_elt set for "
|
||||
f"{content_data.application.handler}"
|
||||
)
|
||||
description_elt = domish.Element((content["app_ns"], "description"))
|
||||
else:
|
||||
description_elt = await utils.as_deferred(
|
||||
jingle_description_elt,
|
||||
client, session, content_data.content_name, *content_data.app_args,
|
||||
**content_data.app_kwargs
|
||||
)
|
||||
mess_data["xml"].propose.addChild(description_elt)
|
||||
mess_data = self.build_message_data(client, peer_jid, "propose", session["id"])
|
||||
message_elt = mess_data["xml"]
|
||||
for content_data in session["contents"].values():
|
||||
# we get the full element build by the application plugin
|
||||
jingle_description_elt = content_data["application_data"]["desc_elt"]
|
||||
# and copy it to only keep the root <description> element, no children
|
||||
description_elt = domish.Element(
|
||||
(jingle_description_elt.uri, jingle_description_elt.name),
|
||||
defaultUri=jingle_description_elt.defaultUri,
|
||||
attribs=jingle_description_elt.attributes,
|
||||
localPrefixes=jingle_description_elt.localPrefixes,
|
||||
)
|
||||
message_elt.propose.addChild(description_elt)
|
||||
response_d = defer.Deferred()
|
||||
# we wait for 2 min before cancelling the session init
|
||||
# response_d.addTimeout(2*60, reactor)
|
||||
# FIXME: let's application decide timeout?
|
||||
response_d.addTimeout(2, reactor)
|
||||
client._xep_0353_pending_sessions[session['id']] = response_d
|
||||
response_d.addTimeout(2 * 60, reactor)
|
||||
client._xep_0353_pending_sessions[session["id"]] = response_d
|
||||
await client.send_message_data(mess_data)
|
||||
try:
|
||||
accepting_jid = await response_d
|
||||
except defer.TimeoutError:
|
||||
log.warning(_(
|
||||
"Message initiation with {peer_jid} timed out"
|
||||
).format(peer_jid=peer_jid))
|
||||
log.warning(
|
||||
_("Message initiation with {peer_jid} timed out").format(
|
||||
peer_jid=peer_jid
|
||||
)
|
||||
)
|
||||
else:
|
||||
if iq_elt["to"] != accepting_jid.userhost():
|
||||
raise exceptions.InternalError(
|
||||
f"<jingle> 'to' attribute ({iq_elt['to']!r}) must not differ "
|
||||
f"from bare JID of the accepting entity ({accepting_jid!r}), this "
|
||||
"may be a sign of an internal bug, a hack attempt, or a MITM attack!"
|
||||
)
|
||||
iq_elt["to"] = accepting_jid.full()
|
||||
session["peer_jid"] = accepting_jid
|
||||
del client._xep_0353_pending_sessions[session['id']]
|
||||
del client._xep_0353_pending_sessions[session["id"]]
|
||||
return True
|
||||
|
||||
async def _on_message_received(self, client, message_elt, post_treat):
|
||||
|
|
@ -170,11 +185,11 @@ class XEP_0353:
|
|||
human_name = getattr(application.handler, "human_name", application.name)
|
||||
except (exceptions.NotFound, AttributeError):
|
||||
if app_ns.startswith("urn:xmpp:jingle:apps:"):
|
||||
human_name = app_ns[21:].split(":", 1)[0].replace('-', ' ').title()
|
||||
human_name = app_ns[21:].split(":", 1)[0].replace("-", " ").title()
|
||||
else:
|
||||
splitted_ns = app_ns.split(':')
|
||||
splitted_ns = app_ns.split(":")
|
||||
if len(splitted_ns) > 1:
|
||||
human_name = splitted_ns[-2].replace('- ', ' ').title()
|
||||
human_name = splitted_ns[-2].replace("- ", " ").title()
|
||||
else:
|
||||
human_name = app_ns
|
||||
|
||||
|
|
@ -185,28 +200,27 @@ class XEP_0353:
|
|||
).format(peer_jid=peer_jid, human_name=human_name)
|
||||
confirm_title = D_("Invitation from an unknown contact")
|
||||
accept = await xml_tools.defer_confirm(
|
||||
self.host, confirm_msg, confirm_title, profile=client.profile,
|
||||
self.host,
|
||||
confirm_msg,
|
||||
confirm_title,
|
||||
profile=client.profile,
|
||||
action_extra={
|
||||
"type": C.META_TYPE_NOT_IN_ROSTER_LEAK,
|
||||
"session_id": session_id,
|
||||
"from_jid": peer_jid.full(),
|
||||
}
|
||||
},
|
||||
)
|
||||
if not accept:
|
||||
mess_data = self.build_message_data(
|
||||
client, client.jid.userhostJID(), "reject", session_id)
|
||||
client, client.jid.userhostJID(), "reject", session_id
|
||||
)
|
||||
await client.send_message_data(mess_data)
|
||||
# we don't sent anything to sender, to avoid leaking presence
|
||||
return False
|
||||
else:
|
||||
await client.presence.available(peer_jid)
|
||||
session_id = elt["id"]
|
||||
# FIXME: accept is not used anymore in new specification, check it and remove it
|
||||
mess_data = self.build_message_data(
|
||||
client, client.jid.userhostJID(), "accept", session_id)
|
||||
await client.send_message_data(mess_data)
|
||||
mess_data = self.build_message_data(
|
||||
client, peer_jid, "proceed", session_id)
|
||||
mess_data = self.build_message_data(client, peer_jid, "proceed", session_id)
|
||||
await client.send_message_data(mess_data)
|
||||
return False
|
||||
|
||||
|
|
@ -224,8 +238,9 @@ class XEP_0353:
|
|||
response_d = client._xep_0353_pending_sessions[session_id]
|
||||
except KeyError:
|
||||
log.warning(
|
||||
_("no pending session found with id {session_id}, did it timed out?")
|
||||
.format(session_id=session_id)
|
||||
_(
|
||||
"no pending session found with id {session_id}, did it timed out?"
|
||||
).format(session_id=session_id)
|
||||
)
|
||||
return True
|
||||
|
||||
|
|
@ -241,7 +256,6 @@ class XEP_0353:
|
|||
|
||||
@implementer(iwokkel.IDisco)
|
||||
class Handler(xmlstream.XMPPHandler):
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
|
||||
return [disco.DiscoFeature(NS_JINGLE_MESSAGE)]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue