From ca0bcbb52725f59cd3227c1e782d4d45f359ab42 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Tue, 4 Jan 2011 15:52:37 +0100 Subject: [PATCH] fallback to disco if clients don't support caps. Fixes #4071 --- src/common/caps_cache.py | 42 +++++++++++++++++++--- src/common/connection_handlers_events.py | 45 +++++++++++++----------- src/common/protocol/caps.py | 11 ++++-- 3 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/common/caps_cache.py b/src/common/caps_cache.py index af69c8aeb..cf14bfadd 100644 --- a/src/common/caps_cache.py +++ b/src/common/caps_cache.py @@ -75,14 +75,17 @@ def client_supports(client_caps, requested_feature): else: return False -def create_suitable_client_caps(node, caps_hash, hash_method): +def create_suitable_client_caps(node, caps_hash, hash_method, fjid=None): """ Create and return a suitable ClientCaps object for the given node, caps_hash, hash_method combination. """ if not node or not caps_hash: - # improper caps, ignore client capabilities. - client_caps = NullClientCaps() + if fjid: + client_caps = NoClientCaps(fjid) + else: + # improper caps, ignore client capabilities. + client_caps = NullClientCaps() elif not hash_method: client_caps = OldClientCaps(caps_hash, node) else: @@ -172,6 +175,7 @@ class AbstractClientCaps(object): def __init__(self, caps_hash, node): self._hash = caps_hash self._node = node + self._hash_method = None def get_discover_strategy(self): return self._discover @@ -228,6 +232,7 @@ class OldClientCaps(AbstractClientCaps): """ def __init__(self, caps_hash, node): AbstractClientCaps.__init__(self, caps_hash, node) + self._hash_method = 'old' def _lookup_in_cache(self, caps_cache): return caps_cache[('old', self._node + '#' + self._hash)] @@ -238,6 +243,22 @@ class OldClientCaps(AbstractClientCaps): def _is_hash_valid(self, identities, features, dataforms): return True +class NoClientCaps(AbstractClientCaps): + """ + For clients that don't support XEP-0115 + """ + def __init__(self, fjid): + AbstractClientCaps.__init__(self, fjid, fjid) + self._hash_method = 'no' + + def _lookup_in_cache(self, caps_cache): + return caps_cache[('no', self._node)] + + def _discover(self, connection, jid): + connection.discoverInfo(jid) + + def _is_hash_valid(self, identities, features, dataforms): + return True class NullClientCaps(AbstractClientCaps): """ @@ -258,6 +279,7 @@ class NullClientCaps(AbstractClientCaps): def __init__(self): AbstractClientCaps.__init__(self, None, None) + self._hash_method = 'dummy' def _lookup_in_cache(self, caps_cache): # lookup something which does not exist to get a new CacheItem created @@ -341,14 +363,17 @@ class CapsCache(object): def set_and_store(self, identities, features): self.identities = identities self.features = features - self._logger.add_caps_entry(self.hash_method, self.hash, + if self.hash_method != 'no': + self._logger.add_caps_entry(self.hash_method, self.hash, identities, features) self.status = CACHED def update_last_seen(self): if not self._recently_seen: self._recently_seen = True - self._logger.update_caps_time(self.hash_method, self.hash) + if self.hash_method != 'no': + self._logger.update_caps_time(self.hash_method, + self.hash) def is_valid(self): """ @@ -401,3 +426,10 @@ class CapsCache(object): discover(connection, jid) else: q.update_last_seen() + + def forget_caps(self, client_caps): + hash_method = client_caps._hash_method + hash = client_caps._hash + key = (hash_method, hash) + if key in self.__cache: + del self.__cache[key] diff --git a/src/common/connection_handlers_events.py b/src/common/connection_handlers_events.py index 476f05b70..7a931a81d 100644 --- a/src/common/connection_handlers_events.py +++ b/src/common/connection_handlers_events.py @@ -660,7 +660,27 @@ class StreamConflictReceivedEvent(nec.NetworkIncomingEvent): self.conn = self.base_event.conn return True -class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): +class PresenceHelperEvent: + def _generate_show(self): + self.show = self.stanza.getShow() + if self.show not in ('chat', 'away', 'xa', 'dnd'): + self.show = '' # We ignore unknown show + if not self.ptype and not self.show: + self.show = 'online' + elif self.ptype == 'unavailable': + self.show = 'offline' + + def _generate_ptype(self): + self.ptype = self.stanza.getType() + if self.ptype == 'available': + self.ptype = None + rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed', + 'unsubscribe', 'unsubscribed') + if self.ptype and not self.ptype in rfc_types: + self.ptype = None + +class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent, +PresenceHelperEvent): name = 'presence-received' base_network_events = ['raw-pres-received'] @@ -674,15 +694,6 @@ class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.keyID = helpers.prepare_and_validate_gpg_keyID(self.conn.name, self.jid, self.keyID) - def _generate_show(self): - self.show = self.stanza.getShow() - if self.show not in ('chat', 'away', 'xa', 'dnd'): - self.show = '' # We ignore unknown show - if not self.ptype and not self.show: - self.show = 'online' - elif self.ptype == 'unavailable': - self.show = 'offline' - def _generate_prio(self): self.prio = self.stanza.getPriority() try: @@ -690,15 +701,6 @@ class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): except Exception: self.prio = 0 - def _generate_ptype(self): - self.ptype = self.stanza.getType() - if self.ptype == 'available': - self.ptype = None - rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed', - 'unsubscribe', 'unsubscribed') - if self.ptype and not self.ptype in rfc_types: - self.ptype = None - def generate(self): self.conn = self.base_event.conn self.stanza = self.base_event.stanza @@ -1489,7 +1491,8 @@ class PingErrorEvent(nec.NetworkIncomingEvent): name = 'ping-error' base_network_events = [] -class CapsPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): +class CapsPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent, +PresenceHelperEvent): name = 'caps-presence-received' base_network_events = ['raw-pres-received'] @@ -1509,6 +1512,8 @@ class CapsPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.get_jid_resource() except Exception: return + self._generate_ptype() + self._generate_show() self._extract_caps_from_presence() return True diff --git a/src/common/protocol/caps.py b/src/common/protocol/caps.py index 340ff743d..78880bf87 100644 --- a/src/common/protocol/caps.py +++ b/src/common/protocol/caps.py @@ -53,9 +53,14 @@ class ConnectionCaps(object): if obj.conn.name != self._account: return obj.client_caps = self._create_suitable_client_caps(obj.node, - obj.caps_hash, obj.hash_method) - self._capscache.query_client_of_jid_if_unknown(self, obj.fjid, - obj.client_caps) + obj.caps_hash, obj.hash_method, obj.fjid) + if obj.show == 'offline' and obj.client_caps._hash_method == 'no': + self._capscache.forget_caps(obj.client_caps) + obj.client_caps = self._create_suitable_client_caps(obj.node, + obj.caps_hash, obj.hash_method) + else: + self._capscache.query_client_of_jid_if_unknown(self, obj.fjid, + obj.client_caps) self._update_client_caps_of_contact(obj) def _update_client_caps_of_contact(self, obj):