Redistribute responsibility: Let contact instances check if features are supported
This commit is contained in:
parent
3295b08b26
commit
c7ff97703f
|
@ -39,51 +39,30 @@ import gajim
|
||||||
import helpers
|
import helpers
|
||||||
|
|
||||||
from common.xmpp import NS_XHTML_IM, NS_RECEIPTS, NS_ESESSION, NS_CHATSTATES
|
from common.xmpp import NS_XHTML_IM, NS_RECEIPTS, NS_ESESSION, NS_CHATSTATES
|
||||||
|
|
||||||
# Features where we cannot safely assume that the other side supports them
|
# Features where we cannot safely assume that the other side supports them
|
||||||
FEATURE_BLACKLIST = [NS_CHATSTATES, NS_XHTML_IM, NS_RECEIPTS, NS_ESESSION]
|
FEATURE_BLACKLIST = [NS_CHATSTATES, NS_XHTML_IM, NS_RECEIPTS, NS_ESESSION]
|
||||||
|
|
||||||
|
|
||||||
class AbstractClientCaps(object):
|
class AbstractClientCaps(object):
|
||||||
'''
|
'''
|
||||||
Base class representing a client and its capabilities as advertised by
|
Base class representing a client and its capabilities as advertised by
|
||||||
a caps tag in a presence
|
a caps tag in a presence
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, caps_cache, caps_hash, node):
|
def __init__(self, caps_hash, node):
|
||||||
self._caps_cache = caps_cache
|
|
||||||
self._hash = caps_hash
|
self._hash = caps_hash
|
||||||
self._node = node
|
self._node = node
|
||||||
|
|
||||||
def query_client_of_jid_if_unknown(self, connection, jid):
|
def get_discover_strategy(self):
|
||||||
'''
|
return self._discover
|
||||||
Asynchronously query the give jid for its (node, ver, exts) caps data
|
|
||||||
using a disco query.
|
|
||||||
|
|
||||||
Query will only be sent if the data is not already cached.
|
|
||||||
'''
|
|
||||||
q = self._lookup_in_cache()
|
|
||||||
if q.queried == 0:
|
|
||||||
self._discover(connection, jid)
|
|
||||||
q.queried = 1
|
|
||||||
|
|
||||||
def contains_feature(self, requested_feature):
|
|
||||||
''' Returns true if these capabilities contain the given feature '''
|
|
||||||
cach_entry = self._lookup_in_cache()
|
|
||||||
supported_features = cach_entry.features
|
|
||||||
if requested_feature in supported_features:
|
|
||||||
return True
|
|
||||||
elif supported_features == [] and cach_entry.queried in (0, 1):
|
|
||||||
# assume feature is supported, if not blacklisted
|
|
||||||
return requested_feature not in FEATURE_BLACKLIST
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _discover(self, connection, jid):
|
def _discover(self, connection, jid):
|
||||||
''' To be implemented by subclassess '''
|
''' To be implemented by subclassess '''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def _lookup_in_cache(self):
|
def get_cache_lookup_strategy(self):
|
||||||
|
return self._lookup_in_cache
|
||||||
|
|
||||||
|
def _lookup_in_cache(self, caps_cache):
|
||||||
''' To be implemented by subclassess '''
|
''' To be implemented by subclassess '''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -91,13 +70,13 @@ class AbstractClientCaps(object):
|
||||||
class ClientCaps(AbstractClientCaps):
|
class ClientCaps(AbstractClientCaps):
|
||||||
''' The current XEP-115 implementation '''
|
''' The current XEP-115 implementation '''
|
||||||
|
|
||||||
def __init__(self, caps_cache, caps_hash, node, hash_method):
|
def __init__(self, caps_hash, node, hash_method):
|
||||||
AbstractClientCaps.__init__(self, caps_cache, caps_hash, node)
|
AbstractClientCaps.__init__(self, caps_hash, node)
|
||||||
assert hash_method != 'old'
|
assert hash_method != 'old'
|
||||||
self._hash_method = hash_method
|
self._hash_method = hash_method
|
||||||
|
|
||||||
def _lookup_in_cache(self):
|
def _lookup_in_cache(self, caps_cache):
|
||||||
return self._caps_cache[(self._hash_method, self._hash)]
|
return caps_cache[(self._hash_method, self._hash)]
|
||||||
|
|
||||||
def _discover(self, connection, jid):
|
def _discover(self, connection, jid):
|
||||||
connection.discoverInfo(jid, '%s#%s' % (self._node, self._hash))
|
connection.discoverInfo(jid, '%s#%s' % (self._node, self._hash))
|
||||||
|
@ -106,11 +85,11 @@ class ClientCaps(AbstractClientCaps):
|
||||||
class OldClientCaps(AbstractClientCaps):
|
class OldClientCaps(AbstractClientCaps):
|
||||||
''' Old XEP-115 implemtation. Kept around for background competability. '''
|
''' Old XEP-115 implemtation. Kept around for background competability. '''
|
||||||
|
|
||||||
def __init__(self, caps_cache, caps_hash, node):
|
def __init__(self, caps_hash, node):
|
||||||
AbstractClientCaps.__init__(self, caps_cache, caps_hash, node)
|
AbstractClientCaps.__init__(self, caps_hash, node)
|
||||||
|
|
||||||
def _lookup_in_cache(self):
|
def _lookup_in_cache(self, caps_cache):
|
||||||
return self._caps_cache[('old', self._node + '#' + self._hash)]
|
return caps_cache[('old', self._node + '#' + self._hash)]
|
||||||
|
|
||||||
def _discover(self, connection, jid):
|
def _discover(self, connection, jid):
|
||||||
connection.discoverInfo(jid)
|
connection.discoverInfo(jid)
|
||||||
|
@ -125,32 +104,19 @@ class NullClientCaps(AbstractClientCaps):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
AbstractClientCaps.__init__(self, None, None)
|
||||||
|
|
||||||
def query_client_of_jid_if_unknown(self, connection, jid):
|
def _lookup_in_cache(self, caps_cache):
|
||||||
pass
|
return caps_cache[('old', '')]
|
||||||
|
|
||||||
def contains_feature(self, feature):
|
def _discover(self, connection, jid):
|
||||||
return feature not in FEATURE_BLACKLIST
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CapsCache(object):
|
class CapsCache(object):
|
||||||
''' This object keeps the mapping between caps data and real disco
|
'''
|
||||||
|
This object keeps the mapping between caps data and real disco
|
||||||
features they represent, and provides simple way to query that info.
|
features they represent, and provides simple way to query that info.
|
||||||
|
|
||||||
It is application-wide, that is there's one object for all
|
|
||||||
connections.
|
|
||||||
|
|
||||||
Goals:
|
|
||||||
* handle storing/retrieving info from database
|
|
||||||
* cache info in memory
|
|
||||||
* expose simple interface
|
|
||||||
|
|
||||||
Properties:
|
|
||||||
* one object for all connections (move to logger.py?)
|
|
||||||
* store info efficiently (a set() of urls -- we can assume there won't be
|
|
||||||
too much of these, ensure that (X,Y,Z1) and (X,Y,Z2) has different
|
|
||||||
features.
|
|
||||||
'''
|
'''
|
||||||
def __init__(self, logger=None):
|
def __init__(self, logger=None):
|
||||||
''' Create a cache for entity capabilities. '''
|
''' Create a cache for entity capabilities. '''
|
||||||
|
@ -219,15 +185,14 @@ class CapsCache(object):
|
||||||
|
|
||||||
def update(self, identities, features):
|
def update(self, identities, features):
|
||||||
# NOTE: self refers to CapsCache object, not to CacheItem
|
# NOTE: self refers to CapsCache object, not to CacheItem
|
||||||
self.identities=identities
|
self.identities = identities
|
||||||
self.features=features
|
self.features = features
|
||||||
self._logger.add_caps_entry(self.hash_method, self.hash,
|
self._logger.add_caps_entry(self.hash_method, self.hash,
|
||||||
identities, features)
|
identities, features)
|
||||||
|
|
||||||
self.__CacheItem = CacheItem
|
self.__CacheItem = CacheItem
|
||||||
|
|
||||||
# prepopulate data which we are sure of; note: we do not log these info
|
# prepopulate data which we are sure of; note: we do not log these info
|
||||||
|
|
||||||
for account in gajim.connections:
|
for account in gajim.connections:
|
||||||
gajimcaps = self[('sha-1', gajim.caps_hash[account])]
|
gajimcaps = self[('sha-1', gajim.caps_hash[account])]
|
||||||
gajimcaps.identities = [gajim.gajim_identity]
|
gajimcaps.identities = [gajim.gajim_identity]
|
||||||
|
@ -257,48 +222,19 @@ class CapsCache(object):
|
||||||
self.__cache[(hash_method, hash_)] = x
|
self.__cache[(hash_method, hash_)] = x
|
||||||
return x
|
return x
|
||||||
|
|
||||||
def preload(self, con, jid, node, hash_method, hash_):
|
def query_client_of_jid_if_unknown(self, connection, jid, client_caps):
|
||||||
''' Preload data about (node, ver, exts) caps using disco
|
''' Preload data about (node, ver, exts) caps using disco
|
||||||
query to jid using proper connection. Don't query if
|
query to jid using proper connection. Don't query if
|
||||||
the data is already in cache. '''
|
the data is already in cache. '''
|
||||||
if hash_method == 'old':
|
lookup_cache_item = client_caps.get_cache_lookup_strategy()
|
||||||
q = self[(hash_method, node + '#' + hash_)]
|
q = lookup_cache_item(self)
|
||||||
else:
|
|
||||||
q = self[(hash_method, hash_)]
|
|
||||||
|
|
||||||
if q.queried==0:
|
if q.queried == 0:
|
||||||
# do query for bare node+hash pair
|
# do query for bare node+hash pair
|
||||||
# this will create proper object
|
# this will create proper object
|
||||||
q.queried=1
|
q.queried = 1
|
||||||
if hash_method == 'old':
|
discover = client_caps.get_discover_strategy()
|
||||||
con.discoverInfo(jid)
|
discover(connection, jid)
|
||||||
else:
|
|
||||||
con.discoverInfo(jid, '%s#%s' % (node, hash_))
|
|
||||||
|
|
||||||
def is_supported(self, contact, feature):
|
|
||||||
if not contact:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Unfortunately, if all resources are offline, the contact
|
|
||||||
# includes the last resource that was online. Check for its
|
|
||||||
# show, so we can be sure it's existant. Otherwise, we still
|
|
||||||
# return caps for a contact that has no resources left.
|
|
||||||
if contact.show == 'offline':
|
|
||||||
return False
|
|
||||||
|
|
||||||
# FIXME: We assume everything is supported if we got no caps.
|
|
||||||
# This is the "Asterix way", after 0.12 release, I will
|
|
||||||
# likely implement a fallback to disco (could be disabled
|
|
||||||
# for mobile users who pay for traffic)
|
|
||||||
if contact.caps_hash_method == 'old':
|
|
||||||
features = self[(contact.caps_hash_method, contact.caps_node + '#' + \
|
|
||||||
contact.caps_hash)].features
|
|
||||||
else:
|
|
||||||
features = self[(contact.caps_hash_method, contact.caps_hash)].features
|
|
||||||
if feature in features or features == []:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
gajim.capscache = CapsCache(gajim.logger)
|
gajim.capscache = CapsCache(gajim.logger)
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,15 @@
|
||||||
|
|
||||||
import common.gajim
|
import common.gajim
|
||||||
|
|
||||||
|
from common.caps import NullClientCaps, FEATURE_BLACKLIST
|
||||||
|
|
||||||
|
|
||||||
class Contact(object):
|
class Contact(object):
|
||||||
'''Information concerning each contact'''
|
'''Information concerning each contact'''
|
||||||
def __init__(self, jid='', name='', groups=[], show='', status='', sub='',
|
def __init__(self, jid='', name='', groups=[], show='', status='', sub='',
|
||||||
ask='', resource='', priority=0, keyID='', caps_node=None,
|
ask='', resource='', priority=0, keyID='', client_caps=None, caps_cache=None,
|
||||||
caps_hash_method=None, caps_hash=None, our_chatstate=None, chatstate=None,
|
our_chatstate=None, chatstate=None, last_status_time=None, msg_id = None,
|
||||||
last_status_time=None, msg_id = None, composing_xep = None, mood={}, tune={},
|
composing_xep = None, mood={}, tune={},
|
||||||
activity={}):
|
activity={}):
|
||||||
self.jid = jid
|
self.jid = jid
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -49,11 +52,9 @@ class Contact(object):
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.keyID = keyID
|
self.keyID = keyID
|
||||||
|
|
||||||
# Capabilities; filled by caps.py/ConnectionCaps object
|
# Entity Capabilities
|
||||||
# every time it gets these from presence stanzas
|
self._client_caps = client_caps or NullClientCaps()
|
||||||
self.caps_node = caps_node
|
self._caps_cache = caps_cache or common.gajim.capscache
|
||||||
self.caps_hash_method = caps_hash_method
|
|
||||||
self.caps_hash = caps_hash
|
|
||||||
|
|
||||||
# please read xep-85 http://www.xmpp.org/extensions/xep-0085.html
|
# please read xep-85 http://www.xmpp.org/extensions/xep-0085.html
|
||||||
# we keep track of xep85 support with the peer by three extra states:
|
# we keep track of xep85 support with the peer by three extra states:
|
||||||
|
@ -135,20 +136,7 @@ class Contact(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _set_supported_caps(self, value):
|
def supports(self, requested_feature):
|
||||||
'''
|
|
||||||
Set an EntityCapabilities object
|
|
||||||
'''
|
|
||||||
self._caps = value
|
|
||||||
|
|
||||||
def _get_supported_caps(self):
|
|
||||||
'''
|
|
||||||
Returns a function which delegates to the EntityCapabilites support checker
|
|
||||||
|
|
||||||
This allows easy checks like:
|
|
||||||
if contact.supports(NS_COOL_FEATURE): ...
|
|
||||||
'''
|
|
||||||
def test(feature):
|
|
||||||
if self.show == 'offline':
|
if self.show == 'offline':
|
||||||
# Unfortunately, if all resources are offline, the contact
|
# Unfortunately, if all resources are offline, the contact
|
||||||
# includes the last resource that was online. Check for its
|
# includes the last resource that was online. Check for its
|
||||||
|
@ -156,10 +144,25 @@ class Contact(object):
|
||||||
# return caps for a contact that has no resources left.
|
# return caps for a contact that has no resources left.
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return self._caps.contains_feature(feature)
|
return self._client_supports(requested_feature)
|
||||||
return test
|
|
||||||
|
|
||||||
supports = property(_get_supported_caps, _set_supported_caps)
|
def _client_supports(self, requested_feature):
|
||||||
|
lookup_item = self._client_caps.get_cache_lookup_strategy()
|
||||||
|
cache_item = lookup_item(self._caps_cache)
|
||||||
|
|
||||||
|
supported_features = cache_item.features
|
||||||
|
if requested_feature in supported_features:
|
||||||
|
return True
|
||||||
|
elif supported_features == [] and cache_item.queried in (0, 1):
|
||||||
|
# assume feature is supported, if we don't know yet, what the client
|
||||||
|
# is capable of
|
||||||
|
return requested_feature not in FEATURE_BLACKLIST
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_supported_client_caps(self, client_caps):
|
||||||
|
''' Set an EntityCapabilities object '''
|
||||||
|
self._client_caps = client_caps
|
||||||
|
|
||||||
|
|
||||||
class GC_Contact:
|
class GC_Contact:
|
||||||
|
@ -175,9 +178,11 @@ class GC_Contact:
|
||||||
self.affiliation = affiliation
|
self.affiliation = affiliation
|
||||||
self.jid = jid
|
self.jid = jid
|
||||||
self.resource = resource
|
self.resource = resource
|
||||||
self.caps_node = None
|
|
||||||
self.caps_hash_method = None
|
# Entity Capabilities
|
||||||
self.caps_hash = None
|
self._client_caps = NullClientCaps()
|
||||||
|
self._caps_cache = common.gajim.capscache
|
||||||
|
|
||||||
self.our_chatstate = our_chatstate
|
self.our_chatstate = our_chatstate
|
||||||
self.composing_xep = composing_xep
|
self.composing_xep = composing_xep
|
||||||
self.chatstate = chatstate
|
self.chatstate = chatstate
|
||||||
|
@ -188,20 +193,7 @@ class GC_Contact:
|
||||||
def get_shown_name(self):
|
def get_shown_name(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def _set_supported_caps(self, value):
|
def supports(self, requested_feature):
|
||||||
'''
|
|
||||||
Set an EntityCapabilities object
|
|
||||||
'''
|
|
||||||
self._caps = value
|
|
||||||
|
|
||||||
def _get_supported_caps(self):
|
|
||||||
'''
|
|
||||||
Returns a function which delegates to the EntityCapabilites support checker
|
|
||||||
|
|
||||||
This allows easy checks like:
|
|
||||||
if contact.supports(NS_COOL_FEATURE): ...
|
|
||||||
'''
|
|
||||||
def test(feature):
|
|
||||||
if self.show == 'offline':
|
if self.show == 'offline':
|
||||||
# Unfortunately, if all resources are offline, the contact
|
# Unfortunately, if all resources are offline, the contact
|
||||||
# includes the last resource that was online. Check for its
|
# includes the last resource that was online. Check for its
|
||||||
|
@ -209,10 +201,25 @@ class GC_Contact:
|
||||||
# return caps for a contact that has no resources left.
|
# return caps for a contact that has no resources left.
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return self._caps.contains_feature(feature)
|
return self._client_supports(requested_feature)
|
||||||
return test
|
|
||||||
|
|
||||||
supports = property(_get_supported_caps, _set_supported_caps)
|
def _client_supports(self, requested_feature):
|
||||||
|
lookup_item = self._client_caps.get_cache_lookup_strategy()
|
||||||
|
cache_item = lookup_item(self._caps_cache)
|
||||||
|
|
||||||
|
supported_features = cache_item.features
|
||||||
|
if requested_feature in supported_features:
|
||||||
|
return True
|
||||||
|
elif supported_features == [] and cache_item.queried in (0, 1):
|
||||||
|
# assume feature is supported, if we don't know yet, what the client
|
||||||
|
# is capable of
|
||||||
|
return requested_feature not in FEATURE_BLACKLIST
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_supported_client_caps(self, client_caps):
|
||||||
|
''' Set an EntityCapabilities object '''
|
||||||
|
self._client_caps = client_caps
|
||||||
|
|
||||||
class Contacts:
|
class Contacts:
|
||||||
'''Information concerning all contacts and groupchat contacts'''
|
'''Information concerning all contacts and groupchat contacts'''
|
||||||
|
@ -246,8 +253,8 @@ class Contacts:
|
||||||
del self._metacontacts_tags[account]
|
del self._metacontacts_tags[account]
|
||||||
|
|
||||||
def create_contact(self, jid='', name='', groups=[], show='', status='',
|
def create_contact(self, jid='', name='', groups=[], show='', status='',
|
||||||
sub='', ask='', resource='', priority=0, keyID='', caps_node=None,
|
sub='', ask='', resource='', priority=0, keyID='', client_caps=None,
|
||||||
caps_hash_method=None, caps_hash=None, our_chatstate=None,
|
caps_cache=None, our_chatstate=None,
|
||||||
chatstate=None, last_status_time=None, composing_xep=None,
|
chatstate=None, last_status_time=None, composing_xep=None,
|
||||||
mood={}, tune={}, activity={}):
|
mood={}, tune={}, activity={}):
|
||||||
|
|
||||||
|
@ -259,8 +266,8 @@ class Contacts:
|
||||||
|
|
||||||
return Contact(jid=jid, name=name, groups=groups_unique, show=show,
|
return Contact(jid=jid, name=name, groups=groups_unique, show=show,
|
||||||
status=status, sub=sub, ask=ask, resource=resource, priority=priority,
|
status=status, sub=sub, ask=ask, resource=resource, priority=priority,
|
||||||
keyID=keyID, caps_node=caps_node, caps_hash_method=caps_hash_method,
|
keyID=keyID, client_caps=client_caps, caps_cache=caps_cache,
|
||||||
caps_hash=caps_hash, our_chatstate=our_chatstate, chatstate=chatstate,
|
our_chatstate=our_chatstate, chatstate=chatstate,
|
||||||
last_status_time=last_status_time, composing_xep=composing_xep,
|
last_status_time=last_status_time, composing_xep=composing_xep,
|
||||||
mood=mood, tune=tune, activity=activity)
|
mood=mood, tune=tune, activity=activity)
|
||||||
|
|
||||||
|
@ -269,8 +276,8 @@ class Contacts:
|
||||||
groups=contact.groups, show=contact.show, status=contact.status,
|
groups=contact.groups, show=contact.show, status=contact.status,
|
||||||
sub=contact.sub, ask=contact.ask, resource=contact.resource,
|
sub=contact.sub, ask=contact.ask, resource=contact.resource,
|
||||||
priority=contact.priority, keyID=contact.keyID,
|
priority=contact.priority, keyID=contact.keyID,
|
||||||
caps_node=contact.caps_node, caps_hash_method=contact.caps_hash_method,
|
client_caps=contact._client_caps, caps_cache=contact._caps_cache,
|
||||||
caps_hash=contact.caps_hash, our_chatstate=contact.our_chatstate,
|
our_chatstate=contact.our_chatstate,
|
||||||
chatstate=contact.chatstate, last_status_time=contact.last_status_time)
|
chatstate=contact.chatstate, last_status_time=contact.last_status_time)
|
||||||
|
|
||||||
def add_contact(self, account, contact):
|
def add_contact(self, account, contact):
|
||||||
|
@ -640,9 +647,8 @@ class Contacts:
|
||||||
jid = gc_contact.get_full_jid()
|
jid = gc_contact.get_full_jid()
|
||||||
return Contact(jid=jid, resource=gc_contact.resource,
|
return Contact(jid=jid, resource=gc_contact.resource,
|
||||||
name=gc_contact.name, groups=[], show=gc_contact.show,
|
name=gc_contact.name, groups=[], show=gc_contact.show,
|
||||||
status=gc_contact.status, sub='none', caps_node=gc_contact.caps_node,
|
status=gc_contact.status, sub='none', client_caps=gc_contact._client_caps,
|
||||||
caps_hash_method=gc_contact.caps_hash_method,
|
caps_cache=gc_contact._caps_cache)
|
||||||
caps_hash=gc_contact.caps_hash)
|
|
||||||
|
|
||||||
def create_gc_contact(self, room_jid='', name='', show='', status='',
|
def create_gc_contact(self, room_jid='', name='', show='', status='',
|
||||||
role='', affiliation='', jid='', resource=''):
|
role='', affiliation='', jid='', resource=''):
|
||||||
|
|
|
@ -32,8 +32,6 @@ import logging
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
import config
|
import config
|
||||||
from contacts import Contacts
|
|
||||||
from events import Events
|
|
||||||
import xmpp
|
import xmpp
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -101,6 +99,9 @@ else:
|
||||||
|
|
||||||
os_info = None # used to cache os information
|
os_info = None # used to cache os information
|
||||||
|
|
||||||
|
from contacts import Contacts
|
||||||
|
from events import Events
|
||||||
|
|
||||||
gmail_domains = ['gmail.com', 'googlemail.com']
|
gmail_domains = ['gmail.com', 'googlemail.com']
|
||||||
|
|
||||||
transport_type = {} # list the type of transport
|
transport_type = {} # list the type of transport
|
||||||
|
|
|
@ -6,11 +6,11 @@ import unittest
|
||||||
import lib
|
import lib
|
||||||
lib.setup_env()
|
lib.setup_env()
|
||||||
|
|
||||||
|
from common import gajim
|
||||||
from common import helpers
|
from common import helpers
|
||||||
from common.contacts import Contact
|
|
||||||
from common.xmpp import NS_MUC, NS_PING, NS_XHTML_IM
|
from common.xmpp import NS_MUC, NS_PING, NS_XHTML_IM
|
||||||
|
|
||||||
from common.caps import CapsCache, ClientCaps, OldClientCaps
|
from common.caps import CapsCache, ClientCaps, OldClientCaps
|
||||||
|
from common.contacts import Contact
|
||||||
|
|
||||||
from mock import Mock
|
from mock import Mock
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ class CommonCapsTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.caps_method = 'sha-1'
|
self.caps_method = 'sha-1'
|
||||||
self.caps_hash = 'm3P2WeXPMGVH2tZPe7yITnfY0Dw='
|
self.caps_hash = 'm3P2WeXPMGVH2tZPe7yITnfY0Dw='
|
||||||
self.caps = (self.caps_method, self.caps_hash)
|
self.client_caps = (self.caps_method, self.caps_hash)
|
||||||
|
|
||||||
self.node = "http://gajim.org"
|
self.node = "http://gajim.org"
|
||||||
self.identity = {'category': 'client', 'type': 'pc', 'name':'Gajim'}
|
self.identity = {'category': 'client', 'type': 'pc', 'name':'Gajim'}
|
||||||
|
@ -35,6 +35,10 @@ class CommonCapsTest(unittest.TestCase):
|
||||||
self.logger = Mock(returnValues={"iter_caps_data":db_caps_cache})
|
self.logger = Mock(returnValues={"iter_caps_data":db_caps_cache})
|
||||||
|
|
||||||
self.cc = CapsCache(self.logger)
|
self.cc = CapsCache(self.logger)
|
||||||
|
# This is a temporary hack required by the way contacts rely on the
|
||||||
|
# existance of a cache. Hopefully this can be refactored to work via
|
||||||
|
# dependency injection
|
||||||
|
gajim.capscache = self.cc
|
||||||
|
|
||||||
|
|
||||||
class TestCapsCache(CommonCapsTest):
|
class TestCapsCache(CommonCapsTest):
|
||||||
|
@ -42,13 +46,13 @@ class TestCapsCache(CommonCapsTest):
|
||||||
def test_set_retrieve(self):
|
def test_set_retrieve(self):
|
||||||
''' Test basic set / retrieve cycle '''
|
''' Test basic set / retrieve cycle '''
|
||||||
|
|
||||||
self.cc[self.caps].identities = self.identities
|
self.cc[self.client_caps].identities = self.identities
|
||||||
self.cc[self.caps].features = self.features
|
self.cc[self.client_caps].features = self.features
|
||||||
|
|
||||||
self.assert_(NS_MUC in self.cc[self.caps].features)
|
self.assert_(NS_MUC in self.cc[self.client_caps].features)
|
||||||
self.assert_(NS_PING not in self.cc[self.caps].features)
|
self.assert_(NS_PING not in self.cc[self.client_caps].features)
|
||||||
|
|
||||||
identities = self.cc[self.caps].identities
|
identities = self.cc[self.client_caps].identities
|
||||||
|
|
||||||
self.assertEqual(1, len(identities))
|
self.assertEqual(1, len(identities))
|
||||||
|
|
||||||
|
@ -57,9 +61,9 @@ class TestCapsCache(CommonCapsTest):
|
||||||
self.assertEqual('pc', identity['type'])
|
self.assertEqual('pc', identity['type'])
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
''' Test caps update gets logged into db '''
|
''' Test client_caps update gets logged into db '''
|
||||||
|
|
||||||
item = self.cc[self.caps]
|
item = self.cc[self.client_caps]
|
||||||
item.update(self.identities, self.features)
|
item.update(self.identities, self.features)
|
||||||
|
|
||||||
self.logger.mockCheckCall(0, "add_caps_entry", self.caps_method,
|
self.logger.mockCheckCall(0, "add_caps_entry", self.caps_method,
|
||||||
|
@ -67,46 +71,31 @@ class TestCapsCache(CommonCapsTest):
|
||||||
|
|
||||||
def test_initialize_from_db(self):
|
def test_initialize_from_db(self):
|
||||||
''' Read cashed dummy data from db '''
|
''' Read cashed dummy data from db '''
|
||||||
self.assertEqual(self.cc[self.caps].queried, 0)
|
self.assertEqual(self.cc[self.client_caps].queried, 0)
|
||||||
self.cc.initialize_from_db()
|
self.cc.initialize_from_db()
|
||||||
self.assertEqual(self.cc[self.caps].queried, 2)
|
self.assertEqual(self.cc[self.client_caps].queried, 2)
|
||||||
|
|
||||||
def test_preload_triggering_query(self):
|
def test_preload_triggering_query(self):
|
||||||
''' Make sure that preload issues a disco '''
|
''' Make sure that preload issues a disco '''
|
||||||
connection = Mock()
|
connection = Mock()
|
||||||
|
client_caps = ClientCaps(self.caps_hash, self.node, self.caps_method)
|
||||||
|
|
||||||
self.cc.preload(connection, "test@gajim.org", self.node,
|
self.cc.query_client_of_jid_if_unknown(connection, "test@gajim.org",
|
||||||
self.caps_method, self.caps_hash)
|
client_caps)
|
||||||
|
|
||||||
connection.mockCheckCall(0, "discoverInfo", "test@gajim.org",
|
self.assertEqual(1, len(connection.mockGetAllCalls()))
|
||||||
"http://gajim.org#m3P2WeXPMGVH2tZPe7yITnfY0Dw=")
|
|
||||||
|
|
||||||
def test_no_preload_query_if_cashed(self):
|
def test_no_preload_query_if_cashed(self):
|
||||||
''' Preload must not send a query if the data is already cached '''
|
''' Preload must not send a query if the data is already cached '''
|
||||||
connection = Mock()
|
connection = Mock()
|
||||||
|
client_caps = ClientCaps(self.caps_hash, self.node, self.caps_method)
|
||||||
|
|
||||||
self.cc.initialize_from_db()
|
self.cc.initialize_from_db()
|
||||||
self.cc.preload(connection, "test@gajim.org", self.node,
|
self.cc.query_client_of_jid_if_unknown(connection, "test@gajim.org",
|
||||||
self.caps_method, self.caps_hash)
|
client_caps)
|
||||||
|
|
||||||
self.assertEqual(0, len(connection.mockGetAllCalls()))
|
self.assertEqual(0, len(connection.mockGetAllCalls()))
|
||||||
|
|
||||||
def test_is_supported(self):
|
|
||||||
contact = Contact(caps_node=self.node,
|
|
||||||
caps_hash_method=self.caps_method,
|
|
||||||
caps_hash=self.caps_hash)
|
|
||||||
|
|
||||||
self.assertTrue(self.cc.is_supported(contact, NS_PING),
|
|
||||||
msg="Assume everything is supported, if we don't have caps")
|
|
||||||
|
|
||||||
self.cc.initialize_from_db()
|
|
||||||
|
|
||||||
self.assertFalse(self.cc.is_supported(contact, NS_PING),
|
|
||||||
msg="Must return false on unsupported feature")
|
|
||||||
|
|
||||||
self.assertTrue(self.cc.is_supported(contact, NS_MUC),
|
|
||||||
msg="Must return True on supported feature")
|
|
||||||
|
|
||||||
def test_hash(self):
|
def test_hash(self):
|
||||||
'''tests the hash computation'''
|
'''tests the hash computation'''
|
||||||
computed_hash = helpers.compute_caps_hash(self.identities, self.features)
|
computed_hash = helpers.compute_caps_hash(self.identities, self.features)
|
||||||
|
@ -117,41 +106,36 @@ class TestClientCaps(CommonCapsTest):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
CommonCapsTest.setUp(self)
|
CommonCapsTest.setUp(self)
|
||||||
self.caps = ClientCaps(self.cc, self.caps_hash, self.node,
|
self.client_caps = ClientCaps(self.caps_hash, self.node, self.caps_method)
|
||||||
self.caps_method)
|
|
||||||
|
|
||||||
def test_no_query_client_of_jid(self):
|
def test_query_by_get_discover_strategy(self):
|
||||||
''' Client must not be queried if the data is already cached '''
|
|
||||||
connection = Mock()
|
|
||||||
self.cc.initialize_from_db()
|
|
||||||
self.caps.query_client_of_jid_if_unknown(connection, "test@gajim.org")
|
|
||||||
|
|
||||||
self.assertEqual(0, len(connection.mockGetAllCalls()))
|
|
||||||
|
|
||||||
def test_query_client_of_jid_if_unknown(self):
|
|
||||||
''' Client must be queried if the data is unkown '''
|
''' Client must be queried if the data is unkown '''
|
||||||
connection = Mock()
|
connection = Mock()
|
||||||
self.caps.query_client_of_jid_if_unknown(connection, "test@gajim.org")
|
discover = self.client_caps.get_discover_strategy()
|
||||||
|
discover(connection, "test@gajim.org")
|
||||||
|
|
||||||
connection.mockCheckCall(0, "discoverInfo", "test@gajim.org",
|
connection.mockCheckCall(0, "discoverInfo", "test@gajim.org",
|
||||||
"http://gajim.org#m3P2WeXPMGVH2tZPe7yITnfY0Dw=")
|
"http://gajim.org#m3P2WeXPMGVH2tZPe7yITnfY0Dw=")
|
||||||
|
|
||||||
def test_is_supported(self):
|
def test_client_supports(self):
|
||||||
self.assertTrue(self.caps.contains_feature(NS_PING),
|
contact = Contact(caps_cache=self.cc)
|
||||||
|
contact.set_supported_client_caps(self.client_caps)
|
||||||
|
|
||||||
|
self.assertTrue(contact.supports(NS_PING),
|
||||||
msg="Assume supported, if we don't have caps")
|
msg="Assume supported, if we don't have caps")
|
||||||
|
|
||||||
self.assertFalse(self.caps.contains_feature(NS_XHTML_IM),
|
self.assertFalse(contact.supports(NS_XHTML_IM),
|
||||||
msg="Must not assume blacklisted feature is supported on default")
|
msg="Must not assume blacklisted feature is supported on default")
|
||||||
|
|
||||||
self.cc.initialize_from_db()
|
self.cc.initialize_from_db()
|
||||||
|
|
||||||
self.assertFalse(self.caps.contains_feature(NS_PING),
|
self.assertFalse(contact.supports(NS_PING),
|
||||||
msg="Must return false on unsupported feature")
|
msg="Must return false on unsupported feature")
|
||||||
|
|
||||||
self.assertTrue(self.caps.contains_feature(NS_XHTML_IM),
|
self.assertTrue(contact.supports(NS_XHTML_IM),
|
||||||
msg="Must return True on supported feature")
|
msg="Must return True on supported feature")
|
||||||
|
|
||||||
self.assertTrue(self.caps.contains_feature(NS_MUC),
|
self.assertTrue(contact.supports(NS_MUC),
|
||||||
msg="Must return True on supported feature")
|
msg="Must return True on supported feature")
|
||||||
|
|
||||||
|
|
||||||
|
@ -159,25 +143,17 @@ class TestOldClientCaps(TestClientCaps):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
TestClientCaps.setUp(self)
|
TestClientCaps.setUp(self)
|
||||||
self.caps = OldClientCaps(self.cc, self.caps_hash, self.node)
|
self.client_caps = OldClientCaps(self.caps_hash, self.node)
|
||||||
|
|
||||||
def test_no_query_client_of_jid(self):
|
def test_query_by_get_discover_strategy(self):
|
||||||
''' Client must not be queried if the data is already cached '''
|
''' Client must be queried if the data is unknown '''
|
||||||
connection = Mock()
|
connection = Mock()
|
||||||
self.cc.initialize_from_db()
|
discover = self.client_caps.get_discover_strategy()
|
||||||
self.caps.query_client_of_jid_if_unknown(connection, "test@gajim.org")
|
discover(connection, "test@gajim.org")
|
||||||
|
|
||||||
self.assertEqual(0, len(connection.mockGetAllCalls()))
|
|
||||||
|
|
||||||
def test_query_client_of_jid_if_unknown(self):
|
|
||||||
''' Client must be queried if the data is unkown '''
|
|
||||||
connection = Mock()
|
|
||||||
self.caps.query_client_of_jid_if_unknown(connection, "test@gajim.org")
|
|
||||||
|
|
||||||
connection.mockCheckCall(0, "discoverInfo", "test@gajim.org")
|
connection.mockCheckCall(0, "discoverInfo", "test@gajim.org")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
|
|
|
@ -6,36 +6,43 @@ import unittest
|
||||||
import lib
|
import lib
|
||||||
lib.setup_env()
|
lib.setup_env()
|
||||||
|
|
||||||
from common.contacts import Contact
|
from common.contacts import Contact, GC_Contact
|
||||||
from common.caps import NullClientCaps
|
from common.caps import NullClientCaps
|
||||||
|
from common.xmpp import NS_MUC
|
||||||
|
|
||||||
from mock import Mock
|
class TestCommonContact(unittest.TestCase):
|
||||||
|
|
||||||
class TestContact(unittest.TestCase):
|
def setUp(self):
|
||||||
|
self.contact = Contact()
|
||||||
|
|
||||||
def test_supports(self):
|
def test_default_client_supports(self):
|
||||||
''' Test the Entity Capabilities part of the contact instance '''
|
'''
|
||||||
|
Test the caps support method of contacts.
|
||||||
|
See test_caps for more enhanced tests.
|
||||||
|
'''
|
||||||
|
|
||||||
NS_MUC = 'http://jabber.org/protocol/muc'
|
self.assertTrue(self.contact.supports(NS_MUC),
|
||||||
|
msg="Must not backtrace on simple check for supported feature")
|
||||||
|
|
||||||
# Test with mocks to get basic set/get property behaviour checked
|
client_caps = NullClientCaps()
|
||||||
all_supported_mock_entity_caps = Mock(
|
self.contact.set_supported_client_caps(client_caps)
|
||||||
returnValues={"contains_feature": True})
|
|
||||||
nothing_supported_mock_entity_caps = Mock(
|
|
||||||
returnValues={"contains_feature": False})
|
|
||||||
|
|
||||||
contact = Contact()
|
self.assertTrue(self.contact.supports(NS_MUC),
|
||||||
|
msg="Must not backtrace on simple check for supported feature")
|
||||||
|
|
||||||
contact.supports = all_supported_mock_entity_caps
|
|
||||||
self.assertTrue(contact.supports(NS_MUC))
|
|
||||||
|
|
||||||
contact.supports = nothing_supported_mock_entity_caps
|
class TestContact(TestCommonContact):
|
||||||
self.assertFalse(contact.supports(NS_MUC))
|
|
||||||
|
|
||||||
# Test with EntityCapabilites to detect API changes
|
def setUp(self):
|
||||||
contact.supports = NullClientCaps()
|
TestCommonContact.setUp(self)
|
||||||
self.assertTrue(contact.supports(NS_MUC),
|
self.contact = Contact()
|
||||||
msg="Default behaviour is to support everything on unknown caps")
|
|
||||||
|
|
||||||
|
class TestGC_Contact(TestCommonContact):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
TestCommonContact.setUp(self)
|
||||||
|
self.contact = GC_Contact()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in New Issue