diff --git a/doap.xml b/doap.xml index 027edf93..70b72373 100644 --- a/doap.xml +++ b/doap.xml @@ -512,6 +512,14 @@ unknown + + + + complete + 0.3 + NEXT + + diff --git a/slixmpp/plugins/__init__.py b/slixmpp/plugins/__init__.py index 91f062a3..4c3235f1 100644 --- a/slixmpp/plugins/__init__.py +++ b/slixmpp/plugins/__init__.py @@ -85,6 +85,7 @@ __all__ = [ 'xep_0323', # IoT Systems Sensor Data 'xep_0325', # IoT Systems Control 'xep_0332', # HTTP Over XMPP Transport + 'xep_0353', # Jingle Message Initiation 'xep_0369', # MIX-CORE 'xep_0377', # Spam reporting 'xep_0403', # MIX-Presence diff --git a/slixmpp/plugins/xep_0353/__init__.py b/slixmpp/plugins/xep_0353/__init__.py new file mode 100644 index 00000000..89d86c51 --- /dev/null +++ b/slixmpp/plugins/xep_0353/__init__.py @@ -0,0 +1,14 @@ +""" + slixmpp: The Slick XMPP Library + Copyright (C) 2020 Emmanuel Gil Peyrot + This file is part of slixmpp. + + See the file LICENSE for copying permission. +""" + +from slixmpp.plugins.base import register_plugin + +from slixmpp.plugins.xep_0353.stanza import Propose, Retract, Accept, Proceed, Reject +from slixmpp.plugins.xep_0353.jingle_message import XEP_0353 + +register_plugin(XEP_0353) diff --git a/slixmpp/plugins/xep_0353/jingle_message.py b/slixmpp/plugins/xep_0353/jingle_message.py new file mode 100644 index 00000000..07395241 --- /dev/null +++ b/slixmpp/plugins/xep_0353/jingle_message.py @@ -0,0 +1,101 @@ +""" + slixmpp: The Slick XMPP Library + Copyright (C) 2020 Emmanuel Gil Peyrot + This file is part of slixmpp. + + See the file LICENSE for copying permission. +""" + +import logging + +from typing import Iterable, Tuple, Optional + +from slixmpp import JID, Message +from slixmpp.plugins import BasePlugin +from slixmpp.xmlstream import register_stanza_plugin +from slixmpp.xmlstream.handler import Callback +from slixmpp.xmlstream.matcher import StanzaPath +from slixmpp.plugins.xep_0353 import stanza, Propose, Retract, Accept, Proceed, Reject + +log = logging.getLogger(__name__) + +class XEP_0353(BasePlugin): + + name = 'xep_0353' + description = 'XEP-0353: Jingle Message Initiation' + stanza = stanza + + def plugin_init(self): + register_stanza_plugin(Message, Propose) + register_stanza_plugin(Message, Retract) + register_stanza_plugin(Message, Accept) + register_stanza_plugin(Message, Proceed) + register_stanza_plugin(Message, Reject) + + self.xmpp.register_handler( + Callback('Indicating Intent to Start a Session', + StanzaPath('message/jingle_propose'), + self._handle_propose)) + self.xmpp.register_handler( + Callback('Disavowing Intent to Start a Session', + StanzaPath('message/jingle_retract'), + self._handle_retract)) + self.xmpp.register_handler( + Callback('Accepting Intent to Start a Session', + StanzaPath('message/jingle_accept'), + self._handle_accept)) + self.xmpp.register_handler( + Callback('Proceed', + StanzaPath('message/jingle_proceed'), + self._handle_accept)) + self.xmpp.register_handler( + Callback('Rejecting Intent to Start a Session', + StanzaPath('message/jingle_reject'), + self._handle_reject)) + + def session_bind(self, jid): + self.xmpp.plugin['xep_0030'].add_feature(stanza.JingleMessage.namespace) + + def plugin_end(self): + self.xmpp.plugin['xep_0030'].del_feature(feature=stanza.JingleMessage.namespace) + + def _handle_propose(self, message): + self.xmpp.event('jingle_message_propose', message) + + def _handle_retract(self, message): + self.xmpp.event('jingle_message_retract', message) + + def _handle_accept(self, message): + self.xmpp.event('jingle_message_accept', message) + + def _handle_proceed(self, message): + self.xmpp.event('jingle_message_proceed', message) + + def _handle_reject(self, message): + self.xmpp.event('jingle_message_reject', message) + + def propose(self, mto: JID, sid: str, descriptions: Iterable[Tuple[str, str]], *, mfrom: Optional[JID] = None): + msg = self.xmpp.make_message(mto, mfrom=mfrom) + msg['jingle_propose']['id'] = sid + msg['jingle_propose']['descriptions'] = descriptions + msg.send() + + def retract(self, mto: JID, sid: str, *, mfrom: Optional[JID] = None): + msg = self.xmpp.make_message(mto, mfrom=mfrom) + msg['jingle_retract']['id'] = sid + msg.send() + + def accept(self, mto: JID, sid: str, *, mfrom: Optional[JID] = None): + msg = self.xmpp.make_message(mto, mfrom=mfrom) + msg['jingle_accept']['id'] = sid + msg.send() + + def proceed(self, mto: JID, sid: str, *, mfrom: Optional[JID] = None): + msg = self.xmpp.make_message(mto, mfrom=mfrom) + msg['jingle_proceed']['id'] = sid + msg.send() + + def reject(self, mto: JID, sid: str, *, mfrom: Optional[JID] = None): + msg = self.xmpp.make_message(mto, mfrom=mfrom) + msg['jingle_reject']['id'] = sid + msg.send() diff --git a/slixmpp/plugins/xep_0353/stanza.py b/slixmpp/plugins/xep_0353/stanza.py new file mode 100644 index 00000000..48046274 --- /dev/null +++ b/slixmpp/plugins/xep_0353/stanza.py @@ -0,0 +1,55 @@ +""" + slixmpp: The Slick XMPP Library + Copyright (C) 2020 Emmanuel Gil Peyrot + This file is part of slixmpp. + + See the file LICENSE for copying permission. +""" + +from typing import Iterable, List, Tuple + +from slixmpp.xmlstream import ElementBase, ET + +class JingleMessage(ElementBase): + namespace = 'urn:xmpp:jingle-message:0' + interfaces = {'id'} + +class Propose(JingleMessage): + name = 'propose' + plugin_attrib = 'jingle_propose' + interfaces = {'id', 'descriptions'} + + def get_descriptions(self) -> List[Tuple[str, str]]: + result = [] + for desc in self.xml: + namespace = desc.tag.split('}')[0][1:] + media = desc.attrib['media'] + result.append((namespace, media)) + return result + + def set_descriptions(self, descriptions: Iterable[Tuple[str, str]]): + self.del_descriptions() + for namespace, media in descriptions: + desc = ET.Element('{%s}description' % namespace) + desc.attrib['media'] = media + self.xml.append(desc) + + def del_descriptions(self): + for desc in self.xml.findall('{*}description'): + self.xml.remove(desc) + +class Retract(JingleMessage): + name = 'retract' + plugin_attrib = 'jingle_retract' + +class Accept(JingleMessage): + name = 'accept' + plugin_attrib = 'jingle_accept' + +class Proceed(JingleMessage): + name = 'proceed' + plugin_attrib = 'jingle_proceed' + +class Reject(JingleMessage): + name = 'reject' + plugin_attrib = 'jingle_reject'