Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/python-nbxmpp.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/nbxmpp
diff options
context:
space:
mode:
authorPhilipp Hörist <philipp@hoerist.com>2019-05-19 16:33:48 +0300
committerPhilipp Hörist <philipp@hoerist.com>2019-05-19 16:33:48 +0300
commitac23d278b5323960b9054535eaac0310cfeae2dd (patch)
tree188879a5975d28e63f4d5cf727ced48dbf94a0cd /nbxmpp
parent0a116941254d652808a2ec954bbf72aa196a872a (diff)
Add IBB (XEP-0047) module
Diffstat (limited to 'nbxmpp')
-rw-r--r--nbxmpp/dispatcher.py2
-rw-r--r--nbxmpp/modules/ibb.py160
-rw-r--r--nbxmpp/structs.py8
3 files changed, 170 insertions, 0 deletions
diff --git a/nbxmpp/dispatcher.py b/nbxmpp/dispatcher.py
index f78a5bd..bf42db3 100644
--- a/nbxmpp/dispatcher.py
+++ b/nbxmpp/dispatcher.py
@@ -68,6 +68,7 @@ from nbxmpp.modules.annotations import Annotations
from nbxmpp.modules.muclumbus import Muclumbus
from nbxmpp.modules.software_version import SoftwareVersion
from nbxmpp.modules.adhoc import AdHoc
+from nbxmpp.modules.ibb import IBB
from nbxmpp.modules.misc import unwrap_carbon
from nbxmpp.modules.misc import unwrap_mam
from nbxmpp.util import get_properties_struct
@@ -204,6 +205,7 @@ class XMPPDispatcher(PlugIn):
self._modules['Muclumbus'] = Muclumbus(self._owner)
self._modules['SoftwareVersion'] = SoftwareVersion(self._owner)
self._modules['AdHoc'] = AdHoc(self._owner)
+ self._modules['IBB'] = IBB(self._owner)
for instance in self._modules.values():
for handler in instance.handlers:
diff --git a/nbxmpp/modules/ibb.py b/nbxmpp/modules/ibb.py
new file mode 100644
index 0000000..e70721c
--- /dev/null
+++ b/nbxmpp/modules/ibb.py
@@ -0,0 +1,160 @@
+# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
+#
+# This file is part of nbxmpp.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+
+from nbxmpp.protocol import Error as ErrorStanza
+from nbxmpp.protocol import ERR_BAD_REQUEST
+from nbxmpp.protocol import ERR_FEATURE_NOT_IMPLEMENTED
+from nbxmpp.protocol import NodeProcessed
+from nbxmpp.protocol import Iq
+from nbxmpp.protocol import NS_IBB
+from nbxmpp.protocol import isResultNode
+from nbxmpp.structs import CommonResult
+from nbxmpp.structs import StanzaHandler
+from nbxmpp.structs import IBBData
+from nbxmpp.util import b64decode
+from nbxmpp.util import b64encode
+from nbxmpp.util import call_on_response
+from nbxmpp.util import callback
+from nbxmpp.util import raise_error
+
+log = logging.getLogger('nbxmpp.m.ibb')
+
+
+class IBB:
+ def __init__(self, client):
+ self._client = client
+ self.handlers = [
+ StanzaHandler(name='iq',
+ callback=self._process_ibb,
+ ns=NS_IBB,
+ priority=20),
+ ]
+
+ def _process_ibb(self, _con, stanza, properties):
+ if properties.type.is_set:
+ open_ = stanza.getTag('open', namespace=NS_IBB)
+ if open_ is not None:
+ properties.ibb = self._parse_open(stanza, open_)
+ return
+
+ close = stanza.getTag('close', namespace=NS_IBB)
+ if close is not None:
+ properties.ibb = self._parse_close(stanza, close)
+ return
+
+ data = stanza.getTag('data', namespace=NS_IBB)
+ if data is not None:
+ properties.ibb = self._parse_data(stanza, data)
+ return
+
+ def _parse_open(self, stanza, open_):
+ attrs = open_.getAttrs()
+ try:
+ block_size = int(attrs.get('block-size'))
+ except Exception as error:
+ log.warning(error)
+ log.warning(stanza)
+ self._client.send(ErrorStanza(stanza, ERR_BAD_REQUEST))
+ raise NodeProcessed
+
+ if block_size > 65535:
+ log.warning('Invalid block-size')
+ self._client.send(ErrorStanza(stanza, ERR_BAD_REQUEST))
+ raise NodeProcessed
+
+ sid = attrs.get('sid')
+ if not sid:
+ log.warning('Invalid sid')
+ self._client.send(ErrorStanza(stanza, ERR_BAD_REQUEST))
+ raise NodeProcessed
+
+ type_ = attrs.get('stanza')
+ if type_ == 'message':
+ self._client.send(ErrorStanza(stanza, ERR_FEATURE_NOT_IMPLEMENTED))
+ raise NodeProcessed
+
+ return IBBData(type='open', block_size=block_size, sid=sid)
+
+ def _parse_close(self, stanza, close):
+ sid = close.getAttrs().get('sid')
+ if sid is None:
+ log.warning('Invalid sid')
+ self._client.send(ErrorStanza(stanza, ERR_BAD_REQUEST))
+ raise NodeProcessed
+ return IBBData(type='close', sid=sid)
+
+ def _parse_data(self, stanza, data):
+ attrs = data.getAttrs()
+
+ sid = attrs.get('sid')
+ if sid is None:
+ log.warning('Invalid sid')
+ self._client.send(ErrorStanza(stanza, ERR_BAD_REQUEST))
+ raise NodeProcessed
+
+ try:
+ seq = int(attrs.get('seq'))
+ except Exception:
+ log.exception('Invalid seq')
+ self._client.send(ErrorStanza(stanza, ERR_BAD_REQUEST))
+ raise NodeProcessed
+
+ try:
+ decoded_data = b64decode(data.getData(), return_type=bytes)
+ except Exception:
+ log.exception('Failed to decode IBB data')
+ self._client.send(ErrorStanza(stanza, ERR_BAD_REQUEST))
+ raise NodeProcessed
+
+ return IBBData(type='data', sid=sid, seq=seq, data=decoded_data)
+
+ def send_reply(self, stanza, error=None):
+ if error is None:
+ reply = stanza.buildReply('result')
+ reply.getChildren().clear()
+ else:
+ reply = ErrorStanza(stanza, error)
+ self._client.send(reply)
+
+ @call_on_response('_default_response')
+ def send_open(self, jid, sid, block_size):
+ iq = Iq('set', to=jid)
+ iq.addChild('open',
+ {'block-size': block_size, 'sid': sid, 'stanza': 'iq'},
+ namespace=NS_IBB)
+ return iq
+
+ @call_on_response('_default_response')
+ def send_close(self, jid, sid):
+ iq = Iq('set', to=jid)
+ iq.addChild('close', {'sid': sid}, namespace=NS_IBB)
+ return iq
+
+ @call_on_response('_default_response')
+ def send_data(self, jid, sid, seq, data):
+ iq = Iq('set', to=jid)
+ ibb_data = iq.addChild('data', {'sid': sid, 'seq': seq}, namespace=NS_IBB)
+ ibb_data.setData(b64encode(data))
+ return iq
+
+ @callback
+ def _default_response(self, stanza):
+ if not isResultNode(stanza):
+ return raise_error(log.info, stanza)
+ return CommonResult(jid=stanza.getFrom())
diff --git a/nbxmpp/structs.py b/nbxmpp/structs.py
index d751663..a614541 100644
--- a/nbxmpp/structs.py
+++ b/nbxmpp/structs.py
@@ -109,6 +109,9 @@ SoftwareVersionResult = namedtuple('SoftwareVersionResult', 'name version os')
AdHocCommandNote = namedtuple('AdHocCommandNote', 'text type')
+IBBData = namedtuple('IBBData', 'block_size sid seq type data')
+IBBData.__new__.__defaults__ = (None, None, None, None, None)
+
class AdHocCommand(namedtuple('AdHocCommand', 'jid node name sessionid status data actions notes')):
__slots__ = []
@@ -285,11 +288,16 @@ class IqProperties:
self.query = None
self.payload = None
self.http_auth = None
+ self.ibb = None
@property
def is_http_auth(self):
return self.http_auth is not None
+ @property
+ def is_ibb(self):
+ return self.ibb is not None
+
class PresenceProperties:
def __init__(self):