From 9133960fbc636ec63e67e3e20826d2a09ca6d6cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Tue, 2 Jun 2009 22:48:32 +0200 Subject: [PATCH 03/15] * added one table in logs.db to save roster * added "roster_version" in configuration to save roster version for the account * added "ver" attribute when gajim requests the roster --- src/common/check_paths.py | 6 ++++++ src/common/config.py | 1 + src/common/connection_handlers.py | 4 +++- src/common/optparser.py | 25 +++++++++++++++++++++++++ src/common/xmpp/client_nb.py | 4 ++-- src/common/xmpp/roster_nb.py | 8 ++++++-- 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/common/check_paths.py b/src/common/check_paths.py index 8b24cccb9..1632d3875 100644 --- a/src/common/check_paths.py +++ b/src/common/check_paths.py @@ -96,6 +96,12 @@ def create_log_db(): jid_id INTEGER PRIMARY KEY UNIQUE, time INTEGER ); + + CREATE TABLE IF NOT EXISTS roster_entry( + account_jid_id INTEGER PRIMARY KEY, + jid_id INTEGER, + subscription INTEGER + ); ''' ) diff --git a/src/common/config.py b/src/common/config.py index 6796a83a2..f1ef7abe1 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -342,6 +342,7 @@ class Config: 'ignore_unknown_contacts': [ opt_bool, False ], 'send_os_info': [ opt_bool, True ], 'log_encrypted_sessions': [opt_bool, True, _('When negotiating an encrypted session, should Gajim assume you want your messages to be logged?')], + 'roster_version': [opt_str, ''], }, {}), 'statusmsg': ({ 'message': [ opt_str, '' ], diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 934b2f88a..5eca5bf1d 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -1164,7 +1164,9 @@ class ConnectionVcard: if iq_obj.getErrorCode() not in ('403', '406', '404'): self.private_storage_supported = False # We can now continue connection by requesting the roster - self.connection.initRoster() + version = gajim.config.get_per('accounts', self.name, + 'roster_version') + self.connection.initRoster(version=version) elif self.awaiting_answers[id_][0] == PRIVACY_ARRIVED: if iq_obj.getType() != 'error': self.privacy_rules_supported = True diff --git a/src/common/optparser.py b/src/common/optparser.py index 975330518..b927fb079 100644 --- a/src/common/optparser.py +++ b/src/common/optparser.py @@ -671,4 +671,29 @@ class OptionsParser: gajim.config.set_per('soundevents', evt, 'path', path) gajim.config.set('version', '0.12.1.5') + def update_config_to_01216(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( + ''' + CREATE TABLE IF NOT EXISTS roster_entry( + account_jid_id INTEGER PRIMARY KEY, + jid_id INTEGER, + subscription INTEGER + ); + ''' + ) + con.commit() + except sqlite.OperationalError: + pass + con.close() + gajim.config.set('version', '0.12.1.6') + + + + # vim: se ts=3: diff --git a/src/common/xmpp/client_nb.py b/src/common/xmpp/client_nb.py index 554be2199..e24277824 100644 --- a/src/common/xmpp/client_nb.py +++ b/src/common/xmpp/client_nb.py @@ -503,10 +503,10 @@ class NonBlockingClient: self.NonBlockingBind.NonBlockingBind(self._Resource, self._on_sasl_auth) return True - def initRoster(self): + def initRoster(self, version=''): ''' Plug in the roster. ''' if not self.__dict__.has_key('NonBlockingRoster'): - roster_nb.NonBlockingRoster.get_instance().PlugIn(self) + roster_nb.NonBlockingRoster.get_instance(version=version).PlugIn(self) def getRoster(self, on_ready=None): ''' Return the Roster instance, previously plugging it in and diff --git a/src/common/xmpp/roster_nb.py b/src/common/xmpp/roster_nb.py index cd802b36b..7f257fefa 100644 --- a/src/common/xmpp/roster_nb.py +++ b/src/common/xmpp/roster_nb.py @@ -36,9 +36,10 @@ class NonBlockingRoster(PlugIn): You can also use mapping interface for access to the internal representation of contacts in roster. ''' - def __init__(self): + def __init__(self, version=''): ''' Init internal variables. ''' PlugIn.__init__(self) + self.version = version self._data = {} self.set=None self._exported_methods=[self.getRoster] @@ -48,7 +49,10 @@ class NonBlockingRoster(PlugIn): (or if the 'force' argument is set). ''' if self.set is None: self.set=0 elif not force: return - self._owner.send(Iq('get',NS_ROSTER)) + + iq = Iq('get',NS_ROSTER) + iq.setTagAttr('query', 'ver', self.version) + self._owner.send(iq) log.info('Roster requested from server') def RosterIqHandler(self,dis,stanza): From 0a3d26e5d42dc95d8b3d0f00148974cbe5232288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Tue, 23 Jun 2009 19:29:25 +0200 Subject: [PATCH 04/15] * added another table to handle roster group * we now handle roster push and roster reception * changed version number in src/common/defs.py --- src/common/check_paths.py | 13 ++++++-- src/common/connection_handlers.py | 9 ++++++ src/common/defs.py | 2 +- src/common/logger.py | 52 +++++++++++++++++++++++++++++++ src/common/optparser.py | 15 +++++++-- 5 files changed, 86 insertions(+), 5 deletions(-) diff --git a/src/common/check_paths.py b/src/common/check_paths.py index 1632d3875..a732e4327 100644 --- a/src/common/check_paths.py +++ b/src/common/check_paths.py @@ -98,9 +98,18 @@ def create_log_db(): ); CREATE TABLE IF NOT EXISTS roster_entry( - account_jid_id INTEGER PRIMARY KEY, + account_jid_id INTEGER, jid_id INTEGER, - subscription INTEGER + name TEXT, + subscription INTEGER, + PRIMARY KEY (account_jid_id, jid_id) + ); + + CREATE TABLE IF NOT EXISTS roster_group( + account_jid_id INTEGER, + jid_id INTEGER, + group_name TEXT, + PRIMARY KEY (account_jid_id, jid_id, group_name) ); ''' ) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 5eca5bf1d..b9ad25d6b 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -1542,6 +1542,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, def _rosterSetCB(self, con, iq_obj): log.debug('rosterSetCB') + version = iq_obj.getTagAttr('query', 'ver') for item in iq_obj.getTag('query').getChildren(): jid = helpers.parse_jid(item.getAttr('jid')) name = item.getAttr('name') @@ -1551,6 +1552,14 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, for group in item.getTags('group'): groups.append(group.getData()) self.dispatch('ROSTER_INFO', (jid, name, sub, ask, groups)) + if version: + account_jid = '%s@%s' % ( + gajim.config.get_per('accounts', self.name, 'name'), + gajim.config.get_per('accounts', self.name, 'hostname')) + gajim.logger.add_or_update_contact(account_jid, jid, name, sub, + groups) + gajim.config.set_per('accounts', self.name, 'roster_version', + version) if not self.connection or self.connected < 2: raise common.xmpp.NodeProcessed reply = common.xmpp.Iq(typ='result', attrs={'id': iq_obj.getID()}, diff --git a/src/common/defs.py b/src/common/defs.py index 29101e0c9..6a1b8438f 100644 --- a/src/common/defs.py +++ b/src/common/defs.py @@ -27,7 +27,7 @@ docdir = '../' datadir = '../' localedir = '../po' -version = '0.12.1.5-svn' +version = '0.12.1.6-hg' import sys, os.path for base in ('.', 'common'): diff --git a/src/common/logger.py b/src/common/logger.py index 9fd4fe995..ca83320a9 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -92,6 +92,13 @@ class Constants: self.TYPE_MRIM, ) = range(14) + ( + self.SUBSCRIPTION_NONE, + self.SUBSCRIPTION_TO, + self.SUBSCRIPTION_FROM, + self.SUBSCRIPTION_BOTH, + ) = range(4) + constants = Constants() class Logger: @@ -331,6 +338,16 @@ class Logger: if type_id == constants.TYPE_MRIM: return 'mrim' + def convert_human_subscription_values_to_db_api_values(self, sub): + if sub == 'none': + return constants.SUBSCRIPTION_NONE + if sub == 'to': + return constants.SUBSCRIPTION_TO + if sub == 'from': + return constants.SUBSCRIPTION_FROM + if sub == 'both': + return constants.SUBSCRIPTION_BOTH + def commit_to_db(self, values, write_unread = False): sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message, subject) VALUES (?, ?, ?, ?, ?, ?, ?)' try: @@ -799,4 +816,39 @@ class Logger: except sqlite.OperationalError, e: print >> sys.stderr, str(e) + def del_contact(self, account_jid, jid): + try: + account_jid_id = self.get_jid_id(account_jid) + jid_id = self.get_jid_id(jid) + except exceptions.PysqliteOperationalError, e: + raise exceptions.PysqliteOperationalError(str(e)) + sql = 'DELETE FROM roster_entry WHERE account_jid_id = %d AND jid_id = %d' % (account_jid_id, jid_id) + self.simple_commit(sql) + + def add_or_update_contact(self, account_jid, jid, name, sub, groups): + if sub == 'remove': + self.del_contact(account_jid, jid) + return + + try: + account_jid_id = self.get_jid_id(account_jid) + jid_id = self.get_jid_id(jid) + except exceptions.PysqliteOperationalError, e: + raise exceptions.PysqliteOperationalError(str(e)) + + # update groups information + # first we delete all previous groups information + sql = 'DELETE FROM roster_group WHERE account_jid_id = %d AND jid_id = %d' % (account_jid_id, jid_id) + self.cur.execute(sql) + # then we add all new groups information + for group in groups: + sql = 'INSERT INTO roster_group VALUES("%d", "%d", "%s")' % ( + account_jid_id, jid_id, group) + self.cur.execute(sql) + + sql = 'REPLACE INTO roster_entry VALUES("%d", "%d", "%s", "%d")' % ( + account_jid_id, jid_id, name, + self.convert_human_subscription_values_to_db_api_values(sub)) + self.simple_commit(sql) + # vim: se ts=3: diff --git a/src/common/optparser.py b/src/common/optparser.py index b927fb079..9cf3d56b6 100644 --- a/src/common/optparser.py +++ b/src/common/optparser.py @@ -198,6 +198,8 @@ class OptionsParser: self.update_config_to_01214() if old < [0, 12, 1, 5] and new >= [0, 12, 1, 5]: self.update_config_to_01215() + if old < [0, 12, 1, 6] and new >= [0, 12, 1, 6]: + self.update_config_to_01216() gajim.logger.init_vars() gajim.config.set('version', new_version) @@ -681,9 +683,18 @@ class OptionsParser: cur.executescript( ''' CREATE TABLE IF NOT EXISTS roster_entry( - account_jid_id INTEGER PRIMARY KEY, + account_jid_id INTEGER, jid_id INTEGER, - subscription INTEGER + name TEXT, + subscription INTEGER, + PRIMARY KEY (account_jid_id, jid_id) + ); + + CREATE TABLE IF NOT EXISTS roster_group( + account_jid_id INTEGER, + jid_id INTEGER, + group_name TEXT, + PRIMARY KEY (account_jid_id, jid_id, group_name) ); ''' ) From f2e214a0339f3620768743c94960617f2e14f25c Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Tue, 23 Jun 2009 22:24:39 +0200 Subject: [PATCH 05/15] use helper function --- src/common/connection_handlers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index b9ad25d6b..abda5877a 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -1553,9 +1553,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, groups.append(group.getData()) self.dispatch('ROSTER_INFO', (jid, name, sub, ask, groups)) if version: - account_jid = '%s@%s' % ( - gajim.config.get_per('accounts', self.name, 'name'), - gajim.config.get_per('accounts', self.name, 'hostname')) + account_jid = gajim.get_jid_from_account(self.name) gajim.logger.add_or_update_contact(account_jid, jid, name, sub, groups) gajim.config.set_per('accounts', self.name, 'roster_version', From d347b79c38a5cb8aeff8dad2d57dfac192a24b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Sat, 27 Jun 2009 17:56:04 +0200 Subject: [PATCH 06/15] * added a method to load roster from DB * now we load roster from DB when we receive an iq result for the roster without any query * added a "force" argument to NonBlockingRoster.getRoster(), to force the return of the instance --- src/common/connection_handlers.py | 19 +++++++-- src/common/logger.py | 67 +++++++++++++++++++++++++++++-- src/common/xmpp/client_nb.py | 6 +-- src/common/xmpp/roster_nb.py | 25 +++++++++--- 4 files changed, 101 insertions(+), 16 deletions(-) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index abda5877a..96f03244e 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -65,6 +65,7 @@ VCARD_PUBLISHED = 'vcard_published' VCARD_ARRIVED = 'vcard_arrived' AGENT_REMOVED = 'agent_removed' METACONTACTS_ARRIVED = 'metacontacts_arrived' +ROSTER_ARRIVED = 'roster_arrived' PRIVACY_ARRIVED = 'privacy_arrived' PEP_CONFIG = 'pep_config' HAS_IDLE = True @@ -1166,7 +1167,16 @@ class ConnectionVcard: # We can now continue connection by requesting the roster version = gajim.config.get_per('accounts', self.name, 'roster_version') - self.connection.initRoster(version=version) + iq_id = self.connection.initRoster(version=version) + self.awaiting_answers[iq_id] = (ROSTER_ARRIVED, ) + elif self.awaiting_answers[id_][0] == ROSTER_ARRIVED: + if iq_obj.getType() == 'result': + if not iq_obj.getTag('query'): + account_jid = gajim.get_jid_from_account(self.name) + roster_data = gajim.logger.get_roster(account_jid) + roster = self.connection.getRoster(force=True) + roster.setRaw(roster_data) + self._getRoster() elif self.awaiting_answers[id_][0] == PRIVACY_ARRIVED: if iq_obj.getType() != 'error': self.privacy_rules_supported = True @@ -2418,7 +2428,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, self.connection.send(result) raise common.xmpp.NodeProcessed - def _getRosterCB(self, con, iq_obj): + def _getRoster(self): log.debug('getRosterCB') if not self.connection: return @@ -2441,6 +2451,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, gajim.proxy65_manager.resolve(proxy, self.connection, our_jid) def _on_roster_set(self, roster): + roster_version = roster.version raw_roster = roster.getRaw() roster = {} our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name)) @@ -2480,6 +2491,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, self.discoverInfo(jid) self.dispatch('ROSTER', roster) + gajim.logger.replace_roster(self.name, roster_version, roster) + print raw_roster def _send_first_presence(self, signed = ''): show = self.continue_connect_info[0] @@ -2621,8 +2634,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, common.xmpp.NS_MUC_OWNER) con.RegisterHandler('iq', self._MucAdminCB, 'result', common.xmpp.NS_MUC_ADMIN) - con.RegisterHandler('iq', self._getRosterCB, 'result', - common.xmpp.NS_ROSTER) con.RegisterHandler('iq', self._PrivateCB, 'result', common.xmpp.NS_PRIVATE) con.RegisterHandler('iq', self._HttpAuthCB, 'get', diff --git a/src/common/logger.py b/src/common/logger.py index ca83320a9..102300c9e 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -339,6 +339,7 @@ class Logger: return 'mrim' def convert_human_subscription_values_to_db_api_values(self, sub): + '''converts from string style to constant ints for db''' if sub == 'none': return constants.SUBSCRIPTION_NONE if sub == 'to': @@ -348,6 +349,17 @@ class Logger: if sub == 'both': return constants.SUBSCRIPTION_BOTH + def convert_db_api_values_to_human_subscription_values(self, sub): + '''converts from constant ints for db to string style''' + if sub == constants.SUBSCRIPTION_NONE: + return 'none' + if sub == constants.SUBSCRIPTION_TO: + return 'to' + if sub == constants.SUBSCRIPTION_FROM: + return 'from' + if sub == constants.SUBSCRIPTION_BOTH: + return 'both' + def commit_to_db(self, values, write_unread = False): sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message, subject) VALUES (?, ?, ?, ?, ?, ?, ?)' try: @@ -816,7 +828,30 @@ class Logger: except sqlite.OperationalError, e: print >> sys.stderr, str(e) + def replace_roster(self, account_name, roster_version, roster): + ''' Replace current roster in DB by a new one. + accout_name is the name of the account to change + roster_version is the version of the new roster + roster is the new version ''' + gajim.config.set_per('accounts', account_name, 'roster_version', '') + account_jid = gajim.get_jid_from_account(account_name) + account_jid_id = self.get_jid_id(account_jid) + + # Delete old roster + sql = 'DELETE FROM roster_entry WHERE account_jid_id = %d' % ( + account_jid_id) + sql = 'DELETE FROM roster_group WHERE account_jid_id = %d' % ( + account_jid_id) + + # Fill roster tables with the new roster + for jid in roster: + self.add_or_update_contact(account_jid, jid, roster[jid]['name'], + roster[jid]['subscription'], roster[jid]['groups']) + gajim.config.set_per('accounts', account_name, 'roster_version', + roster_version) + def del_contact(self, account_jid, jid): + ''' Remove jid from account_jid roster. ''' try: account_jid_id = self.get_jid_id(account_jid) jid_id = self.get_jid_id(jid) @@ -826,6 +861,7 @@ class Logger: self.simple_commit(sql) def add_or_update_contact(self, account_jid, jid, name, sub, groups): + ''' Add or update a contact from account_jid roster. ''' if sub == 'remove': self.del_contact(account_jid, jid) return @@ -836,11 +872,11 @@ class Logger: except exceptions.PysqliteOperationalError, e: raise exceptions.PysqliteOperationalError(str(e)) - # update groups information - # first we delete all previous groups information + # Update groups information + # First we delete all previous groups information sql = 'DELETE FROM roster_group WHERE account_jid_id = %d AND jid_id = %d' % (account_jid_id, jid_id) self.cur.execute(sql) - # then we add all new groups information + # Then we add all new groups information for group in groups: sql = 'INSERT INTO roster_group VALUES("%d", "%d", "%s")' % ( account_jid_id, jid_id, group) @@ -851,4 +887,29 @@ class Logger: self.convert_human_subscription_values_to_db_api_values(sub)) self.simple_commit(sql) + def get_roster(self, account_jid): + ''' Return the accound_jid roster in NonBlockingRoster format. ''' + data = {} + account_jid_id = self.get_jid_id(account_jid) + + # First we fill data with roster_entry informations + self.cur.execute('SELECT j.jid, re.jid_id, re.name, re.subscription FROM roster_entry re, jids j WHERE re.account_jid_id="%(account_jid_id)s" AND j.jid_id=re.jid_id' % {'account_jid_id': account_jid_id}) + for jid, jid_id, name, subscription in self.cur: + data[jid] = {} + data[jid]['name'] = name + data[jid]['subscription'] = self.convert_db_api_values_to_human_subscription_values(subscription) + data[jid]['groups'] = [] + data[jid]['resources'] = {} + data[jid]['ask'] = None + data[jid]['id'] = jid_id + + # Then we add group for roster entries + for jid in data: + self.cur.execute('SELECT group_name FROM roster_group WHERE account_jid_id="%(account_jid_id)s" AND jid_id="%(jid_id)s"' % {'account_jid_id': account_jid_id, 'jid_id': data[jid]['id']}) + for (group_name,) in self.cur: + data[jid]['groups'].append(group_name) + del data[jid]['id'] + + return data + # vim: se ts=3: diff --git a/src/common/xmpp/client_nb.py b/src/common/xmpp/client_nb.py index e24277824..c89cffb2c 100644 --- a/src/common/xmpp/client_nb.py +++ b/src/common/xmpp/client_nb.py @@ -506,13 +506,13 @@ class NonBlockingClient: def initRoster(self, version=''): ''' Plug in the roster. ''' if not self.__dict__.has_key('NonBlockingRoster'): - roster_nb.NonBlockingRoster.get_instance(version=version).PlugIn(self) + return roster_nb.NonBlockingRoster.get_instance(version=version).PlugIn(self) - def getRoster(self, on_ready=None): + def getRoster(self, on_ready=None, force=False): ''' Return the Roster instance, previously plugging it in and requesting roster from server if needed. ''' if self.__dict__.has_key('NonBlockingRoster'): - return self.NonBlockingRoster.getRoster(on_ready) + return self.NonBlockingRoster.getRoster(on_ready, force) return None def sendPresence(self, jid=None, typ=None, requestRoster=0): diff --git a/src/common/xmpp/roster_nb.py b/src/common/xmpp/roster_nb.py index 7f257fefa..196604114 100644 --- a/src/common/xmpp/roster_nb.py +++ b/src/common/xmpp/roster_nb.py @@ -52,8 +52,11 @@ class NonBlockingRoster(PlugIn): iq = Iq('get',NS_ROSTER) iq.setTagAttr('query', 'ver', self.version) + id_ = self._owner.getAnID() + iq.setID(id_) self._owner.send(iq) log.info('Roster requested from server') + return id_ def RosterIqHandler(self,dis,stanza): ''' Subscription tracker. Used internally for setting items state in @@ -64,6 +67,9 @@ class NonBlockingRoster(PlugIn): return query = stanza.getTag('query') if query: + self.version = stanza.getTagAttr('query', 'ver') + if self.version is None: + self.version = '' for item in query.getTags('item'): jid=item.getAttr('jid') if item.getAttr('subscription')=='remove': @@ -192,6 +198,11 @@ class NonBlockingRoster(PlugIn): def getRaw(self): '''Returns the internal data representation of the roster.''' return self._data + def setRaw(self, data): + '''Returns the internal data representation of the roster.''' + self._data = data + self._data[self._owner.User+'@'+self._owner.Server]={'resources':{},'name':None,'ask':None,'subscription':None,'groups':None,} + self.set=1 # copypasted methods for roster.py from constructor to here @@ -203,7 +214,7 @@ class NonBlockingRoster(PlugIn): self._owner.RegisterHandler('iq', self.RosterIqHandler, 'set', NS_ROSTER) self._owner.RegisterHandler('presence', self.PresenceHandler) if request: - self.Request() + return self.Request() def _on_roster_set(self, data): if data: @@ -216,16 +227,18 @@ class NonBlockingRoster(PlugIn): self.on_ready = None return True - def getRoster(self, on_ready=None): + def getRoster(self, on_ready=None, force=False): ''' Requests roster from server if neccessary and returns self. ''' + return_self = True if not self.set: self.on_ready = on_ready self._owner.onreceive(self._on_roster_set) - return - if on_ready: + return_self = False + elif on_ready: on_ready(self) - on_ready = None - else: + return_self = False + if return_self or force: return self + return None # vim: se ts=3: From b3fbd598665f28d608333b5c7e311a9725209965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Sat, 27 Jun 2009 20:54:49 +0200 Subject: [PATCH 07/15] * added 'ask' support * fixed a bug (when a contact does not have a name) --- src/common/check_paths.py | 1 + src/common/connection_handlers.py | 2 +- src/common/logger.py | 29 ++++++++++++++++++++--------- src/common/optparser.py | 1 + 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/common/check_paths.py b/src/common/check_paths.py index a732e4327..027b9d5fd 100644 --- a/src/common/check_paths.py +++ b/src/common/check_paths.py @@ -102,6 +102,7 @@ def create_log_db(): jid_id INTEGER, name TEXT, subscription INTEGER, + ask BOOLEAN, PRIMARY KEY (account_jid_id, jid_id) ); diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 96f03244e..8ca4e63c2 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -1565,7 +1565,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, if version: account_jid = gajim.get_jid_from_account(self.name) gajim.logger.add_or_update_contact(account_jid, jid, name, sub, - groups) + ask, groups) gajim.config.set_per('accounts', self.name, 'roster_version', version) if not self.connection or self.connected < 2: diff --git a/src/common/logger.py b/src/common/logger.py index 102300c9e..0c95a0b4b 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -846,7 +846,8 @@ class Logger: # Fill roster tables with the new roster for jid in roster: self.add_or_update_contact(account_jid, jid, roster[jid]['name'], - roster[jid]['subscription'], roster[jid]['groups']) + roster[jid]['subscription'], roster[jid]['ask'], + roster[jid]['groups']) gajim.config.set_per('accounts', account_name, 'roster_version', roster_version) @@ -860,7 +861,7 @@ class Logger: sql = 'DELETE FROM roster_entry WHERE account_jid_id = %d AND jid_id = %d' % (account_jid_id, jid_id) self.simple_commit(sql) - def add_or_update_contact(self, account_jid, jid, name, sub, groups): + def add_or_update_contact(self, account_jid, jid, name, sub, ask, groups): ''' Add or update a contact from account_jid roster. ''' if sub == 'remove': self.del_contact(account_jid, jid) @@ -882,9 +883,13 @@ class Logger: account_jid_id, jid_id, group) self.cur.execute(sql) - sql = 'REPLACE INTO roster_entry VALUES("%d", "%d", "%s", "%d")' % ( - account_jid_id, jid_id, name, - self.convert_human_subscription_values_to_db_api_values(sub)) + if name is None: + name = '' + + sql = 'REPLACE INTO roster_entry VALUES("%d", "%d", "%s", "%s", "%d")'\ + % (account_jid_id, jid_id, name, + self.convert_human_subscription_values_to_db_api_values(sub), + bool(ask)) self.simple_commit(sql) def get_roster(self, account_jid): @@ -893,14 +898,20 @@ class Logger: account_jid_id = self.get_jid_id(account_jid) # First we fill data with roster_entry informations - self.cur.execute('SELECT j.jid, re.jid_id, re.name, re.subscription FROM roster_entry re, jids j WHERE re.account_jid_id="%(account_jid_id)s" AND j.jid_id=re.jid_id' % {'account_jid_id': account_jid_id}) - for jid, jid_id, name, subscription in self.cur: + self.cur.execute('SELECT j.jid, re.jid_id, re.name, re.subscription, re.ask FROM roster_entry re, jids j WHERE re.account_jid_id="%(account_jid_id)s" AND j.jid_id=re.jid_id' % {'account_jid_id': account_jid_id}) + for jid, jid_id, name, subscription, ask in self.cur: data[jid] = {} - data[jid]['name'] = name + if name: + data[jid]['name'] = name + else: + data[jid]['name'] = None data[jid]['subscription'] = self.convert_db_api_values_to_human_subscription_values(subscription) data[jid]['groups'] = [] data[jid]['resources'] = {} - data[jid]['ask'] = None + if ask: + data[jid]['ask'] = 'subscribe' + else: + data[jid]['ask'] = None data[jid]['id'] = jid_id # Then we add group for roster entries diff --git a/src/common/optparser.py b/src/common/optparser.py index 9cf3d56b6..6907dc4b9 100644 --- a/src/common/optparser.py +++ b/src/common/optparser.py @@ -687,6 +687,7 @@ class OptionsParser: jid_id INTEGER, name TEXT, subscription INTEGER, + ask BOOLEAN, PRIMARY KEY (account_jid_id, jid_id) ); From ff2be614836de1bdaaa43ece49424aae19089490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Sun, 28 Jun 2009 20:43:18 +0200 Subject: [PATCH 08/15] * clean roster tables when we remove an account --- src/common/logger.py | 15 +++++++++++++-- src/config.py | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/common/logger.py b/src/common/logger.py index 0c95a0b4b..be4840a97 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -858,7 +858,7 @@ class Logger: jid_id = self.get_jid_id(jid) except exceptions.PysqliteOperationalError, e: raise exceptions.PysqliteOperationalError(str(e)) - sql = 'DELETE FROM roster_entry WHERE account_jid_id = %d AND jid_id = %d' % (account_jid_id, jid_id) + sql = 'DELETE FROM roster_entry WHERE account_jid_id=%d AND jid_id=%d' % (account_jid_id, jid_id) self.simple_commit(sql) def add_or_update_contact(self, account_jid, jid, name, sub, ask, groups): @@ -875,7 +875,7 @@ class Logger: # Update groups information # First we delete all previous groups information - sql = 'DELETE FROM roster_group WHERE account_jid_id = %d AND jid_id = %d' % (account_jid_id, jid_id) + sql = 'DELETE FROM roster_group WHERE account_jid_id=%d AND jid_id=%d' % (account_jid_id, jid_id) self.cur.execute(sql) # Then we add all new groups information for group in groups: @@ -923,4 +923,15 @@ class Logger: return data + def remove_roster(self, account_jid): + account_jid_id = self.get_jid_id(account_jid) + + sql = 'DELETE FROM roster_group WHERE account_jid_id=%d' % ( + account_jid_id) + self.cur.execute(sql) + + sql = 'DELETE FROM roster_entry WHERE account_jid_id=%d' % ( + account_jid_id) + self.simple_commit(sql) + # vim: se ts=3: diff --git a/src/config.py b/src/config.py index 85d3c014a..867e6837f 100644 --- a/src/config.py +++ b/src/config.py @@ -2632,6 +2632,7 @@ class RemoveAccountWindow: gajim.interface.roster.close_all(self.account, force = True) gajim.connections[self.account].disconnect(on_purpose = True) del gajim.connections[self.account] + gajim.logger.remove_roster(gajim.get_jid_from_account(self.account)) gajim.config.del_per('accounts', self.account) gajim.interface.save_config() del gajim.interface.instances[self.account] From 5e4fb8fddb39c6a6e46c3c098f15ee0fcdbc0c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Mon, 29 Jun 2009 15:49:46 +0200 Subject: [PATCH 09/15] * in order to permit roster loading at gajim startup we need to save all roster pushs (even if it's not for versioning) --- src/common/connection_handlers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 8ca4e63c2..73f2160b7 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -1562,10 +1562,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, for group in item.getTags('group'): groups.append(group.getData()) self.dispatch('ROSTER_INFO', (jid, name, sub, ask, groups)) + account_jid = gajim.get_jid_from_account(self.name) + gajim.logger.add_or_update_contact(account_jid, jid, name, sub, ask, + groups) if version: - account_jid = gajim.get_jid_from_account(self.name) - gajim.logger.add_or_update_contact(account_jid, jid, name, sub, - ask, groups) gajim.config.set_per('accounts', self.name, 'roster_version', version) if not self.connection or self.connected < 2: From 4d10bdcf6f0661562424bc4fb34526374a012f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Tue, 30 Jun 2009 11:46:27 +0200 Subject: [PATCH 10/15] * In order to allow the viewing of the roster when we are offline (and some other cool stuffs), with and without roster versioning, now we load roster when RosterWindow is created (at gajim startup). Fixes #3190 --- src/common/connection_handlers.py | 6 ++++-- src/common/xmpp/roster_nb.py | 2 ++ src/gajim.py | 1 + src/roster_window.py | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 73f2160b7..2c3453ef8 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -2452,6 +2452,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, def _on_roster_set(self, roster): roster_version = roster.version + received_from_server = roster.received_from_server raw_roster = roster.getRaw() roster = {} our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name)) @@ -2490,9 +2491,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, # we can't determine which iconset to use self.discoverInfo(jid) - self.dispatch('ROSTER', roster) - gajim.logger.replace_roster(self.name, roster_version, roster) print raw_roster + gajim.logger.replace_roster(self.name, roster_version, roster) + if received_from_server: + self.dispatch('ROSTER', roster) def _send_first_presence(self, signed = ''): show = self.continue_connect_info[0] diff --git a/src/common/xmpp/roster_nb.py b/src/common/xmpp/roster_nb.py index 196604114..7ebd5870c 100644 --- a/src/common/xmpp/roster_nb.py +++ b/src/common/xmpp/roster_nb.py @@ -43,6 +43,7 @@ class NonBlockingRoster(PlugIn): self._data = {} self.set=None self._exported_methods=[self.getRoster] + self.received_from_server = False def Request(self,force=0): ''' Request roster from server if it were not yet requested @@ -67,6 +68,7 @@ class NonBlockingRoster(PlugIn): return query = stanza.getTag('query') if query: + self.received_from_server = True self.version = stanza.getTagAttr('query', 'ver') if self.version is None: self.version = '' diff --git a/src/gajim.py b/src/gajim.py index b486c2d2c..868e00e74 100644 --- a/src/gajim.py +++ b/src/gajim.py @@ -3423,6 +3423,7 @@ class Interface: gtk.window_set_default_icon(pix) self.roster = roster_window.RosterWindow() + self.roster.init_roster() self.init_emoticons() self.make_regexps() diff --git a/src/roster_window.py b/src/roster_window.py index 8b152de04..ef7c7b0be 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -6176,6 +6176,11 @@ class RosterWindow: # #self.xml.get_widget('menubar').hide() # return + def init_roster(self): + for account in gajim.connections: + roster = gajim.logger.get_roster(gajim.get_jid_from_account(account)) + gajim.handlers['ROSTER'](account, roster) + ################################################################################ ### ################################################################################ From 6c56dd173bbdd38ca2cbd816ed8984d09ecf0d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Tue, 30 Jun 2009 17:06:17 +0200 Subject: [PATCH 11/15] * now there is no more sparkling when we update the RosterWindow with the roster received from the server (when server does not handle roster versioning or when it resends the whole roster) --- src/common/connection_handlers.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 2c3453ef8..d75719ec2 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -2491,10 +2491,17 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, # we can't determine which iconset to use self.discoverInfo(jid) - print raw_roster gajim.logger.replace_roster(self.name, roster_version, roster) if received_from_server: - self.dispatch('ROSTER', roster) + for contact in gajim.contacts.iter_contacts(self.name): + if contact.jid not in roster: + self.dispatch('ROSTER_INFO', (self.name, + (contact.jid, None, None, None, ()))) + for jid in roster: + self.dispatch('ROSTER_INFO', (jid, roster[jid]['name'], + roster[jid]['subscription'], roster[jid]['ask'], + roster[jid]['groups'])) + #self.dispatch('ROSTER', roster) def _send_first_presence(self, signed = ''): show = self.continue_connect_info[0] From 23394732c2ea099b5f90cc096b70a1c9a7c9e083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Mon, 6 Jul 2009 22:34:24 +0200 Subject: [PATCH 12/15] * fixed a bug when we have a groupchat minimized in the roster --- src/common/connection_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index d75719ec2..d935a9096 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -2494,7 +2494,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, gajim.logger.replace_roster(self.name, roster_version, roster) if received_from_server: for contact in gajim.contacts.iter_contacts(self.name): - if contact.jid not in roster: + if not contact.is_groupchat() and contact.jid not in roster: self.dispatch('ROSTER_INFO', (self.name, (contact.jid, None, None, None, ()))) for jid in roster: From 5e2d6efd0b8f2e0b6a7d21a52e7492de6f59626c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Tue, 7 Jul 2009 15:14:56 +0200 Subject: [PATCH 13/15] * fixed a bug when we remove a contact from our contact list (before we did not remove contact from roster_group table) --- src/common/logger.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/logger.py b/src/common/logger.py index be4840a97..fc5913b15 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -858,6 +858,8 @@ class Logger: jid_id = self.get_jid_id(jid) except exceptions.PysqliteOperationalError, e: raise exceptions.PysqliteOperationalError(str(e)) + sql = 'DELETE FROM roster_group WHERE account_jid_id=%d AND jid_id=%d' % (account_jid_id, jid_id) + self.cur.execute(sql) sql = 'DELETE FROM roster_entry WHERE account_jid_id=%d AND jid_id=%d' % (account_jid_id, jid_id) self.simple_commit(sql) From c66aee5b9d0c8544d6e408e33b4468381ee12543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Thu, 9 Jul 2009 14:33:18 +0200 Subject: [PATCH 14/15] * better way to propagate roster at start --- src/common/connection.py | 5 +++++ src/gajim.py | 3 ++- src/roster_window.py | 5 ----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/common/connection.py b/src/common/connection.py index 362631095..3e1fbdf5e 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -1951,6 +1951,11 @@ class Connection(ConnectionHandlers): self.connection.SendAndCallForResponse(iq, _on_response) + def load_roster_from_db(self): + roster = gajim.logger.get_roster(gajim.get_jid_from_account(self.name)) + self.dispatch('ROSTER', roster) + + # END Connection # vim: se ts=3: diff --git a/src/gajim.py b/src/gajim.py index 868e00e74..33e7f575e 100644 --- a/src/gajim.py +++ b/src/gajim.py @@ -3423,7 +3423,8 @@ class Interface: gtk.window_set_default_icon(pix) self.roster = roster_window.RosterWindow() - self.roster.init_roster() + for account in gajim.connections: + gajim.connections[account].load_roster_from_db() self.init_emoticons() self.make_regexps() diff --git a/src/roster_window.py b/src/roster_window.py index ef7c7b0be..8b152de04 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -6176,11 +6176,6 @@ class RosterWindow: # #self.xml.get_widget('menubar').hide() # return - def init_roster(self): - for account in gajim.connections: - roster = gajim.logger.get_roster(gajim.get_jid_from_account(account)) - gajim.handlers['ROSTER'](account, roster) - ################################################################################ ### ################################################################################ From 3b4b086bdfdafecc1154e999ac7cc0854da35bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Verrier?= Date: Thu, 9 Jul 2009 14:34:51 +0200 Subject: [PATCH 15/15] * the good version is not 0.12.1.6-hg but 0.12.3.1-dev --- configure.ac | 2 +- src/common/defs.py | 2 +- src/common/optparser.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index b4e09eeb0..9730c0d38 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_INIT([Gajim - A Jabber Instant Messager], - [0.12.1.5-svn],[http://trac.gajim.org/],[gajim]) + [0.12.3.1-dev],[http://trac.gajim.org/],[gajim]) AC_PREREQ([2.59]) AC_CONFIG_HEADER(config.h) diff --git a/src/common/defs.py b/src/common/defs.py index 6a1b8438f..c321440d0 100644 --- a/src/common/defs.py +++ b/src/common/defs.py @@ -27,7 +27,7 @@ docdir = '../' datadir = '../' localedir = '../po' -version = '0.12.1.6-hg' +version = '0.12.3.1-dev' import sys, os.path for base in ('.', 'common'): diff --git a/src/common/optparser.py b/src/common/optparser.py index 6907dc4b9..de887eb85 100644 --- a/src/common/optparser.py +++ b/src/common/optparser.py @@ -198,8 +198,8 @@ class OptionsParser: self.update_config_to_01214() if old < [0, 12, 1, 5] and new >= [0, 12, 1, 5]: self.update_config_to_01215() - if old < [0, 12, 1, 6] and new >= [0, 12, 1, 6]: - self.update_config_to_01216() + if old < [0, 12, 3, 1] and new >= [0, 12, 3, 1]: + self.update_config_to_01231() gajim.logger.init_vars() gajim.config.set('version', new_version) @@ -673,7 +673,7 @@ class OptionsParser: gajim.config.set_per('soundevents', evt, 'path', path) gajim.config.set('version', '0.12.1.5') - def update_config_to_01216(self): + def update_config_to_01231(self): back = os.getcwd() os.chdir(logger.LOG_DB_FOLDER) con = sqlite.connect(logger.LOG_DB_FILE) @@ -703,7 +703,7 @@ class OptionsParser: except sqlite.OperationalError: pass con.close() - gajim.config.set('version', '0.12.1.6') + gajim.config.set('version', '0.12.3.1')