caps: data stored and retrieved

This commit is contained in:
Tomasz Melcer 2007-07-09 21:24:47 +00:00
parent 952e6d96e3
commit 6c1858224e
6 changed files with 143 additions and 35 deletions

View file

@ -1,5 +1,5 @@
AC_INIT([Gajim - A Jabber Instant Messager],
[0.11.1.2],[http://trac.gajim.org/],[gajim])
[0.11.1.3],[http://trac.gajim.org/],[gajim])
AC_PREREQ([2.59])
AM_INIT_AUTOMAKE([1.8])
AC_CONFIG_HEADER(config.h)

View file

@ -11,14 +11,10 @@
## GNU General Public License for more details.
##
#import logger
#import gajim
from itertools import *
import gajim
import xmpp
import xmpp.features_nb
#from meta import VerboseClassType
import gajim
class CapsCache(object):
''' This object keeps the mapping between caps data and real disco
@ -97,6 +93,7 @@ class CapsCache(object):
ciself.node = node
ciself.version = version
ciself.features = set()
ciself.ext = ext
ciself.exts = {}
# set of tuples: (category, type, name)
@ -110,10 +107,6 @@ class CapsCache(object):
# 2 == got the answer
ciself.queried = 0
def __iadd__(ciself, newfeature):
newfeature=self.__names.setdefault(newfeature, newfeature)
ciself.features.add(newfeature)
class CacheQuery(object):
def __init__(cqself, proxied):
cqself.proxied=proxied
@ -125,6 +118,8 @@ class CapsCache(object):
def __getitem__(ciself, exts):
if not exts: # (), [], None, False, whatever
return ciself
if isinstance(exts, basestring):
exts=(exts,)
if len(exts)==1:
ext=exts[0]
if ext in ciself.exts:
@ -135,12 +130,21 @@ class CapsCache(object):
proxied = [ciself]
proxied.extend(ciself[(e,)] for e in exts)
return ciself.CacheQuery(proxied)
def update(ciself, identities, features):
# NOTE: self refers to CapsCache object, not to CacheItem
self.identities=identities
self.features=features
self.logger.add_caps_entry(
ciself.node, ciself.version, ciself.ext,
identities, features)
self.__CacheItem = CacheItem
# prepopulate data which we are sure of; note: we do not log these info
gajim = 'http://gajim.org/caps'
gajimnode = 'http://gajim.org/caps'
gajimcaps=self[(gajim, '0.11.1')]
gajimcaps=self[(gajimnode, '0.11.1')]
gajimcaps.category='client'
gajimcaps.type='pc'
gajimcaps.features=set((xmpp.NS_BYTESTREAM, xmpp.NS_SI,
@ -152,17 +156,16 @@ class CapsCache(object):
# TODO: older gajim versions
# start logging data from the net
self.__logger = logger
self.logger = logger
def load_from_db(self):
# get data from logger...
if self.__logger is not None:
for node, version, category, type_, name in self.__logger.get_caps_cache():
x=self.__clients[(node, version)]
x.category=category
x.type=type_
x.name=name
for node, version, ext, feature in self.__logger.get_caps_features_cache():
self.__clients[(node, version)][ext]+=feature
if self.logger is not None:
for node, ver, ext, identities, features in self.logger.iter_caps_data():
x=self[(node, ver, ext)]
x.identities=identities
x.features=features
x.queried=2
def __getitem__(self, caps):
node_version = caps[:2]
@ -185,6 +188,7 @@ class CapsCache(object):
# this will create proper object
q.queried=1
account.discoverInfo(jid, '%s#%s' % (node, ver))
else:
for ext in exts:
qq=q[ext]
@ -192,8 +196,9 @@ class CapsCache(object):
# do query for node+version+ext triple
qq.queried=1
account.discoverInfo(jid, '%s#%s' % (node, ext))
else:
capscache = CapsCache()
gajim.capscache = CapsCache(gajim.logger)
class ConnectionCaps(object):
''' This class highly depends on that it is a part of Connection class. '''
@ -223,7 +228,7 @@ class ConnectionCaps(object):
jid=str(presence.getFrom())
# start disco query...
capscache.preload(self, jid, node, ver, exts)
gajim.capscache.preload(self, jid, node, ver, exts)
contact=gajim.contacts.get_contact_from_full_jid(self.name, jid)
if contact in [None, []]:
@ -237,10 +242,7 @@ class ConnectionCaps(object):
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
@ -251,11 +253,9 @@ class ConnectionCaps(object):
exts=(ext,)
# if we don't have this info already...
caps=capscache[(node, contact.caps_ver, exts)]
caps=gajim.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)
identities=set((i['category'], i['type'], i.get('name')) for i in identities)
caps.update(identities, 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

@ -42,6 +42,7 @@ def create_log_db():
# logs.jid_id --> jids.jid_id but Sqlite doesn't do FK etc so it's done in python code
# jids.jid text column will be JID if TC-related, room_jid if GC-related,
# ROOM_JID/nick if pm-related.
# also check optparser.py, which updates databases on gajim updates
cur.executescript(
'''
CREATE TABLE jids(
@ -74,6 +75,12 @@ def create_log_db():
);
CREATE INDEX idx_logs_jid_id_kind ON logs (jid_id, kind);
CREATE TABLE caps_cache (
node TEXT,
ver TEXT,
ext TEXT,
data BLOB);
'''
)

View file

@ -2,7 +2,7 @@ docdir = '../'
datadir = '../'
version = '0.11.1.2'
version = '0.11.1.3'
import sys, os.path
for base in ('.', 'common'):

View file

@ -19,6 +19,8 @@ import os
import sys
import time
import datetime
from gzip import GzipFile
from cStringIO import StringIO
import exceptions
import gajim
@ -633,6 +635,76 @@ class Logger:
answer[result[0]] = self.convert_api_values_to_human_transport_type(result[1])
return answer
# initial interface for accessing logs of stored caps
def get_stored_caps(self): pass
def add_caps_entry(self): pass
# A longer note here:
# The database contains a blob field. Pysqlite seems to need special care for such fields.
# When storing, we need to convert string into buffer object (1).
# When retrieving, we need to convert it back to a string to decompress it. (2)
# GzipFile needs a file-like object, StringIO emulates file for plain strings.
def iter_caps_data(self):
''' Iterate over caps cache data stored in the database.
The iterator values are pairs of (node, ver, ext, identities, features):
identities == {'category':'foo', 'type':'bar', 'name':'boo'},
features being a list of feature namespaces. '''
# get data from table
# the data field contains binary object (gzipped data), this is a hack
# to get that data without trying to convert it to unicode
#tmp, self.con.text_factory = self.con.text_factory, str
try:
self.cur.execute('''SELECT node, ver, ext, data FROM caps_cache;''');
except sqlite.OperationalError:
# might happen when there's no caps_cache table yet
# -- there's no data to read anyway then
#self.con.text_factory = tmp
return
#self.con.text_factory = tmp
for node, ver, ext, data in self.cur:
# for each row: unpack the data field
# (format: (category, type, name, category, type, name, ...
# ..., 'FEAT', feature1, feature2, ...).join(' '))
# NOTE: if there's a need to do more gzip, put that to a function
data=GzipFile(fileobj=StringIO(str(data))).read().split(' ') # (2) -- note above
i=0
identities=set()
features=set()
while i<len(data) and data[i]!='FEAT':
category=data[i]
type=data[i+1]
name=data[i+2]
identities.add((category,type,name))
i+=3
i+=1
while i<len(data):
features.add(data[i])
i+=1
if not ext: ext=None # to make '' a None
# yield the row
yield node, ver, ext, identities, features
def add_caps_entry(self, node, ver, ext, identities, features):
data=[]
for identity in identities:
# there is no FEAT category
if identity[0]=='FEAT': return
if len(identity)<2 or not identity[2]:
data.extend((identity[0], identity[1], ''))
else:
data.extend(identity)
data.append('FEAT')
data.extend(features)
data = ' '.join(data)
string = StringIO() # if there's a need to do more gzip, put that to a function
gzip=GzipFile(fileobj=string, mode='w')
gzip.write(data)
gzip.close()
data = string.getvalue()
self.cur.execute('''
INSERT INTO caps_cache ( node, ver, ext, data )
VALUES (?, ?, ?, ?);
''', (node, ver, ext, buffer(data))) # (1) -- note above
try:
self.con.commit()
except sqlite.OperationalError, e:
print >> sys.stderr, str(e)

View file

@ -157,9 +157,13 @@ class OptionsParser:
self.update_config_to_01111()
if old < [0, 11, 1, 2] and new >= [0, 11, 1, 2]:
self.update_config_to_01112()
if old < [0, 11, 1, 3] and new >= [0, 11, 1, 3]:
self.update_config_to_01113()
gajim.logger.init_vars()
gajim.config.set('version', new_version)
gajim.capscache.load_from_db()
def update_config_x_to_09(self):
# Var name that changed:
@ -402,3 +406,28 @@ class OptionsParser:
self.old_values['roster_theme'] == 'gtk+':
gajim.config.set('roster_theme', _('default'))
gajim.config.set('version', '0.11.1.2')
def update_config_to_01113(self):
# copy&pasted from update_config_to_01013, possibly 'FIXME see #2812' applies too
back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER)
con = sqlite.connect(logger.LOG_DB_FILE)
os.chdir(back)
cur = con.cursor()
try:
cur.executescript(
'''
CREATE TABLE caps_cache (
node TEXT,
ver TEXT,
ext TEXT,
data BLOB
);
'''
)
con.commit()
except sqlite.OperationalError, e:
pass
con.close()
gajim.config.set('version', '0.11.1.3')