Commit ac23d278 authored by Philipp Hörist's avatar Philipp Hörist

Add IBB (XEP-0047) module

parent 0a116941
Pipeline #3512 passed with stages
in 32 seconds
......@@ -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:
# Copyright (C) 2019 Philipp Hörist <philipp AT>
# 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
# 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 <>.
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 = [
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_)
close = stanza.getTag('close', namespace=NS_IBB)
if close is not None:
properties.ibb = self._parse_close(stanza, close)
data = stanza.getTag('data', namespace=NS_IBB)
if data is not None:
properties.ibb = self._parse_data(stanza, data)
def _parse_open(self, stanza, open_):
attrs = open_.getAttrs()
block_size = int(attrs.get('block-size'))
except Exception as error:
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
seq = int(attrs.get('seq'))
except Exception:
log.exception('Invalid seq')
self._client.send(ErrorStanza(stanza, ERR_BAD_REQUEST))
raise NodeProcessed
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 = ErrorStanza(stanza, error)
def send_open(self, jid, sid, block_size):
iq = Iq('set', to=jid)
{'block-size': block_size, 'sid': sid, 'stanza': 'iq'},
return iq
def send_close(self, jid, sid):
iq = Iq('set', to=jid)
iq.addChild('close', {'sid': sid}, namespace=NS_IBB)
return iq
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)
return iq
def _default_response(self, stanza):
if not isResultNode(stanza):
return raise_error(, stanza)
return CommonResult(jid=stanza.getFrom())
......@@ -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
def is_http_auth(self):
return self.http_auth is not None
def is_ibb(self):
return self.ibb is not None
class PresenceProperties:
def __init__(self):
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment