new XEP-0115 implementation (version 1.5)
This commit is contained in:
parent
be310652c4
commit
a3827fe5d0
|
@ -1,5 +1,5 @@
|
|||
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])
|
||||
AM_INIT_AUTOMAKE([1.8])
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
|
|
|
@ -20,6 +20,7 @@ from itertools import *
|
|||
import xmpp
|
||||
import xmpp.features_nb
|
||||
import gajim
|
||||
import helpers
|
||||
|
||||
class CapsCache(object):
|
||||
''' This object keeps the mapping between caps data and real disco
|
||||
|
@ -93,17 +94,30 @@ class CapsCache(object):
|
|||
|
||||
class CacheItem(object):
|
||||
''' TODO: logging data into db '''
|
||||
def __init__(ciself, node, version, ext=None):
|
||||
def __init__(ciself, hash_method, hash):
|
||||
# cached into db
|
||||
ciself.node = node
|
||||
ciself.version = version
|
||||
ciself.features = set()
|
||||
ciself.ext = ext
|
||||
ciself.exts = {}
|
||||
ciself.hash_method = hash_method
|
||||
ciself.hash = hash
|
||||
|
||||
# set of tuples: (category, type, name)
|
||||
# (dictionaries are not hashable, so cannot be in sets)
|
||||
ciself.identities = set()
|
||||
@property
|
||||
def features():
|
||||
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:
|
||||
# have we sent the query?
|
||||
|
@ -112,53 +126,21 @@ class CapsCache(object):
|
|||
# 2 == got the answer
|
||||
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):
|
||||
# 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,
|
||||
ciself.identities=identities
|
||||
ciself.features=features
|
||||
self.logger.add_caps_entry(ciself.hash_method, ciself.hash,
|
||||
identities, features)
|
||||
|
||||
self.__CacheItem = CacheItem
|
||||
|
||||
# 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.category='client'
|
||||
gajimcaps.type='pc'
|
||||
gajimcaps.features=set((xmpp.NS_BYTESTREAM, xmpp.NS_SI,
|
||||
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
|
||||
gajimcaps = self[('sha-1', gajim.caps_hash)]
|
||||
gajimcaps.identities = [gajim.gajim_identity]
|
||||
gajimcaps.features = gajim.gajim_common_features + \
|
||||
gajim.gajim_optional_features
|
||||
|
||||
# start logging data from the net
|
||||
self.logger = logger
|
||||
|
@ -166,40 +148,32 @@ class CapsCache(object):
|
|||
def load_from_db(self):
|
||||
# get data from logger...
|
||||
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
|
||||
for hash_method, hash, identities, features in \
|
||||
self.logger.iter_caps_data():
|
||||
x = self[(hash_method, hash)]
|
||||
x.identities = identities
|
||||
x.features = features
|
||||
x.queried = 2
|
||||
|
||||
def __getitem__(self, caps):
|
||||
node_version = caps[:2]
|
||||
if node_version in self.__cache:
|
||||
return self.__cache[node_version][caps[2]]
|
||||
node, version = self.__names.setdefault(caps[0], caps[0]), caps[1]
|
||||
x=self.__CacheItem(node, version)
|
||||
self.__cache[(node, version)]=x
|
||||
if caps in self.__cache:
|
||||
return self.__cache[caps]
|
||||
hash_method, hash = caps[0], caps[1]
|
||||
x = self.__CacheItem(hash_method, hash)
|
||||
self.__cache[(hash_method, hash)] = 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
|
||||
query to jid using proper connection. Don't query if
|
||||
the data is already in cache. '''
|
||||
q=self[(node, ver, ())]
|
||||
qq=q
|
||||
q = self[(hash_method, hash)]
|
||||
|
||||
if q.queried==0:
|
||||
# do query for bare node+version pair
|
||||
# do query for bare node+hash pair
|
||||
# this will create proper object
|
||||
q.queried=1
|
||||
account.discoverInfo(jid, '%s#%s' % (node, ver))
|
||||
|
||||
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))
|
||||
con.discoverInfo(jid, '%s#%s' % (node, hash))
|
||||
|
||||
gajim.capscache = CapsCache(gajim.logger)
|
||||
|
||||
|
@ -210,19 +184,14 @@ class ConnectionCaps(object):
|
|||
for xmpp registered in connection_handlers.py'''
|
||||
|
||||
# get the caps element
|
||||
caps=presence.getTag('c')
|
||||
if not caps: return
|
||||
|
||||
node, ver=caps['node'], caps['ver']
|
||||
if node is None or ver is None:
|
||||
# improper caps in stanza, ignoring
|
||||
caps = presence.getTag('c')
|
||||
if not caps:
|
||||
return
|
||||
|
||||
try:
|
||||
exts=caps['ext'].split(' ')
|
||||
except AttributeError:
|
||||
# no exts means no exts, a perfectly valid case
|
||||
exts=[]
|
||||
hash_method, node, hash = caps['hash'], caps['node'], caps['ver']
|
||||
if hash_method is None or node is None or hash is None:
|
||||
# improper caps in stanza, ignoring
|
||||
return
|
||||
|
||||
# we will put these into proper Contact object and ask
|
||||
# for disco... so that disco will learn how to interpret
|
||||
|
@ -231,7 +200,7 @@ class ConnectionCaps(object):
|
|||
jid=str(presence.getFrom())
|
||||
|
||||
# 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)
|
||||
if contact in [None, []]:
|
||||
|
@ -240,25 +209,31 @@ class ConnectionCaps(object):
|
|||
contact = contact[0]
|
||||
|
||||
# overwriting old data
|
||||
contact.caps_node=node
|
||||
contact.caps_ver=ver
|
||||
contact.caps_exts=exts
|
||||
contact.caps_node = node
|
||||
contact.caps_hash_method = hash_method
|
||||
contact.caps_hash = hash
|
||||
|
||||
def _capsDiscoCB(self, jid, node, identities, features, data):
|
||||
contact=gajim.contacts.get_contact_from_full_jid(self.name, jid)
|
||||
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('#', 1)
|
||||
if ext==contact.caps_ver: # this can be also version (like '0.9')
|
||||
exts=None
|
||||
else:
|
||||
exts=(ext,)
|
||||
contact = gajim.contacts.get_contact_from_full_jid(self.name, jid)
|
||||
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, hash = node.split('#', 1)
|
||||
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...
|
||||
caps=gajim.capscache[(node, contact.caps_ver, exts)]
|
||||
if caps.queried==2: return
|
||||
caps = gajim.capscache[(contact.caps_hash_method, hash)]
|
||||
if caps.queried == 2:
|
||||
return
|
||||
|
||||
identities=set((i['category'], i['type'], i.get('name')) for i in identities)
|
||||
caps.update(identities, features)
|
||||
|
||||
|
|
|
@ -82,9 +82,8 @@ 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,
|
||||
hash_method TEXT,
|
||||
hash TEXT,
|
||||
data BLOB);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS rooms_last_message_time(
|
||||
|
|
|
@ -667,7 +667,7 @@ class ConnectionDisco:
|
|||
common.xmpp.NS_DISCO, frm = to)
|
||||
iq.setAttr('id', id)
|
||||
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])
|
||||
for f in (common.xmpp.NS_BYTESTREAM, common.xmpp.NS_SI,
|
||||
common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS):
|
||||
|
@ -744,55 +744,17 @@ class ConnectionDisco:
|
|||
q = iq.getTag('query')
|
||||
if node:
|
||||
q.setAttr('node', node)
|
||||
q.addChild('identity', attrs = {'type': 'pc', 'category': 'client',
|
||||
'name': 'Gajim'})
|
||||
q.addChild('identity', attrs = gajim.gajim_identity)
|
||||
extension = None
|
||||
if node and node.find('#') != -1:
|
||||
extension = node[node.index('#') + 1:]
|
||||
client_version = 'http://gajim.org/caps#' + gajim.version.split('-',
|
||||
1)[0]
|
||||
client_version = 'http://gajim.org#' + gajim.caps_hash
|
||||
|
||||
if node in (None, client_version):
|
||||
q.addChild('feature', attrs = {'var': common.xmpp.NS_BYTESTREAM})
|
||||
q.addChild('feature', attrs = {'var': common.xmpp.NS_SI})
|
||||
q.addChild('feature', attrs = {'var': common.xmpp.NS_FILE})
|
||||
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC})
|
||||
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})
|
||||
for f in gajim.gajim_common_features:
|
||||
q.addChild('feature', attrs = {'var': f})
|
||||
for f in gajim.gajim_optional_features:
|
||||
q.addChild('feature', attrs = {'var': f})
|
||||
|
||||
if q.getChildren():
|
||||
self.connection.send(iq)
|
||||
|
@ -892,16 +854,9 @@ class ConnectionVcard:
|
|||
def add_caps(self, p):
|
||||
''' advertise our capabilities in presence stanza (xep-0115)'''
|
||||
c = p.setTag('c', namespace = common.xmpp.NS_CAPS)
|
||||
c.setAttr('node', 'http://gajim.org/caps')
|
||||
ext = []
|
||||
if not gajim.config.get('ignore_incoming_xhtml'):
|
||||
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])
|
||||
c.setAttr('hash', 'sha-1')
|
||||
c.setAttr('node', 'http://gajim.org')
|
||||
c.setAttr('ver', gajim.caps_hash)
|
||||
return p
|
||||
|
||||
def node_to_dict(self, node):
|
||||
|
|
|
@ -44,8 +44,8 @@ class Contact:
|
|||
# Capabilities; filled by caps.py/ConnectionCaps object
|
||||
# every time it gets these from presence stanzas
|
||||
self.caps_node = None
|
||||
self.caps_ver = None
|
||||
self.caps_exts = None
|
||||
self.caps_hash_method = None
|
||||
self.caps_hash = None
|
||||
|
||||
# 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:
|
||||
|
|
|
@ -2,7 +2,7 @@ docdir = '../'
|
|||
|
||||
datadir = '../'
|
||||
|
||||
version = '0.11.4.3-svn'
|
||||
version = '0.11.4.4-svn'
|
||||
|
||||
import sys, os.path
|
||||
for base in ('.', 'common'):
|
||||
|
|
|
@ -27,6 +27,7 @@ import locale
|
|||
import config
|
||||
from contacts import Contacts
|
||||
from events import Events
|
||||
import xmpp
|
||||
|
||||
try:
|
||||
import defs
|
||||
|
@ -164,6 +165,21 @@ else:
|
|||
if system('gpg -h >/dev/null 2>&1'):
|
||||
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):
|
||||
pos = jid.find('@')
|
||||
return jid[:pos]
|
||||
|
|
|
@ -30,6 +30,8 @@ import urllib
|
|||
import errno
|
||||
import select
|
||||
import sha
|
||||
import hashlib
|
||||
import base64
|
||||
import sys
|
||||
from encodings.punycode import punycode_encode
|
||||
from encodings import idna
|
||||
|
@ -38,7 +40,7 @@ import gajim
|
|||
from i18n import Q_
|
||||
from i18n import ngettext
|
||||
from xmpp_stringprep import nodeprep, resourceprep, nameprep
|
||||
|
||||
import xmpp
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
from osx import nsapp
|
||||
|
@ -1199,3 +1201,93 @@ def prepare_and_validate_gpg_keyID(account, jid, keyID):
|
|||
keyID = 'UNKNOWN'
|
||||
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)
|
||||
|
|
|
@ -698,60 +698,59 @@ class Logger:
|
|||
# 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;''');
|
||||
self.cur.execute('SELECT hash_method, hash, 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 hash_method, hash, 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('\0')
|
||||
data = GzipFile(fileobj=StringIO(str(data))).read().split('\0')
|
||||
i=0
|
||||
identities=set()
|
||||
features=set()
|
||||
while i<(len(data)-2) and data[i]!='FEAT':
|
||||
category=data[i]
|
||||
type=data[i+1]
|
||||
name=data[i+2]
|
||||
identities.add((category,type,name))
|
||||
i+=3
|
||||
identities = list()
|
||||
features = list()
|
||||
while i < (len(data) - 3) and data[i] != 'FEAT':
|
||||
category = data[i]
|
||||
type_ = data[i + 1]
|
||||
lang = data[i + 2]
|
||||
name = data[i + 3]
|
||||
identities.append({'category': category, 'type': type_,
|
||||
'xml:lang': lang, 'name': name})
|
||||
i += 4
|
||||
i+=1
|
||||
while i<len(data):
|
||||
features.add(data[i])
|
||||
i+=1
|
||||
if not ext: ext=None # to make '' a None
|
||||
while i < len(data):
|
||||
features.append(data[i])
|
||||
i += 1
|
||||
|
||||
# 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=[]
|
||||
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)
|
||||
if identity['category'] == 'FEAT':
|
||||
return
|
||||
data.extend((identity.get('category'), identity.get('type', ''),
|
||||
identity.get('xml:lang', ''), identity.get('name', '')))
|
||||
data.append('FEAT')
|
||||
data.extend(features)
|
||||
data = '\0'.join(data)
|
||||
# if there's a need to do more gzip, put that to a function
|
||||
string = StringIO()
|
||||
gzip=GzipFile(fileobj=string, mode='w')
|
||||
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
|
||||
INSERT INTO caps_cache ( hash_method, hash, data )
|
||||
VALUES (?, ?, ?);
|
||||
''', (hash_method, hash, buffer(data))) # (1) -- note above
|
||||
try:
|
||||
self.con.commit()
|
||||
except sqlite.OperationalError, e:
|
||||
|
|
|
@ -180,6 +180,8 @@ class OptionsParser:
|
|||
self.update_config_to_01142()
|
||||
if old < [0, 11, 4, 3] and new >= [0, 11, 4, 3]:
|
||||
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.config.set('version', new_version)
|
||||
|
@ -566,3 +568,30 @@ class OptionsParser:
|
|||
pass
|
||||
con.close()
|
||||
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')
|
||||
|
|
|
@ -33,7 +33,7 @@ import thread
|
|||
import logging
|
||||
log = logging.getLogger('gajim.c.x.transports_nb')
|
||||
|
||||
from common import gajim
|
||||
import common.gajim
|
||||
|
||||
USE_PYOPENSSL = False
|
||||
|
||||
|
@ -755,16 +755,16 @@ class NonBlockingTLS(PlugIn):
|
|||
#tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
|
||||
tcpsock.ssl_errnum = 0
|
||||
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:
|
||||
tcpsock._sslContext.load_verify_locations(cacerts)
|
||||
except:
|
||||
log.warning('Unable to load SSL certificats from file %s' % \
|
||||
os.path.abspath(cacerts))
|
||||
# load users certs
|
||||
if os.path.isfile(gajim.MY_CACERTS):
|
||||
if os.path.isfile(common.gajim.MY_CACERTS):
|
||||
store = tcpsock._sslContext.get_cert_store()
|
||||
f = open(gajim.MY_CACERTS)
|
||||
f = open(common.gajim.MY_CACERTS)
|
||||
lines = f.readlines()
|
||||
i = 0
|
||||
begin = -1
|
||||
|
@ -779,11 +779,11 @@ class NonBlockingTLS(PlugIn):
|
|||
store.add_cert(X509cert)
|
||||
except OpenSSL.crypto.Error, exception_obj:
|
||||
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:
|
||||
log.warning(
|
||||
'Unknown error while loading certificate from file %s' % \
|
||||
gajim.MY_CACERTS)
|
||||
common.gajim.MY_CACERTS)
|
||||
begin = -1
|
||||
i += 1
|
||||
tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock)
|
||||
|
|
|
@ -538,6 +538,7 @@ class PreferencesWindow:
|
|||
if gajim.connections[account].pep_supported:
|
||||
pep.user_send_mood(account, '')
|
||||
self.on_checkbutton_toggled(widget, 'publish_mood')
|
||||
helpers.update_optional_features()
|
||||
|
||||
def on_publish_activity_checkbutton_toggled(self, widget):
|
||||
if widget.get_active() == False:
|
||||
|
@ -545,6 +546,7 @@ class PreferencesWindow:
|
|||
if gajim.connections[account].pep_supported:
|
||||
pep.user_send_activity(account, '')
|
||||
self.on_checkbutton_toggled(widget, 'publish_activity')
|
||||
helpers.update_optional_features()
|
||||
|
||||
def on_publish_tune_checkbutton_toggled(self, widget):
|
||||
if widget.get_active() == False:
|
||||
|
@ -552,17 +554,21 @@ class PreferencesWindow:
|
|||
if gajim.connections[account].pep_supported:
|
||||
pep.user_send_tune(account, '')
|
||||
self.on_checkbutton_toggled(widget, 'publish_tune')
|
||||
helpers.update_optional_features()
|
||||
gajim.interface.roster.enable_syncing_status_msg_from_current_music_track(
|
||||
widget.get_active())
|
||||
|
||||
def on_subscribe_mood_checkbutton_toggled(self, widget):
|
||||
self.on_checkbutton_toggled(widget, 'subscribe_mood')
|
||||
helpers.update_optional_features()
|
||||
|
||||
def on_subscribe_activity_checkbutton_toggled(self, widget):
|
||||
self.on_checkbutton_toggled(widget, 'subscribe_activity')
|
||||
helpers.update_optional_features()
|
||||
|
||||
def on_subscribe_tune_checkbutton_toggled(self, widget):
|
||||
self.on_checkbutton_toggled(widget, 'subscribe_tune')
|
||||
helpers.update_optional_features()
|
||||
|
||||
def on_sort_by_show_checkbutton_toggled(self, widget):
|
||||
self.on_checkbutton_toggled(widget, 'sort_by_show')
|
||||
|
@ -625,6 +631,7 @@ class PreferencesWindow:
|
|||
|
||||
def on_xhtml_checkbutton_toggled(self, widget):
|
||||
self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml')
|
||||
helpers.update_optional_features()
|
||||
|
||||
def apply_speller(self):
|
||||
for acct in gajim.connections:
|
||||
|
@ -719,12 +726,17 @@ class PreferencesWindow:
|
|||
|
||||
def on_outgoing_chat_states_combobox_changed(self, widget):
|
||||
active = widget.get_active()
|
||||
old_value = gajim.config.get('outgoing_chat_state_notifications')
|
||||
if active == 0: # all
|
||||
gajim.config.set('outgoing_chat_state_notifications', 'all')
|
||||
elif active == 1: # only composing
|
||||
gajim.config.set('outgoing_chat_state_notifications', 'composing_only')
|
||||
else: # 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):
|
||||
active = widget.get_active()
|
||||
|
|
|
@ -3106,6 +3106,8 @@ class Interface:
|
|||
if gajim.config.get('autodetect_browser_mailer') or not cfg_was_read:
|
||||
gtkgui_helpers.autodetect_browser_mailer()
|
||||
|
||||
helpers.update_optional_features()
|
||||
|
||||
if gajim.verbose:
|
||||
gajim.log.setLevel(gajim.logging.DEBUG)
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue