Caps: caps are queried, results are stored in cache.

This commit is contained in:
Tomasz Melcer 2007-06-30 17:31:27 +00:00
parent 31f3e2f44a
commit 1c0aa7ad10
3 changed files with 57 additions and 45 deletions

View File

@ -53,10 +53,8 @@ class CapsCache(object):
True True
>>> chatstates in cc[caps] >>> chatstates in cc[caps]
False False
>>> cc[caps].category >>> cc[caps].identities
'client' set({'category':'client', 'type':'pc'})
>>> cc[caps].type
'pc'
>>> x=cc[caps] # more efficient if making several queries for one set of caps >>> x=cc[caps] # more efficient if making several queries for one set of caps
ATypicalBlackBoxObject ATypicalBlackBoxObject
>>> muc in x >>> muc in x
@ -71,14 +69,12 @@ class CapsCache(object):
# setting data # setting data
>>> newcaps=('http://exodus.jabberstudio.org/caps', '0.9a', None) >>> newcaps=('http://exodus.jabberstudio.org/caps', '0.9a', None)
>>> cc[newcaps].category='client' >>> cc[newcaps].identities.add({'category':'client', 'type':'pc', 'name':'Gajim'})
>>> cc[newcaps].type='pc'
>>> cc[newcaps].features+=muc # same as: >>> cc[newcaps].features+=muc # same as:
>>> cc[newcaps]+=muc >>> cc[newcaps]+=muc
>>> cc[newcaps]['csn']+=chatstates # adding data as if ext was 'csn' >>> cc[newcaps]['csn']+=chatstates # adding data as if ext was 'csn'
# warning: no feature removal! # warning: no feature removal!
''' '''
# __metaclass__ = VerboseClassType
def __init__(self, logger=None): def __init__(self, logger=None):
''' Create a cache for entity capabilities. ''' ''' Create a cache for entity capabilities. '''
# our containers: # our containers:
@ -93,18 +89,9 @@ class CapsCache(object):
# client (node/version pair) # client (node/version pair)
self.__names = {} self.__names = {}
self.__cache = {} self.__cache = {}
class CacheQuery(object):
# __metaclass__ = VerboseClassType
def __init__(cqself, proxied):
cqself.proxied=proxied
def __getattr__(cqself, obj):
if obj!='exts': return getattr(cqself.proxied[0], obj)
return set(chain(ci.features for ci in cqself.proxied))
class CacheItem(object): class CacheItem(object):
''' TODO: logging data into db ''' ''' TODO: logging data into db '''
# __metaclass__ = VerboseClassType
def __init__(ciself, node, version, ext=None): def __init__(ciself, node, version, ext=None):
# cached into db # cached into db
ciself.node = node ciself.node = node
@ -113,10 +100,9 @@ class CapsCache(object):
ciself.exts = {} ciself.exts = {}
# set of tuples: (category, type, name) # set of tuples: (category, type, name)
# (dictionaries are not hashable, so cannot be in sets)
ciself.identities = set() ciself.identities = set()
ciself.cache = self
# not cached into db: # not cached into db:
# have we sent the query? # have we sent the query?
# 0 == not queried # 0 == not queried
@ -128,8 +114,16 @@ class CapsCache(object):
newfeature=self.__names.setdefault(newfeature, newfeature) newfeature=self.__names.setdefault(newfeature, newfeature)
ciself.features.add(newfeature) ciself.features.add(newfeature)
class CacheQuery(object):
def __init__(cqself, proxied):
cqself.proxied=proxied
def __getattr__(cqself, obj):
if obj!='exts': return getattr(cqself.proxied[0], obj)
return set(chain(ci.features for ci in cqself.proxied))
def __getitem__(ciself, exts): def __getitem__(ciself, exts):
if len(exts)==0: if not exts: # (), [], None, False, whatever
return ciself return ciself
if len(exts)==1: if len(exts)==1:
ext=exts[0] ext=exts[0]
@ -140,8 +134,7 @@ class CapsCache(object):
return x return x
proxied = [ciself] proxied = [ciself]
proxied.extend(ciself[(e,)] for e in exts) proxied.extend(ciself[(e,)] for e in exts)
return CacheQuery(proxied) return ciself.CacheQuery(proxied)
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
@ -180,38 +173,25 @@ class CapsCache(object):
self.__cache[(node, version)]=x self.__cache[(node, version)]=x
return x return x
def preload(self, con, jid, node, ver, exts): def preload(self, account, jid, node, ver, exts):
''' 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. '''
q=self[(node, ver, ())] q=self[(node, ver, ())]
qq=q qq=q
def callback(identities, features):
try:
qq.identities=set(
(i['category'], i['type'], i.get('name'))
for i in identities)
qq.features=set(self.__names[f] for f in features)
qq.queried=2
print 'Got features!'
print '%s/%s:' % (qq.node, qq.version)
print '%s\n%s' % (qq.identities, qq.features)
except KeyError: # improper answer, ignore
qq.queried=0
if q.queried==0: if q.queried==0:
# do query for bare node+version pair # do query for bare node+version pair
# this will create proper object # this will create proper object
q.queried=1 q.queried=1
xmpp.features_nb.discoverInfo(con, jid, '%s#%s' % (node, ver), callback) account.discoverInfo(jid, '%s#%s' % (node, ver))
for ext in exts: for ext in exts:
qq=q[ext] qq=q[ext]
if qq.queried==0: if qq.queried==0:
# do query for node+version+ext triple # do query for node+version+ext triple
qq.queried=1 qq.queried=1
xmpp.features_nb.discoverInfo(con, jid, account.discoverInfo(jid, '%s#%s' % (node, ext))
'%s#%s' % (node, ext), callback)
capscache = CapsCache() capscache = CapsCache()
@ -243,7 +223,7 @@ class ConnectionCaps(object):
jid=str(presence.getFrom()) jid=str(presence.getFrom())
# start disco query... # start disco query...
capscache.preload(con, jid, node, ver, exts) capscache.preload(self, jid, node, ver, exts)
contact=gajim.contacts.get_contact_from_full_jid(self.name, jid) contact=gajim.contacts.get_contact_from_full_jid(self.name, jid)
if contact in [None, []]: if contact in [None, []]:
@ -255,3 +235,27 @@ class ConnectionCaps(object):
contact.caps_node=node contact.caps_node=node
contact.caps_ver=ver contact.caps_ver=ver
contact.caps_exts=exts contact.caps_exts=exts
def _capsDiscoCB(self, jid, node, identities, features, data):
gajim.log.debug('capsDiscoCB(jid=%r, node=%r, identities=%r, features=%r, data=%r)' %\
(jid, node, identities, features, data))
contact=gajim.contacts.get_contact_from_full_jid(self.name, jid)
gajim.log.debug(' contact=%r' % contact)
if not contact: return
if not contact.caps_node: return # we didn't asked for that?
if not node.startswith(contact.caps_node+'#'): return
node, ext = node.split('#')
if ext==contact.caps_ver: # this can be also version (like '0.9')
exts=None
else:
exts=(ext,)
# if we don't have this info already...
caps=capscache[(node, contact.caps_ver, exts)]
if caps.queried==2: return
caps.identities=set((i['category'], i['type'], i.get('name')) for i in identities)
caps.features=set(features)
gajim.log.debug('capsDiscoCB: added caps for %r:\n identities=%r\n features=%r'\
% ((node, contact.caps_ver, exts), caps.identities, caps.features))

View File

@ -776,11 +776,13 @@ class ConnectionDisco:
attr = {} attr = {}
for key in i.getAttrs().keys(): for key in i.getAttrs().keys():
attr[key] = i.getAttr(key) attr[key] = i.getAttr(key)
if attr.has_key('category') and attr['category'] in ('gateway', 'headline')\ if attr.has_key('category') and \
and attr.has_key('type'): attr['category'] in ('gateway', 'headline') and \
attr.has_key('type'):
transport_type = attr['type'] transport_type = attr['type']
if attr.has_key('category') and attr['category'] == 'conference' \ if attr.has_key('category') and \
and attr.has_key('type') and attr['type'] == 'text': attr['category'] == 'conference' and \
attr.has_key('type') and attr['type'] == 'text':
is_muc = True is_muc = True
identities.append(attr) identities.append(attr)
elif i.getName() == 'feature': elif i.getName() == 'feature':
@ -812,8 +814,10 @@ class ConnectionDisco:
self.available_transports[transport_type].append(jid) self.available_transports[transport_type].append(jid)
else: else:
self.available_transports[transport_type] = [jid] self.available_transports[transport_type] = [jid]
self.dispatch('AGENT_INFO_INFO', (jid, node, identities, self.dispatch('AGENT_INFO_INFO', (jid, node, identities,
features, data)) features, data))
self._capsDiscoCB(jid, node, identities, features, data)
class ConnectionVcard: class ConnectionVcard:
def __init__(self): def __init__(self):

View File

@ -234,10 +234,14 @@ class Contacts:
return [] return []
def get_contact_from_full_jid(self, account, fjid): def get_contact_from_full_jid(self, account, fjid):
'''we will split the jid into bare jid and resource part, ''' Get Contact object for specific resource of given jid'''
then get proper contact.'''
barejid, resource = common.gajim.get_room_and_nick_from_fjid(fjid) barejid, resource = common.gajim.get_room_and_nick_from_fjid(fjid)
return self.get_contact(account, barejid, resource) if barejid in self._contacts[account]:
contacts = self._contacts[account][barejid]
for c in contacts:
if c.resource==resource:
return c
return None
def get_highest_prio_contact_from_contacts(self, contacts): def get_highest_prio_contact_from_contacts(self, contacts):
if not contacts: if not contacts: