Philipp Hörist pushed to branch master at gajim / python-nbxmpp
Commits:
-
5f8368e5
by Philipp Hörist at 2018-12-21T22:33:14Z
-
8a87c2be
by Philipp Hörist at 2018-12-21T23:34:08Z
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:
| ... | ... | @@ -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
|
| 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
|
| 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
|
| 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
|
| ... | ... | @@ -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'
|
| ... | ... | @@ -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):
|