new XEP-0115 implementation (version 1.5)

This commit is contained in:
Yann Leboulanger 2008-04-20 22:58:47 +00:00
parent be310652c4
commit a3827fe5d0
13 changed files with 274 additions and 195 deletions

View file

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

View file

@ -20,6 +20,7 @@ from itertools import *
import xmpp import xmpp
import xmpp.features_nb import xmpp.features_nb
import gajim import gajim
import helpers
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
@ -93,17 +94,30 @@ class CapsCache(object):
class CacheItem(object): class CacheItem(object):
''' TODO: logging data into db ''' ''' TODO: logging data into db '''
def __init__(ciself, node, version, ext=None): def __init__(ciself, hash_method, hash):
# cached into db # cached into db
ciself.node = node ciself.hash_method = hash_method
ciself.version = version ciself.hash = hash
ciself.features = set()
ciself.ext = ext
ciself.exts = {}
# set of tuples: (category, type, name) @property
# (dictionaries are not hashable, so cannot be in sets) def features():
ciself.identities = set() def fget(self):
return self.getAttr('features')
def fset(self, value):
list_ = []
for feature in value:
list_.append(self.__names.setdefault(feature, feature))
self.setAttr('features', list_)
@property
def identities():
def fget(self):
return self.getAttr('identities')
def fset(self, value):
list_ = []
for identity in value:
list_.append(self.__names.setdefault(identity, identity))
self.setAttr('identities', list_)
# not cached into db: # not cached into db:
# have we sent the query? # have we sent the query?
@ -112,53 +126,21 @@ class CapsCache(object):
# 2 == got the answer # 2 == got the answer
ciself.queried = 0 ciself.queried = 0
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):
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:
return ciself.exts[ext]
x=CacheItem(ciself.node, ciself.version, ext)
ciself.exts[ext]=x
return x
proxied = [ciself]
proxied.extend(ciself[(e,)] for e in exts)
return ciself.CacheQuery(proxied)
def update(ciself, identities, features): def update(ciself, identities, features):
# NOTE: self refers to CapsCache object, not to CacheItem # NOTE: self refers to CapsCache object, not to CacheItem
self.identities=identities ciself.identities=identities
self.features=features ciself.features=features
self.logger.add_caps_entry( self.logger.add_caps_entry(ciself.hash_method, ciself.hash,
ciself.node, ciself.version, ciself.ext,
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
gajimnode = 'http://gajim.org/caps'
gajimcaps=self[(gajimnode, '0.11.1')] gajimcaps = self[('sha-1', gajim.caps_hash)]
gajimcaps.category='client' gajimcaps.identities = [gajim.gajim_identity]
gajimcaps.type='pc' gajimcaps.features = gajim.gajim_common_features + \
gajimcaps.features=set((xmpp.NS_BYTESTREAM, xmpp.NS_SI, gajim.gajim_optional_features
xmpp.NS_FILE, xmpp.NS_MUC, xmpp.NS_COMMANDS,
xmpp.NS_DISCO_INFO, xmpp.NS_PING, xmpp.NS_TIME_REVISED))
gajimcaps['cstates'].features=set((xmpp.NS_CHATSTATES,))
gajimcaps['xhtml'].features=set((xmpp.NS_XHTML_IM,))
# TODO: older gajim versions
# start logging data from the net # start logging data from the net
self.logger = logger self.logger = logger
@ -166,40 +148,32 @@ class CapsCache(object):
def load_from_db(self): def load_from_db(self):
# get data from logger... # get data from logger...
if self.logger is not None: if self.logger is not None:
for node, ver, ext, identities, features in self.logger.iter_caps_data(): for hash_method, hash, identities, features in \
x=self[(node, ver, ext)] self.logger.iter_caps_data():
x.identities=identities x = self[(hash_method, hash)]
x.features=features x.identities = identities
x.queried=2 x.features = features
x.queried = 2
def __getitem__(self, caps): def __getitem__(self, caps):
node_version = caps[:2] if caps in self.__cache:
if node_version in self.__cache: return self.__cache[caps]
return self.__cache[node_version][caps[2]] hash_method, hash = caps[0], caps[1]
node, version = self.__names.setdefault(caps[0], caps[0]), caps[1] x = self.__CacheItem(hash_method, hash)
x=self.__CacheItem(node, version) self.__cache[(hash_method, hash)] = x
self.__cache[(node, version)]=x
return x return x
def preload(self, account, jid, node, ver, exts): def preload(self, con, jid, node, hash_method, hash):
''' 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[(hash_method, hash)]
qq=q
if q.queried==0: if q.queried==0:
# do query for bare node+version pair # do query for bare node+hash pair
# this will create proper object # this will create proper object
q.queried=1 q.queried=1
account.discoverInfo(jid, '%s#%s' % (node, ver)) con.discoverInfo(jid, '%s#%s' % (node, hash))
for ext in exts:
qq=q[ext]
if qq.queried==0:
# do query for node+version+ext triple
qq.queried=1
account.discoverInfo(jid, '%s#%s' % (node, ext))
gajim.capscache = CapsCache(gajim.logger) gajim.capscache = CapsCache(gajim.logger)
@ -210,19 +184,14 @@ class ConnectionCaps(object):
for xmpp registered in connection_handlers.py''' for xmpp registered in connection_handlers.py'''
# get the caps element # get the caps element
caps=presence.getTag('c') caps = presence.getTag('c')
if not caps: return if not caps:
node, ver=caps['node'], caps['ver']
if node is None or ver is None:
# improper caps in stanza, ignoring
return return
try: hash_method, node, hash = caps['hash'], caps['node'], caps['ver']
exts=caps['ext'].split(' ') if hash_method is None or node is None or hash is None:
except AttributeError: # improper caps in stanza, ignoring
# no exts means no exts, a perfectly valid case return
exts=[]
# we will put these into proper Contact object and ask # we will put these into proper Contact object and ask
# for disco... so that disco will learn how to interpret # for disco... so that disco will learn how to interpret
@ -231,7 +200,7 @@ class ConnectionCaps(object):
jid=str(presence.getFrom()) jid=str(presence.getFrom())
# start disco query... # start disco query...
gajim.capscache.preload(self, jid, node, ver, exts) gajim.capscache.preload(self, jid, node, hash_method, hash)
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, []]:
@ -240,25 +209,31 @@ class ConnectionCaps(object):
contact = contact[0] contact = contact[0]
# overwriting old data # overwriting old data
contact.caps_node=node contact.caps_node = node
contact.caps_ver=ver contact.caps_hash_method = hash_method
contact.caps_exts=exts contact.caps_hash = hash
def _capsDiscoCB(self, jid, node, identities, features, data): def _capsDiscoCB(self, jid, node, identities, features, data):
contact=gajim.contacts.get_contact_from_full_jid(self.name, jid) contact = gajim.contacts.get_contact_from_full_jid(self.name, jid)
if not contact: return if not contact:
if not contact.caps_node: return # we didn't asked for that? return
if not node.startswith(contact.caps_node+'#'): return if not contact.caps_node:
node, ext = node.split('#', 1) return # we didn't asked for that?
if ext==contact.caps_ver: # this can be also version (like '0.9') if not node.startswith(contact.caps_node + '#'):
exts=None return
else: node, hash = node.split('#', 1)
exts=(ext,) computed_hash = helpers.compute_caps_hash(identities, features,
contact.caps_hash_method)
if computed_hash != hash:
# wrong hash, forget it
contact.caps_node = ''
contact.caps_hash_method = ''
contact.caps_hash = ''
return
# if we don't have this info already... # if we don't have this info already...
caps=gajim.capscache[(node, contact.caps_ver, exts)] caps = gajim.capscache[(contact.caps_hash_method, hash)]
if caps.queried==2: return if caps.queried == 2:
return
identities=set((i['category'], i['type'], i.get('name')) for i in identities)
caps.update(identities, features) caps.update(identities, features)

View file

@ -82,9 +82,8 @@ def create_log_db():
CREATE INDEX idx_logs_jid_id_kind ON logs (jid_id, kind); CREATE INDEX idx_logs_jid_id_kind ON logs (jid_id, kind);
CREATE TABLE caps_cache ( CREATE TABLE caps_cache (
node TEXT, hash_method TEXT,
ver TEXT, hash TEXT,
ext TEXT,
data BLOB); data BLOB);
CREATE TABLE IF NOT EXISTS rooms_last_message_time( CREATE TABLE IF NOT EXISTS rooms_last_message_time(

View file

@ -667,7 +667,7 @@ class ConnectionDisco:
common.xmpp.NS_DISCO, frm = to) common.xmpp.NS_DISCO, frm = to)
iq.setAttr('id', id) iq.setAttr('id', id)
query = iq.setTag('query') query = iq.setTag('query')
query.setAttr('node','http://gajim.org/caps#' + gajim.version.split('-', query.setAttr('node','http://gajim.org#' + gajim.version.split('-',
1)[0]) 1)[0])
for f in (common.xmpp.NS_BYTESTREAM, common.xmpp.NS_SI, for f in (common.xmpp.NS_BYTESTREAM, common.xmpp.NS_SI,
common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS): common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS):
@ -744,55 +744,17 @@ class ConnectionDisco:
q = iq.getTag('query') q = iq.getTag('query')
if node: if node:
q.setAttr('node', node) q.setAttr('node', node)
q.addChild('identity', attrs = {'type': 'pc', 'category': 'client', q.addChild('identity', attrs = gajim.gajim_identity)
'name': 'Gajim'})
extension = None extension = None
if node and node.find('#') != -1: if node and node.find('#') != -1:
extension = node[node.index('#') + 1:] extension = node[node.index('#') + 1:]
client_version = 'http://gajim.org/caps#' + gajim.version.split('-', client_version = 'http://gajim.org#' + gajim.caps_hash
1)[0]
if node in (None, client_version): if node in (None, client_version):
q.addChild('feature', attrs = {'var': common.xmpp.NS_BYTESTREAM}) for f in gajim.gajim_common_features:
q.addChild('feature', attrs = {'var': common.xmpp.NS_SI}) q.addChild('feature', attrs = {'var': f})
q.addChild('feature', attrs = {'var': common.xmpp.NS_FILE}) for f in gajim.gajim_optional_features:
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC}) q.addChild('feature', attrs = {'var': f})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC_USER})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC_ADMIN})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC_OWNER})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC_CONFIG})
q.addChild('feature', attrs = {'var': common.xmpp.NS_COMMANDS})
q.addChild('feature', attrs = {'var': common.xmpp.NS_DISCO_INFO})
q.addChild('feature', attrs = {'var': 'ipv6'})
q.addChild('feature', attrs = {'var': 'jabber:iq:gateway'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_LAST})
q.addChild('feature', attrs = {'var': common.xmpp.NS_PRIVACY})
q.addChild('feature', attrs = {'var': common.xmpp.NS_PRIVATE})
q.addChild('feature', attrs = {'var': common.xmpp.NS_REGISTER})
q.addChild('feature', attrs = {'var': common.xmpp.NS_VERSION})
q.addChild('feature', attrs = {'var': common.xmpp.NS_DATA})
q.addChild('feature', attrs = {'var': common.xmpp.NS_ENCRYPTED})
q.addChild('feature', attrs = {'var': 'msglog'})
q.addChild('feature', attrs = {'var': 'sslc2s'})
q.addChild('feature', attrs = {'var': 'stringprep'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_PING})
q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY})
q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY + '+notify'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE})
q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE + '+notify'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD + '+notify'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_ESESSION_INIT})
if (node is None or extension == 'cstates') and gajim.config.get('outgoing_chat_state_notifactions') != 'disabled':
q.addChild('feature', attrs = {'var': common.xmpp.NS_CHATSTATES})
if (node is None or extension == 'xhtml') and not gajim.config.get('ignore_incoming_xhtml'):
q.addChild('feature', attrs = {'var': common.xmpp.NS_XHTML_IM})
if node is None:
q.addChild('feature', attrs = {'var': common.xmpp.NS_PING})
q.addChild('feature', attrs = {'var': common.xmpp.NS_TIME_REVISED})
if q.getChildren(): if q.getChildren():
self.connection.send(iq) self.connection.send(iq)
@ -892,16 +854,9 @@ class ConnectionVcard:
def add_caps(self, p): def add_caps(self, p):
''' advertise our capabilities in presence stanza (xep-0115)''' ''' advertise our capabilities in presence stanza (xep-0115)'''
c = p.setTag('c', namespace = common.xmpp.NS_CAPS) c = p.setTag('c', namespace = common.xmpp.NS_CAPS)
c.setAttr('node', 'http://gajim.org/caps') c.setAttr('hash', 'sha-1')
ext = [] c.setAttr('node', 'http://gajim.org')
if not gajim.config.get('ignore_incoming_xhtml'): c.setAttr('ver', gajim.caps_hash)
ext.append('xhtml')
if gajim.config.get('outgoing_chat_state_notifactions') != 'disabled':
ext.append('cstates')
if len(ext):
c.setAttr('ext', ' '.join(ext))
c.setAttr('ver', gajim.version.split('-', 1)[0])
return p return p
def node_to_dict(self, node): def node_to_dict(self, node):

View file

@ -44,8 +44,8 @@ class Contact:
# Capabilities; filled by caps.py/ConnectionCaps object # Capabilities; filled by caps.py/ConnectionCaps object
# every time it gets these from presence stanzas # every time it gets these from presence stanzas
self.caps_node = None self.caps_node = None
self.caps_ver = None self.caps_hash_method = None
self.caps_exts = None self.caps_hash = None
# 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:

View file

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

View file

@ -27,6 +27,7 @@ import locale
import config import config
from contacts import Contacts from contacts import Contacts
from events import Events from events import Events
import xmpp
try: try:
import defs import defs
@ -164,6 +165,21 @@ else:
if system('gpg -h >/dev/null 2>&1'): if system('gpg -h >/dev/null 2>&1'):
HAVE_GPG = False HAVE_GPG = False
gajim_identity = {'type': 'pc', 'category': 'client', 'name': 'Gajim'}
gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI,
xmpp.NS_FILE, xmpp.NS_MUC, xmpp.NS_MUC_USER,
xmpp.NS_MUC_ADMIN, xmpp.NS_MUC_OWNER,
xmpp.NS_MUC_CONFIG, xmpp.NS_COMMANDS,
xmpp.NS_DISCO_INFO, 'ipv6', 'jabber:iq:gateway', xmpp.NS_LAST,
xmpp.NS_PRIVACY, xmpp.NS_PRIVATE, xmpp.NS_REGISTER,
xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED,
'msglog', 'sslc2s', 'stringprep', xmpp.NS_PING,
xmpp.NS_TIME_REVISED]
# Optional features gajim supports
gajim_optional_features = []
caps_hash = ''
def get_nick_from_jid(jid): def get_nick_from_jid(jid):
pos = jid.find('@') pos = jid.find('@')
return jid[:pos] return jid[:pos]

View file

@ -30,6 +30,8 @@ import urllib
import errno import errno
import select import select
import sha import sha
import hashlib
import base64
import sys import sys
from encodings.punycode import punycode_encode from encodings.punycode import punycode_encode
from encodings import idna from encodings import idna
@ -38,7 +40,7 @@ import gajim
from i18n import Q_ from i18n import Q_
from i18n import ngettext from i18n import ngettext
from xmpp_stringprep import nodeprep, resourceprep, nameprep from xmpp_stringprep import nodeprep, resourceprep, nameprep
import xmpp
if sys.platform == 'darwin': if sys.platform == 'darwin':
from osx import nsapp from osx import nsapp
@ -1199,3 +1201,93 @@ def prepare_and_validate_gpg_keyID(account, jid, keyID):
keyID = 'UNKNOWN' keyID = 'UNKNOWN'
return keyID return keyID
def sort_identities_func(i1, i2):
cat1 = i1['category']
cat2 = i2['category']
if cat1 < cat2:
return -1
if cat1 > cat2:
return 1
if i1.has_key('type'):
type1 = i1['type']
else:
type1 = ''
if i2.has_key('type'):
type2 = i2['type']
else:
type2 = ''
if type1 < type2:
return -1
if type1 > type2:
return 1
if i1.has_key('xml:lang'):
lang1 = i1['xml:lang']
else:
lang1 = ''
if i2.has_key('xml:lang'):
lang2 = i2['xml:lang']
else:
lang2 = ''
if lang1 < lang2:
return -1
if lang1 > lang2:
return 1
return 0
def compute_caps_hash(identities, features, hash_method='sha-1'):
S = ''
identities.sort(cmp=sort_identities_func)
for i in identities:
c = i['category']
if i.has_key('type'):
type_ = i['type']
else:
type_ = ''
if i.has_key('xml:lang'):
lang = i['xml:lang']
else:
lang = ''
if i.has_key('name'):
name = i['name']
else:
name = ''
S += '%s/%s/%s/%s<' % (c, type_, lang, name)
features.sort()
for f in features:
S += '%s<' % f
if hash_method == 'sha-1':
hash = hashlib.sha1(S)
elif hash_method == 'md5':
hash = hashlib.md5(S)
else:
return ''
return base64.b64encode(hash.digest())
def update_optional_features():
gajim.gajim_optional_features = []
if gajim.config.get('publish_mood'):
gajim.gajim_optional_features.append(xmpp.NS_MOOD)
if gajim.config.get('subscribe_mood'):
gajim.gajim_optional_features.append(xmpp.NS_MOOD + '+notify')
if gajim.config.get('publish_activity'):
gajim.gajim_optional_features.append(xmpp.NS_ACTIVITY)
if gajim.config.get('subscribe_activity'):
gajim.gajim_optional_features.append(xmpp.NS_ACTIVITY + '+notify')
if gajim.config.get('publish_tune'):
gajim.gajim_optional_features.append(xmpp.NS_TUNE)
if gajim.config.get('subscribe_tune'):
gajim.gajim_optional_features.append(xmpp.NS_TUNE + '+notify')
if gajim.config.get('outgoing_chat_state_notifactions') != 'disabled':
gajim.gajim_optional_features.append(xmpp.NS_CHATSTATES)
if not gajim.config.get('ignore_incoming_xhtml'):
gajim.gajim_optional_features.append(xmpp.NS_XHTML_IM)
if gajim.HAVE_PYCRYPTO:
gajim.gajim_optional_features.append(xmpp.NS_ESESSION_INIT)
gajim.caps_hash = compute_caps_hash([gajim.gajim_identity],
gajim.gajim_common_features + gajim.gajim_optional_features)
# re-send presence with new hash
for account in gajim.connections:
connected = gajim.connections[account].connected
if connected > 1 and gajim.SHOW_LIST[connected] != 'invisible':
gajim.connections[account].change_status(gajim.SHOW_LIST[connected],
gajim.connections[account].status)

View file

@ -698,60 +698,59 @@ class Logger:
# to get that data without trying to convert it to unicode # to get that data without trying to convert it to unicode
#tmp, self.con.text_factory = self.con.text_factory, str #tmp, self.con.text_factory = self.con.text_factory, str
try: try:
self.cur.execute('''SELECT node, ver, ext, data FROM caps_cache;'''); self.cur.execute('SELECT hash_method, hash, data FROM caps_cache;');
except sqlite.OperationalError: except sqlite.OperationalError:
# might happen when there's no caps_cache table yet # might happen when there's no caps_cache table yet
# -- there's no data to read anyway then # -- there's no data to read anyway then
#self.con.text_factory = tmp #self.con.text_factory = tmp
return return
#self.con.text_factory = tmp #self.con.text_factory = tmp
for hash_method, hash, data in self.cur:
for node, ver, ext, data in self.cur:
# for each row: unpack the data field # for each row: unpack the data field
# (format: (category, type, name, category, type, name, ... # (format: (category, type, name, category, type, name, ...
# ..., 'FEAT', feature1, feature2, ...).join(' ')) # ..., 'FEAT', feature1, feature2, ...).join(' '))
# NOTE: if there's a need to do more gzip, put that to a function # NOTE: if there's a need to do more gzip, put that to a function
data=GzipFile(fileobj=StringIO(str(data))).read().split('\0') data = GzipFile(fileobj=StringIO(str(data))).read().split('\0')
i=0 i=0
identities=set() identities = list()
features=set() features = list()
while i<(len(data)-2) and data[i]!='FEAT': while i < (len(data) - 3) and data[i] != 'FEAT':
category=data[i] category = data[i]
type=data[i+1] type_ = data[i + 1]
name=data[i+2] lang = data[i + 2]
identities.add((category,type,name)) name = data[i + 3]
i+=3 identities.append({'category': category, 'type': type_,
'xml:lang': lang, 'name': name})
i += 4
i+=1 i+=1
while i<len(data): while i < len(data):
features.add(data[i]) features.append(data[i])
i+=1 i += 1
if not ext: ext=None # to make '' a None
# yield the row # yield the row
yield node, ver, ext, identities, features yield hash_method, hash, identities, features
def add_caps_entry(self, node, ver, ext, identities, features): def add_caps_entry(self, hash_method, hash, identities, features):
data=[] data=[]
for identity in identities: for identity in identities:
# there is no FEAT category # there is no FEAT category
if identity[0]=='FEAT': return if identity['category'] == 'FEAT':
if len(identity)<2 or not identity[2]: return
data.extend((identity[0], identity[1], '')) data.extend((identity.get('category'), identity.get('type', ''),
else: identity.get('xml:lang', ''), identity.get('name', '')))
data.extend(identity)
data.append('FEAT') data.append('FEAT')
data.extend(features) data.extend(features)
data = '\0'.join(data) data = '\0'.join(data)
# if there's a need to do more gzip, put that to a function # if there's a need to do more gzip, put that to a function
string = StringIO() string = StringIO()
gzip=GzipFile(fileobj=string, mode='w') gzip = GzipFile(fileobj=string, mode='w')
gzip.write(data) gzip.write(data)
gzip.close() gzip.close()
data = string.getvalue() data = string.getvalue()
self.cur.execute(''' self.cur.execute('''
INSERT INTO caps_cache ( node, ver, ext, data ) INSERT INTO caps_cache ( hash_method, hash, data )
VALUES (?, ?, ?, ?); VALUES (?, ?, ?);
''', (node, ver, ext, buffer(data))) # (1) -- note above ''', (hash_method, hash, buffer(data))) # (1) -- note above
try: try:
self.con.commit() self.con.commit()
except sqlite.OperationalError, e: except sqlite.OperationalError, e:

View file

@ -180,6 +180,8 @@ class OptionsParser:
self.update_config_to_01142() self.update_config_to_01142()
if old < [0, 11, 4, 3] and new >= [0, 11, 4, 3]: if old < [0, 11, 4, 3] and new >= [0, 11, 4, 3]:
self.update_config_to_01143() self.update_config_to_01143()
if old < [0, 11, 4, 4] and new >= [0, 11, 4, 4]:
self.update_config_to_01144()
gajim.logger.init_vars() gajim.logger.init_vars()
gajim.config.set('version', new_version) gajim.config.set('version', new_version)
@ -566,3 +568,30 @@ class OptionsParser:
pass pass
con.close() con.close()
gajim.config.set('version', '0.11.4.3') gajim.config.set('version', '0.11.4.3')
def update_config_to_01144(self):
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('DROP TABLE caps_cache;')
con.commit()
except sqlite.OperationalError, e:
pass
try:
cur.executescript(
'''
CREATE TABLE caps_cache (
hash_method TEXT,
hash TEXT,
data BLOB
);
'''
)
con.commit()
except sqlite.OperationalError, e:
pass
con.close()
gajim.config.set('version', '0.11.4.4')

View file

@ -33,7 +33,7 @@ import thread
import logging import logging
log = logging.getLogger('gajim.c.x.transports_nb') log = logging.getLogger('gajim.c.x.transports_nb')
from common import gajim import common.gajim
USE_PYOPENSSL = False USE_PYOPENSSL = False
@ -755,16 +755,16 @@ class NonBlockingTLS(PlugIn):
#tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
tcpsock.ssl_errnum = 0 tcpsock.ssl_errnum = 0
tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback) tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback)
cacerts = os.path.join(gajim.DATA_DIR, 'other', 'cacerts.pem') cacerts = os.path.join(common.gajim.DATA_DIR, 'other', 'cacerts.pem')
try: try:
tcpsock._sslContext.load_verify_locations(cacerts) tcpsock._sslContext.load_verify_locations(cacerts)
except: except:
log.warning('Unable to load SSL certificats from file %s' % \ log.warning('Unable to load SSL certificats from file %s' % \
os.path.abspath(cacerts)) os.path.abspath(cacerts))
# load users certs # load users certs
if os.path.isfile(gajim.MY_CACERTS): if os.path.isfile(common.gajim.MY_CACERTS):
store = tcpsock._sslContext.get_cert_store() store = tcpsock._sslContext.get_cert_store()
f = open(gajim.MY_CACERTS) f = open(common.gajim.MY_CACERTS)
lines = f.readlines() lines = f.readlines()
i = 0 i = 0
begin = -1 begin = -1
@ -779,11 +779,11 @@ class NonBlockingTLS(PlugIn):
store.add_cert(X509cert) store.add_cert(X509cert)
except OpenSSL.crypto.Error, exception_obj: except OpenSSL.crypto.Error, exception_obj:
log.warning('Unable to load a certificate from file %s: %s' %\ log.warning('Unable to load a certificate from file %s: %s' %\
(gajim.MY_CACERTS, exception_obj.args[0][0][2])) (common.gajim.MY_CACERTS, exception_obj.args[0][0][2]))
except: except:
log.warning( log.warning(
'Unknown error while loading certificate from file %s' % \ 'Unknown error while loading certificate from file %s' % \
gajim.MY_CACERTS) common.gajim.MY_CACERTS)
begin = -1 begin = -1
i += 1 i += 1
tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock) tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock)

View file

@ -538,6 +538,7 @@ class PreferencesWindow:
if gajim.connections[account].pep_supported: if gajim.connections[account].pep_supported:
pep.user_send_mood(account, '') pep.user_send_mood(account, '')
self.on_checkbutton_toggled(widget, 'publish_mood') self.on_checkbutton_toggled(widget, 'publish_mood')
helpers.update_optional_features()
def on_publish_activity_checkbutton_toggled(self, widget): def on_publish_activity_checkbutton_toggled(self, widget):
if widget.get_active() == False: if widget.get_active() == False:
@ -545,6 +546,7 @@ class PreferencesWindow:
if gajim.connections[account].pep_supported: if gajim.connections[account].pep_supported:
pep.user_send_activity(account, '') pep.user_send_activity(account, '')
self.on_checkbutton_toggled(widget, 'publish_activity') self.on_checkbutton_toggled(widget, 'publish_activity')
helpers.update_optional_features()
def on_publish_tune_checkbutton_toggled(self, widget): def on_publish_tune_checkbutton_toggled(self, widget):
if widget.get_active() == False: if widget.get_active() == False:
@ -552,17 +554,21 @@ class PreferencesWindow:
if gajim.connections[account].pep_supported: if gajim.connections[account].pep_supported:
pep.user_send_tune(account, '') pep.user_send_tune(account, '')
self.on_checkbutton_toggled(widget, 'publish_tune') self.on_checkbutton_toggled(widget, 'publish_tune')
helpers.update_optional_features()
gajim.interface.roster.enable_syncing_status_msg_from_current_music_track( gajim.interface.roster.enable_syncing_status_msg_from_current_music_track(
widget.get_active()) widget.get_active())
def on_subscribe_mood_checkbutton_toggled(self, widget): def on_subscribe_mood_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'subscribe_mood') self.on_checkbutton_toggled(widget, 'subscribe_mood')
helpers.update_optional_features()
def on_subscribe_activity_checkbutton_toggled(self, widget): def on_subscribe_activity_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'subscribe_activity') self.on_checkbutton_toggled(widget, 'subscribe_activity')
helpers.update_optional_features()
def on_subscribe_tune_checkbutton_toggled(self, widget): def on_subscribe_tune_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'subscribe_tune') self.on_checkbutton_toggled(widget, 'subscribe_tune')
helpers.update_optional_features()
def on_sort_by_show_checkbutton_toggled(self, widget): def on_sort_by_show_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'sort_by_show') self.on_checkbutton_toggled(widget, 'sort_by_show')
@ -625,6 +631,7 @@ class PreferencesWindow:
def on_xhtml_checkbutton_toggled(self, widget): def on_xhtml_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml') self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml')
helpers.update_optional_features()
def apply_speller(self): def apply_speller(self):
for acct in gajim.connections: for acct in gajim.connections:
@ -719,12 +726,17 @@ class PreferencesWindow:
def on_outgoing_chat_states_combobox_changed(self, widget): def on_outgoing_chat_states_combobox_changed(self, widget):
active = widget.get_active() active = widget.get_active()
old_value = gajim.config.get('outgoing_chat_state_notifications')
if active == 0: # all if active == 0: # all
gajim.config.set('outgoing_chat_state_notifications', 'all') gajim.config.set('outgoing_chat_state_notifications', 'all')
elif active == 1: # only composing elif active == 1: # only composing
gajim.config.set('outgoing_chat_state_notifications', 'composing_only') gajim.config.set('outgoing_chat_state_notifications', 'composing_only')
else: # disabled else: # disabled
gajim.config.set('outgoing_chat_state_notifications', 'disabled') gajim.config.set('outgoing_chat_state_notifications', 'disabled')
new_value = gajim.config.get('outgoing_chat_state_notifications')
if 'disabled' in (old_value, new_value):
# we changed from disabled to sth else or vice versa
helpers.update_optional_features()
def on_displayed_chat_states_combobox_changed(self, widget): def on_displayed_chat_states_combobox_changed(self, widget):
active = widget.get_active() active = widget.get_active()

View file

@ -3106,6 +3106,8 @@ class Interface:
if gajim.config.get('autodetect_browser_mailer') or not cfg_was_read: if gajim.config.get('autodetect_browser_mailer') or not cfg_was_read:
gtkgui_helpers.autodetect_browser_mailer() gtkgui_helpers.autodetect_browser_mailer()
helpers.update_optional_features()
if gajim.verbose: if gajim.verbose:
gajim.log.setLevel(gajim.logging.DEBUG) gajim.log.setLevel(gajim.logging.DEBUG)
else: else: