Philipp Hörist pushed to branch master at gajim / python-nbxmpp

Commits:

6 changed files:

Changes:

  • nbxmpp/dispatcher_nb.py
    ... ... @@ -43,8 +43,11 @@ from nbxmpp.protocol import Error
    43 43
     from nbxmpp.protocol import ERR_FEATURE_NOT_IMPLEMENTED
    
    44 44
     from nbxmpp.modules.eme import EME
    
    45 45
     from nbxmpp.modules.http_auth import HTTPAuth
    
    46
    +from nbxmpp.modules.presence import BasePresence
    
    47
    +from nbxmpp.modules.nickname import Nickname
    
    48
    +from nbxmpp.modules.muc import MUC
    
    46 49
     from nbxmpp.misc import unwrap_carbon
    
    47
    -from nbxmpp.util import get_property_dict
    
    50
    +from nbxmpp.util import get_properties_struct
    
    48 51
     
    
    49 52
     
    
    50 53
     log = logging.getLogger('nbxmpp.dispatcher_nb')
    
    ... ... @@ -148,8 +151,11 @@ class XMPPDispatcher(PlugIn):
    148 151
             self.handlers = handlers
    
    149 152
     
    
    150 153
         def _register_modules(self):
    
    154
    +        self._modules['BasePresence'] = BasePresence(self._owner)
    
    151 155
             self._modules['EME'] = EME(self._owner)
    
    152 156
             self._modules['HTTPAuth'] = HTTPAuth(self._owner)
    
    157
    +        self._modules['Nickname'] = Nickname(self._owner)
    
    158
    +        self._modules['MUC'] = MUC(self._owner)
    
    153 159
     
    
    154 160
             for instance in self._modules.values():
    
    155 161
                 for handler in instance.handlers:
    
    ... ... @@ -477,7 +483,7 @@ class XMPPDispatcher(PlugIn):
    477 483
             # Convert simplexml to Protocol object
    
    478 484
             stanza = self.handlers[xmlns][name]['type'](node=stanza)
    
    479 485
     
    
    480
    -        properties = get_property_dict(name)
    
    486
    +        properties = get_properties_struct(name)
    
    481 487
             if name == 'message':
    
    482 488
                 # https://tools.ietf.org/html/rfc6120#section-8.1.1.1
    
    483 489
                 # If the stanza does not include a 'to' address then the client MUST
    

  • nbxmpp/modules/muc.py
    1
    +# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
    
    2
    +#
    
    3
    +# This file is part of nbxmpp.
    
    4
    +#
    
    5
    +# This program is free software; you can redistribute it and/or
    
    6
    +# modify it under the terms of the GNU General Public License
    
    7
    +# as published by the Free Software Foundation; either version 3
    
    8
    +# of the License, or (at your option) any later version.
    
    9
    +#
    
    10
    +# This program is distributed in the hope that it will be useful,
    
    11
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    12
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    13
    +# GNU General Public License for more details.
    
    14
    +#
    
    15
    +# You should have received a copy of the GNU General Public License
    
    16
    +# along with this program; If not, see <http://www.gnu.org/licenses/>.
    
    17
    +
    
    18
    +import logging
    
    19
    +
    
    20
    +from nbxmpp.protocol import NS_MUC_USER
    
    21
    +from nbxmpp.protocol import NS_MUC
    
    22
    +from nbxmpp.util import StanzaHandler
    
    23
    +
    
    24
    +log = logging.getLogger('nbxmpp.m.presence')
    
    25
    +
    
    26
    +
    
    27
    +class MUC:
    
    28
    +    def __init__(self, client):
    
    29
    +        self._client = client
    
    30
    +        self.handlers = [
    
    31
    +            StanzaHandler(name='presence',
    
    32
    +                          callback=self._process_muc_presence,
    
    33
    +                          ns=NS_MUC,
    
    34
    +                          priority=11),
    
    35
    +            StanzaHandler(name='presence',
    
    36
    +                          callback=self._process_muc_user_presence,
    
    37
    +                          ns=NS_MUC_USER,
    
    38
    +                          priority=11),
    
    39
    +        ]
    
    40
    +
    
    41
    +    def _process_muc_presence(self, _con, stanza, properties):
    
    42
    +        muc = stanza.getTag('x', namespace=NS_MUC)
    
    43
    +        if muc is None:
    
    44
    +            return
    
    45
    +        properties.from_muc = True
    
    46
    +
    
    47
    +    def _process_muc_user_presence(self, _con, stanza, properties):
    
    48
    +        muc = stanza.getTag('x', namespace=NS_MUC_USER)
    
    49
    +        if muc is None:
    
    50
    +            return
    
    51
    +        properties.from_muc = True

  • nbxmpp/modules/nickname.py
    1
    +# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
    
    2
    +#
    
    3
    +# This file is part of nbxmpp.
    
    4
    +#
    
    5
    +# This program is free software; you can redistribute it and/or
    
    6
    +# modify it under the terms of the GNU General Public License
    
    7
    +# as published by the Free Software Foundation; either version 3
    
    8
    +# of the License, or (at your option) any later version.
    
    9
    +#
    
    10
    +# This program is distributed in the hope that it will be useful,
    
    11
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    12
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    13
    +# GNU General Public License for more details.
    
    14
    +#
    
    15
    +# You should have received a copy of the GNU General Public License
    
    16
    +# along with this program; If not, see <http://www.gnu.org/licenses/>.
    
    17
    +
    
    18
    +import logging
    
    19
    +
    
    20
    +from nbxmpp.protocol import NS_NICK
    
    21
    +from nbxmpp.util import StanzaHandler
    
    22
    +
    
    23
    +log = logging.getLogger('nbxmpp.m.nickname')
    
    24
    +
    
    25
    +
    
    26
    +class Nickname:
    
    27
    +    def __init__(self, client):
    
    28
    +        self._client = client
    
    29
    +        self.handlers = [
    
    30
    +            StanzaHandler(name='message',
    
    31
    +                          callback=self._process_nickname,
    
    32
    +                          ns=NS_NICK,
    
    33
    +                          priority=40),
    
    34
    +            StanzaHandler(name='presence',
    
    35
    +                          callback=self._process_nickname,
    
    36
    +                          ns=NS_NICK,
    
    37
    +                          priority=40),
    
    38
    +        ]
    
    39
    +
    
    40
    +    def _process_nickname(self, _con, stanza, properties):
    
    41
    +        if stanza.getName() == 'message':
    
    42
    +            properties.nickname = self._parse_nickname(stanza)
    
    43
    +
    
    44
    +        elif stanza.getName() == 'presence':
    
    45
    +            # the nickname MUST NOT be included in presence broadcasts
    
    46
    +            # (i.e., <presence/> stanzas with no 'type' attribute or
    
    47
    +            # of type "unavailable").
    
    48
    +            if properties.type in (None, 'unavailable'):
    
    49
    +                return
    
    50
    +            properties.nickname = self._parse_nickname(stanza)
    
    51
    +
    
    52
    +    @staticmethod
    
    53
    +    def _parse_nickname(stanza):
    
    54
    +        nickname = stanza.getTagData('nick', namespace=NS_NICK)
    
    55
    +        if not nickname:
    
    56
    +            return
    
    57
    +        return nickname

  • nbxmpp/modules/presence.py
    1
    +# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
    
    2
    +#
    
    3
    +# This file is part of nbxmpp.
    
    4
    +#
    
    5
    +# This program is free software; you can redistribute it and/or
    
    6
    +# modify it under the terms of the GNU General Public License
    
    7
    +# as published by the Free Software Foundation; either version 3
    
    8
    +# of the License, or (at your option) any later version.
    
    9
    +#
    
    10
    +# This program is distributed in the hope that it will be useful,
    
    11
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    12
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    13
    +# GNU General Public License for more details.
    
    14
    +#
    
    15
    +# You should have received a copy of the GNU General Public License
    
    16
    +# along with this program; If not, see <http://www.gnu.org/licenses/>.
    
    17
    +
    
    18
    +import logging
    
    19
    +
    
    20
    +from nbxmpp.protocol import Error
    
    21
    +from nbxmpp.protocol import ERR_BAD_REQUEST
    
    22
    +from nbxmpp.protocol import NodeProcessed
    
    23
    +from nbxmpp.protocol import presence_types
    
    24
    +from nbxmpp.util import StanzaHandler
    
    25
    +
    
    26
    +log = logging.getLogger('nbxmpp.m.presence')
    
    27
    +
    
    28
    +
    
    29
    +class BasePresence:
    
    30
    +    def __init__(self, client):
    
    31
    +        self._client = client
    
    32
    +        self.handlers = [
    
    33
    +            StanzaHandler(name='presence',
    
    34
    +                          callback=self._process_presence_base,
    
    35
    +                          priority=10),
    
    36
    +        ]
    
    37
    +
    
    38
    +    def _process_presence_base(self, _con, stanza, properties):
    
    39
    +        properties.type = self._parse_type(stanza)
    
    40
    +        properties.priority = self._parse_priority(stanza)
    
    41
    +        properties.show = self._parse_show(stanza)
    
    42
    +        properties.jid = stanza.getFrom()
    
    43
    +        properties.id = stanza.getID()
    
    44
    +        properties.status = stanza.getStatus()
    
    45
    +
    
    46
    +        if properties.type == 'error':
    
    47
    +            properties.error_code = stanza.getErrorCode()
    
    48
    +            properties.error_message = stanza.getErrorMsg()
    
    49
    +
    
    50
    +        own_jid = self._client.get_bound_jid()
    
    51
    +        if own_jid == stanza.getFrom():
    
    52
    +            properties.self_presence = True
    
    53
    +
    
    54
    +    @staticmethod
    
    55
    +    def _parse_priority(stanza):
    
    56
    +        priority = stanza.getPriority()
    
    57
    +        if priority is None:
    
    58
    +            return 0
    
    59
    +
    
    60
    +        try:
    
    61
    +            priority = int(priority)
    
    62
    +        except Exception:
    
    63
    +            log.warning('Invalid priority value: %s', priority)
    
    64
    +            log.warning(stanza)
    
    65
    +            return 0
    
    66
    +
    
    67
    +        if priority not in range(-128, 127):
    
    68
    +            log.warning('Invalid priority value: %s', priority)
    
    69
    +            log.warning(stanza)
    
    70
    +            return 0
    
    71
    +
    
    72
    +        return priority
    
    73
    +
    
    74
    +    def _parse_type(self, stanza):
    
    75
    +        type_ = stanza.getType()
    
    76
    +        if type_ is None:
    
    77
    +            return
    
    78
    +
    
    79
    +        if type_ not in presence_types:
    
    80
    +            log.warning('Presence with invalid type received')
    
    81
    +            log.warning(stanza)
    
    82
    +            self._client.send(Error(stanza, ERR_BAD_REQUEST))
    
    83
    +            raise NodeProcessed
    
    84
    +        return type_
    
    85
    +
    
    86
    +    @staticmethod
    
    87
    +    def _parse_show(stanza):
    
    88
    +        show = stanza.getShow()
    
    89
    +        if show not in ('chat', 'away', 'xa', 'dnd'):
    
    90
    +            return
    
    91
    +        return show

  • nbxmpp/protocol.py
    ... ... @@ -496,6 +496,16 @@ _status_conditions = {
    496 496
         'removed-shutdown': 332,
    
    497 497
     }
    
    498 498
     
    
    499
    +presence_types = [
    
    500
    +    'error',
    
    501
    +    'probe',
    
    502
    +    'subscribe',
    
    503
    +    'subscribed',
    
    504
    +    'unavailable',
    
    505
    +    'unsubscribe',
    
    506
    +    'unsubscribed'
    
    507
    +]
    
    508
    +
    
    499 509
     STREAM_NOT_AUTHORIZED = 'urn:ietf:params:xml:ns:xmpp-streams not-authorized'
    
    500 510
     STREAM_REMOTE_CONNECTION_FAILED = 'urn:ietf:params:xml:ns:xmpp-streams remote-connection-failed'
    
    501 511
     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):
    50 50
         return result.decode()
    
    51 51
     
    
    52 52
     
    
    53
    -class PropertyBase:
    
    54
    -    def __init__(self):
    
    55
    -        self._data = {}
    
    56
    -
    
    57
    -    def __getattr__(self, key):
    
    58
    -        return self._data[key]
    
    53
    +class Properties:
    
    54
    +    pass
    
    59 55
     
    
    60
    -    def __setattr__(self, key, value):
    
    61
    -        if '_data' in key:
    
    62
    -            super().__setattr__(key, value)
    
    63
    -        else:
    
    64
    -            self._data[key] = value
    
    65 56
     
    
    66
    -
    
    67
    -class MessagePropertyDict(PropertyBase):
    
    57
    +class MessageProperties:
    
    68 58
         def __init__(self):
    
    69
    -        self._data = {
    
    70
    -            'carbon_type': None,
    
    71
    -            'eme': None,
    
    72
    -            'http_auth': None,
    
    73
    -        }
    
    59
    +        self.carbon_type = None
    
    60
    +        self.eme = None
    
    61
    +        self.http_auth = None
    
    62
    +        self.nickname = None
    
    74 63
     
    
    75 64
         @property
    
    76 65
         def is_http_auth(self):
    
    77
    -        return self._data['http_auth'] is not None
    
    66
    +        return self.http_auth is not None
    
    78 67
     
    
    79 68
     
    
    80
    -class IqPropertyDict(PropertyBase):
    
    69
    +class IqProperties:
    
    81 70
         def __init__(self):
    
    82
    -        self._data = {
    
    83
    -            'http_auth': None,
    
    84
    -        }
    
    71
    +        self.http_auth = None
    
    85 72
     
    
    86 73
         @property
    
    87 74
         def is_http_auth(self):
    
    88
    -        return self._data['http_auth'] is not None
    
    75
    +        return self.http_auth is not None
    
    89 76
     
    
    90 77
     
    
    91
    -def get_property_dict(name):
    
    78
    +class PresenceProperties:
    
    79
    +    def __init__(self):
    
    80
    +        self.type = None
    
    81
    +        self.priority = None
    
    82
    +        self.show = None
    
    83
    +        self.jid = None
    
    84
    +        self.resource = None
    
    85
    +        self.id = None
    
    86
    +        self.nickname = None
    
    87
    +        self.self_presence = False
    
    88
    +        self.from_muc = False
    
    89
    +        self.status = ''
    
    90
    +        self.error_message = ''
    
    91
    +        self.error_code = ''
    
    92
    +
    
    93
    +
    
    94
    +def get_properties_struct(name):
    
    92 95
         if name == 'message':
    
    93
    -        return MessagePropertyDict()
    
    96
    +        return MessageProperties()
    
    94 97
         if name == 'iq':
    
    95
    -        return IqPropertyDict()
    
    96
    -    return PropertyBase()
    
    98
    +        return IqProperties()
    
    99
    +    if name == 'presence':
    
    100
    +        return PresenceProperties()
    
    101
    +    return Properties()
    
    97 102
     
    
    98 103
     
    
    99 104
     def validate_jid(jid_string):