Philipp Hörist pushed to branch master at gajim / python-nbxmpp
Commits: 5f8368e5 by Philipp Hörist at 2018-12-21T22:33:14Z Add more property parsing
- Add (XEP-0172) module - Add (XEP-0045) module - Add a BasePresence module which parses standard presence propertys
- - - - - 8a87c2be by Philipp Hörist at 2018-12-21T23:34:08Z Simplify properties struct
- - - - -
6 changed files:
- nbxmpp/dispatcher_nb.py - + nbxmpp/modules/muc.py - + nbxmpp/modules/nickname.py - + nbxmpp/modules/presence.py - nbxmpp/protocol.py - nbxmpp/util.py
Changes:
===================================== nbxmpp/dispatcher_nb.py ===================================== @@ -43,8 +43,11 @@ from nbxmpp.protocol import Error from nbxmpp.protocol import ERR_FEATURE_NOT_IMPLEMENTED from nbxmpp.modules.eme import EME from nbxmpp.modules.http_auth import HTTPAuth +from nbxmpp.modules.presence import BasePresence +from nbxmpp.modules.nickname import Nickname +from nbxmpp.modules.muc import MUC from nbxmpp.misc import unwrap_carbon -from nbxmpp.util import get_property_dict +from nbxmpp.util import get_properties_struct
log = logging.getLogger('nbxmpp.dispatcher_nb') @@ -148,8 +151,11 @@ class XMPPDispatcher(PlugIn): self.handlers = handlers
def _register_modules(self): + self._modules['BasePresence'] = BasePresence(self._owner) self._modules['EME'] = EME(self._owner) self._modules['HTTPAuth'] = HTTPAuth(self._owner) + self._modules['Nickname'] = Nickname(self._owner) + self._modules['MUC'] = MUC(self._owner)
for instance in self._modules.values(): for handler in instance.handlers: @@ -477,7 +483,7 @@ class XMPPDispatcher(PlugIn): # Convert simplexml to Protocol object stanza = self.handlers[xmlns][name]['type'](node=stanza)
- properties = get_property_dict(name) + properties = get_properties_struct(name) if name == 'message': # https://tools.ietf.org/html/rfc6120#section-8.1.1.1 # If the stanza does not include a 'to' address then the client MUST
===================================== nbxmpp/modules/muc.py ===================================== @@ -0,0 +1,51 @@ +# Copyright (C) 2018 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 NS_MUC_USER +from nbxmpp.protocol import NS_MUC +from nbxmpp.util import StanzaHandler + +log = logging.getLogger('nbxmpp.m.presence') + + +class MUC: + def __init__(self, client): + self._client = client + self.handlers = [ + StanzaHandler(name='presence', + callback=self._process_muc_presence, + ns=NS_MUC, + priority=11), + StanzaHandler(name='presence', + callback=self._process_muc_user_presence, + ns=NS_MUC_USER, + priority=11), + ] + + def _process_muc_presence(self, _con, stanza, properties): + muc = stanza.getTag('x', namespace=NS_MUC) + if muc is None: + return + properties.from_muc = True + + def _process_muc_user_presence(self, _con, stanza, properties): + muc = stanza.getTag('x', namespace=NS_MUC_USER) + if muc is None: + return + properties.from_muc = True
===================================== nbxmpp/modules/nickname.py ===================================== @@ -0,0 +1,57 @@ +# Copyright (C) 2018 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 NS_NICK +from nbxmpp.util import StanzaHandler + +log = logging.getLogger('nbxmpp.m.nickname') + + +class Nickname: + def __init__(self, client): + self._client = client + self.handlers = [ + StanzaHandler(name='message', + callback=self._process_nickname, + ns=NS_NICK, + priority=40), + StanzaHandler(name='presence', + callback=self._process_nickname, + ns=NS_NICK, + priority=40), + ] + + def _process_nickname(self, _con, stanza, properties): + if stanza.getName() == 'message': + properties.nickname = self._parse_nickname(stanza) + + elif stanza.getName() == 'presence': + # the nickname MUST NOT be included in presence broadcasts + # (i.e., <presence/> stanzas with no 'type' attribute or + # of type "unavailable"). + if properties.type in (None, 'unavailable'): + return + properties.nickname = self._parse_nickname(stanza) + + @staticmethod + def _parse_nickname(stanza): + nickname = stanza.getTagData('nick', namespace=NS_NICK) + if not nickname: + return + return nickname
===================================== nbxmpp/modules/presence.py ===================================== @@ -0,0 +1,91 @@ +# Copyright (C) 2018 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 +from nbxmpp.protocol import ERR_BAD_REQUEST +from nbxmpp.protocol import NodeProcessed +from nbxmpp.protocol import presence_types +from nbxmpp.util import StanzaHandler + +log = logging.getLogger('nbxmpp.m.presence') + + +class BasePresence: + def __init__(self, client): + self._client = client + self.handlers = [ + StanzaHandler(name='presence', + callback=self._process_presence_base, + priority=10), + ] + + def _process_presence_base(self, _con, stanza, properties): + properties.type = self._parse_type(stanza) + properties.priority = self._parse_priority(stanza) + properties.show = self._parse_show(stanza) + properties.jid = stanza.getFrom() + properties.id = stanza.getID() + properties.status = stanza.getStatus() + + if properties.type == 'error': + properties.error_code = stanza.getErrorCode() + properties.error_message = stanza.getErrorMsg() + + own_jid = self._client.get_bound_jid() + if own_jid == stanza.getFrom(): + properties.self_presence = True + + @staticmethod + def _parse_priority(stanza): + priority = stanza.getPriority() + if priority is None: + return 0 + + try: + priority = int(priority) + except Exception: + log.warning('Invalid priority value: %s', priority) + log.warning(stanza) + return 0 + + if priority not in range(-128, 127): + log.warning('Invalid priority value: %s', priority) + log.warning(stanza) + return 0 + + return priority + + def _parse_type(self, stanza): + type_ = stanza.getType() + if type_ is None: + return + + if type_ not in presence_types: + log.warning('Presence with invalid type received') + log.warning(stanza) + self._client.send(Error(stanza, ERR_BAD_REQUEST)) + raise NodeProcessed + return type_ + + @staticmethod + def _parse_show(stanza): + show = stanza.getShow() + if show not in ('chat', 'away', 'xa', 'dnd'): + return + return show
===================================== nbxmpp/protocol.py ===================================== @@ -496,6 +496,16 @@ _status_conditions = { 'removed-shutdown': 332, }
+presence_types = [ + 'error', + 'probe', + 'subscribe', + 'subscribed', + 'unavailable', + 'unsubscribe', + 'unsubscribed' +] + STREAM_NOT_AUTHORIZED = 'urn:ietf:params:xml:ns:xmpp-streams not-authorized' STREAM_REMOTE_CONNECTION_FAILED = 'urn:ietf:params:xml:ns:xmpp-streams remote-connection-failed' SASL_MECHANISM_TOO_WEAK = 'urn:ietf:params:xml:ns:xmpp-sasl mechanism-too-weak'
===================================== nbxmpp/util.py ===================================== @@ -50,50 +50,55 @@ def b64encode(data, return_type=str): return result.decode()
-class PropertyBase: - def __init__(self): - self._data = {} - - def __getattr__(self, key): - return self._data[key] +class Properties: + pass
- def __setattr__(self, key, value): - if '_data' in key: - super().__setattr__(key, value) - else: - self._data[key] = value
- -class MessagePropertyDict(PropertyBase): +class MessageProperties: def __init__(self): - self._data = { - 'carbon_type': None, - 'eme': None, - 'http_auth': None, - } + self.carbon_type = None + self.eme = None + self.http_auth = None + self.nickname = None
@property def is_http_auth(self): - return self._data['http_auth'] is not None + return self.http_auth is not None
-class IqPropertyDict(PropertyBase): +class IqProperties: def __init__(self): - self._data = { - 'http_auth': None, - } + self.http_auth = None
@property def is_http_auth(self): - return self._data['http_auth'] is not None + return self.http_auth is not None
-def get_property_dict(name): +class PresenceProperties: + def __init__(self): + self.type = None + self.priority = None + self.show = None + self.jid = None + self.resource = None + self.id = None + self.nickname = None + self.self_presence = False + self.from_muc = False + self.status = '' + self.error_message = '' + self.error_code = '' + + +def get_properties_struct(name): if name == 'message': - return MessagePropertyDict() + return MessageProperties() if name == 'iq': - return IqPropertyDict() - return PropertyBase() + return IqProperties() + if name == 'presence': + return PresenceProperties() + return Properties()
def validate_jid(jid_string):
View it on GitLab: https://dev.gajim.org/gajim/python-nbxmpp/compare/987a239f019d0bc9bc6c65dda8...