Philipp Hörist pushed to branch master at gajim / gajim

Commits:

17 changed files:

Changes:

  • gajim/common/caps_cache.py
    ... ... @@ -29,8 +29,6 @@ through ClientCaps objects which are hold by contact instances.
    29 29
     
    
    30 30
     import logging
    
    31 31
     
    
    32
    -import nbxmpp
    
    33
    -from nbxmpp.const import Affiliation
    
    34 32
     from nbxmpp import (NS_XHTML_IM, NS_ESESSION, NS_CHATSTATES,
    
    35 33
         NS_JINGLE_ICE_UDP, NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO,
    
    36 34
         NS_JINGLE_FILE_TRANSFER_5)
    
    ... ... @@ -52,15 +50,13 @@ FAKED = 3 # allow NullClientCaps to behave as it has a cached item
    52 50
     ################################################################################
    
    53 51
     
    
    54 52
     capscache = None
    
    55
    -muc_caps_cache = None
    
    53
    +
    
    56 54
     def initialize(logger):
    
    57 55
         """
    
    58 56
         Initialize this module
    
    59 57
         """
    
    60 58
         global capscache
    
    61
    -    global muc_caps_cache
    
    62 59
         capscache = CapsCache(logger)
    
    63
    -    muc_caps_cache = MucCapsCache()
    
    64 60
     
    
    65 61
     def client_supports(client_caps, requested_feature):
    
    66 62
         lookup_item = client_caps.get_cache_lookup_strategy()
    
    ... ... @@ -333,87 +329,3 @@ class CapsCache:
    333 329
             key = (hash_method, hash_)
    
    334 330
             if key in self.__cache:
    
    335 331
                 del self.__cache[key]
    336
    -
    
    337
    -
    
    338
    -class MucCapsCache:
    
    339
    -
    
    340
    -    def __init__(self):
    
    341
    -        self.cache = {}
    
    342
    -
    
    343
    -    def append(self, info):
    
    344
    -        if nbxmpp.NS_MUC not in info.features:
    
    345
    -            # Not a MUC, don't cache info
    
    346
    -            return
    
    347
    -
    
    348
    -        self.cache[info.jid] = info
    
    349
    -
    
    350
    -    def is_cached(self, jid):
    
    351
    -        return jid in self.cache
    
    352
    -
    
    353
    -    def supports(self, jid, feature):
    
    354
    -        if jid in self.cache:
    
    355
    -            if feature in self.cache[jid].features:
    
    356
    -                return True
    
    357
    -        return False
    
    358
    -
    
    359
    -    def has_mam(self, jid):
    
    360
    -        try:
    
    361
    -            if nbxmpp.NS_MAM_2 in self.cache[jid].features:
    
    362
    -                return True
    
    363
    -            if nbxmpp.NS_MAM_1 in self.cache[jid].features:
    
    364
    -                return True
    
    365
    -            return False
    
    366
    -        except (KeyError, AttributeError):
    
    367
    -            return False
    
    368
    -
    
    369
    -    def get_mam_namespace(self, jid):
    
    370
    -        try:
    
    371
    -            if nbxmpp.NS_MAM_2 in self.cache[jid].features:
    
    372
    -                return nbxmpp.NS_MAM_2
    
    373
    -            if nbxmpp.NS_MAM_1 in self.cache[jid].features:
    
    374
    -                return nbxmpp.NS_MAM_1
    
    375
    -        except (KeyError, AttributeError):
    
    376
    -            return
    
    377
    -
    
    378
    -    def is_subject_change_allowed(self, jid, affiliation):
    
    379
    -        allowed = True
    
    380
    -        if affiliation in (Affiliation.OWNER, Affiliation.ADMIN):
    
    381
    -            return allowed
    
    382
    -
    
    383
    -        if jid in self.cache:
    
    384
    -            for form in self.cache[jid].dataforms:
    
    385
    -                try:
    
    386
    -                    allowed = form['muc#roominfo_changesubject'].value
    
    387
    -                except KeyError:
    
    388
    -                    pass
    
    389
    -        return allowed
    
    390
    -
    
    391
    -    def is_open(self, jid):
    
    392
    -        return 'muc_membersonly' not in self.cache[jid].features
    
    393
    -
    
    394
    -    def is_password_protected(self, jid):
    
    395
    -        return 'muc_unsecured' not in self.cache[jid].features
    
    396
    -
    
    397
    -    def is_anonymous(self, jid):
    
    398
    -        return 'muc_nonanonymous' not in self.cache[jid].features
    
    399
    -
    
    400
    -    def is_persistent(self, jid):
    
    401
    -        return 'muc_temporary' not in self.cache[jid].features
    
    402
    -
    
    403
    -    def get_room_infos(self, jid):
    
    404
    -        room_info = {}
    
    405
    -        if jid in self.cache:
    
    406
    -            for form in self.cache[jid].dataforms:
    
    407
    -                try:
    
    408
    -                    room_info['name'] = form['muc#roomconfig_roomname'].value
    
    409
    -                except KeyError:
    
    410
    -                    room_info['name'] = None
    
    411
    -                try:
    
    412
    -                    room_info['desc'] = form['muc#roominfo_description'].value
    
    413
    -                except KeyError:
    
    414
    -                    room_info['desc'] = None
    
    415
    -                try:
    
    416
    -                    room_info['lang'] = form['muc#roominfo_lang'].value
    
    417
    -                except KeyError:
    
    418
    -                    room_info['lang'] = None
    
    419
    -        return room_info or None

  • gajim/common/contacts.py
    ... ... @@ -188,11 +188,10 @@ class Contact(CommonContact):
    188 188
             if name:
    
    189 189
                 return name
    
    190 190
     
    
    191
    -        cache = caps_cache.muc_caps_cache.cache
    
    192
    -        if self.jid in cache:
    
    193
    -            name = cache[self.jid].muc_room_name
    
    194
    -            if name:
    
    195
    -                return name
    
    191
    +        disco_info = app.logger.get_last_disco_info(self.jid)
    
    192
    +        if disco_info is not None:
    
    193
    +            if disco_info.muc_room_name:
    
    194
    +                return disco_info.muc_room_name
    
    196 195
             return self.jid.split('@')[0]
    
    197 196
     
    
    198 197
         def get_shown_groups(self):
    

  • gajim/common/helpers.py
    ... ... @@ -65,7 +65,6 @@ from gi.repository import GLib
    65 65
     import precis_i18n.codec  # pylint: disable=unused-import
    
    66 66
     
    
    67 67
     from gajim.common import app
    
    68
    -from gajim.common import caps_cache
    
    69 68
     from gajim.common import configpaths
    
    70 69
     from gajim.common.i18n import Q_
    
    71 70
     from gajim.common.i18n import _
    
    ... ... @@ -1087,10 +1086,11 @@ def update_optional_features(account=None):
    1087 1086
             # Give plugins the possibility to add their features
    
    1088 1087
             app.plugin_manager.extension_point('update_caps', account_)
    
    1089 1088
     
    
    1090
    -        app.caps_hash[account_] = compute_caps_hash(DiscoInfo(
    
    1091
    -            None, None, [app.gajim_identity],
    
    1092
    -            app.gajim_common_features + features,
    
    1093
    -            []), compare=False)
    
    1089
    +        disco_info = DiscoInfo(None,
    
    1090
    +                               [app.gajim_identity],
    
    1091
    +                               app.gajim_common_features + features,
    
    1092
    +                               [])
    
    1093
    +        app.caps_hash[account_] = compute_caps_hash(disco_info, compare=False)
    
    1094 1094
             # re-send presence with new hash
    
    1095 1095
             connected = app.connections[account_].connected
    
    1096 1096
             if connected > 1 and app.SHOW_LIST[connected] != 'invisible':
    
    ... ... @@ -1334,9 +1334,9 @@ def call_counter(func):
    1334 1334
         return helper
    
    1335 1335
     
    
    1336 1336
     def get_sync_threshold(jid, archive_info):
    
    1337
    -    cache = caps_cache.muc_caps_cache
    
    1337
    +    disco_info = app.logger.get_last_disco_info(jid)
    
    1338 1338
         if archive_info is None or archive_info.sync_threshold is None:
    
    1339
    -        if cache.supports(jid, 'muc#roomconfig_membersonly'):
    
    1339
    +        if disco_info is not None and disco_info.muc_is_members_only:
    
    1340 1340
                 threshold = app.config.get('private_room_sync_threshold')
    
    1341 1341
             else:
    
    1342 1342
                 threshold = app.config.get('public_room_sync_threshold')
    

  • gajim/common/logger.py
    ... ... @@ -38,8 +38,10 @@ from collections import namedtuple
    38 38
     from gi.repository import GLib
    
    39 39
     
    
    40 40
     from nbxmpp.protocol import Node
    
    41
    +from nbxmpp.protocol import Iq
    
    41 42
     from nbxmpp.structs import DiscoInfo
    
    42 43
     from nbxmpp.modules.dataforms import extend_form
    
    44
    +from nbxmpp.modules.discovery import parse_disco_info
    
    43 45
     
    
    44 46
     from gajim.common import exceptions
    
    45 47
     from gajim.common import app
    
    ... ... @@ -105,6 +107,11 @@ CACHE_SQL_STATEMENT = '''
    105 107
                 data TEXT,
    
    106 108
                 last_seen INTEGER
    
    107 109
         );
    
    110
    +    CREATE TABLE last_seen_disco_info(
    
    111
    +            jid TEXT PRIMARY KEY UNIQUE,
    
    112
    +            disco_info TEXT,
    
    113
    +            last_seen INTEGER
    
    114
    +    );
    
    108 115
         CREATE TABLE roster_entry(
    
    109 116
                 account_jid_id INTEGER,
    
    110 117
                 jid_id INTEGER,
    
    ... ... @@ -120,7 +127,7 @@ CACHE_SQL_STATEMENT = '''
    120 127
                 group_name TEXT,
    
    121 128
                 PRIMARY KEY (account_jid_id, jid_id, group_name)
    
    122 129
         );
    
    123
    -    PRAGMA user_version=2;
    
    130
    +    PRAGMA user_version=3;
    
    124 131
         '''
    
    125 132
     
    
    126 133
     log = logging.getLogger('gajim.c.logger')
    
    ... ... @@ -170,6 +177,17 @@ def caps_decoder(dict_):
    170 177
                         dataforms=dataforms)
    
    171 178
     
    
    172 179
     
    
    180
    +def _convert_disco_info(disco_info):
    
    181
    +    return parse_disco_info(Iq(node=disco_info))
    
    182
    +
    
    183
    +def _adapt_disco_info(disco_info):
    
    184
    +    return str(disco_info.stanza)
    
    185
    +
    
    186
    +sqlite.register_converter('disco_info', _convert_disco_info)
    
    187
    +
    
    188
    +sqlite.register_adapter(DiscoInfo, _adapt_disco_info)
    
    189
    +
    
    190
    +
    
    173 191
     class Logger:
    
    174 192
         def __init__(self):
    
    175 193
             self._jid_ids = {}
    
    ... ... @@ -179,6 +197,8 @@ class Logger:
    179 197
             self._log_db_path = configpaths.get('LOG_DB')
    
    180 198
             self._cache_db_path = configpaths.get('CACHE_DB')
    
    181 199
     
    
    200
    +        self._disco_info_cache = {}
    
    201
    +
    
    182 202
             self._create_databases()
    
    183 203
             self._migrate_databases()
    
    184 204
             self._connect_databases()
    
    ... ... @@ -299,6 +319,16 @@ class Logger:
    299 319
                     ]
    
    300 320
                 self._execute_multiple(con, statements)
    
    301 321
     
    
    322
    +        if self._get_user_version(con) < 3:
    
    323
    +            statements = [
    
    324
    +                '''CREATE TABLE last_seen_disco_info(
    
    325
    +                    jid TEXT PRIMARY KEY UNIQUE,
    
    326
    +                    disco_info TEXT,
    
    327
    +                    last_seen INTEGER)''',
    
    328
    +                'PRAGMA user_version=3'
    
    329
    +                ]
    
    330
    +            self._execute_multiple(con, statements)
    
    331
    +
    
    302 332
         @staticmethod
    
    303 333
         def _execute_multiple(con, statements):
    
    304 334
             """
    
    ... ... @@ -341,8 +371,10 @@ class Logger:
    341 371
             app.ged.raise_event(event, None, str(error))
    
    342 372
     
    
    343 373
         def _connect_databases(self):
    
    344
    -        self._con = self._connect(
    
    345
    -            self._log_db_path, timeout=20.0, isolation_level='IMMEDIATE')
    
    374
    +        self._con = self._connect(self._log_db_path,
    
    375
    +                                  timeout=20.0,
    
    376
    +                                  isolation_level='IMMEDIATE',
    
    377
    +                                  detect_types=sqlite.PARSE_COLNAMES)
    
    346 378
     
    
    347 379
             self._con.row_factory = self.namedtuple_factory
    
    348 380
     
    
    ... ... @@ -1465,3 +1497,55 @@ class Logger:
    1465 1497
                 self._con.execute(sql, tuple(kwargs.values()) + (jid_id,))
    
    1466 1498
             log.info('Set message archive info: %s %s', jid, kwargs)
    
    1467 1499
             self._timeout_commit()
    
    1500
    +
    
    1501
    +    def get_last_disco_info(self, jid):
    
    1502
    +        """
    
    1503
    +        Get last disco info from jid
    
    1504
    +
    
    1505
    +        :param jid:     The jid
    
    1506
    +
    
    1507
    +        """
    
    1508
    +
    
    1509
    +        if jid in self._disco_info_cache:
    
    1510
    +            return self._disco_info_cache[jid]
    
    1511
    +
    
    1512
    +        sql = '''SELECT disco_info as "disco_info [disco_info]", last_seen FROM
    
    1513
    +                 last_seen_disco_info
    
    1514
    +                 WHERE jid = ?'''
    
    1515
    +        row = self._con.execute(sql, (str(jid),)).fetchone()
    
    1516
    +        if row is None:
    
    1517
    +            return None
    
    1518
    +
    
    1519
    +        disco_info = row.disco_info._replace(timestamp=row.last_seen)
    
    1520
    +        self._disco_info_cache[jid] = disco_info
    
    1521
    +        return disco_info
    
    1522
    +
    
    1523
    +    def set_last_disco_info(self, jid, disco_info):
    
    1524
    +        """
    
    1525
    +        Get last disco info from jid
    
    1526
    +
    
    1527
    +        :param jid:          The jid
    
    1528
    +
    
    1529
    +        :param disco_info:   A DiscoInfo object
    
    1530
    +
    
    1531
    +        """
    
    1532
    +
    
    1533
    +        log.info('Save disco info from %s', jid)
    
    1534
    +
    
    1535
    +        disco_exists = self.get_last_disco_info(jid) is not None
    
    1536
    +        if disco_exists:
    
    1537
    +            sql = '''UPDATE last_seen_disco_info SET
    
    1538
    +                     disco_info = ?, last_seen = ?
    
    1539
    +                     WHERE jid = ?'''
    
    1540
    +
    
    1541
    +            self._con.execute(sql, (disco_info, disco_info.timestamp, str(jid)))
    
    1542
    +
    
    1543
    +        else:
    
    1544
    +            sql = '''INSERT INTO last_seen_disco_info
    
    1545
    +                     (jid, disco_info, last_seen)
    
    1546
    +                     VALUES (?, ?, ?)'''
    
    1547
    +
    
    1548
    +            self._con.execute(sql, (str(jid), disco_info, disco_info.timestamp))
    
    1549
    +
    
    1550
    +        self._disco_info_cache[jid] = disco_info
    
    1551
    +        self._timeout_commit()

  • gajim/common/modules/discovery.py
    ... ... @@ -19,10 +19,9 @@ from nbxmpp.structs import StanzaHandler
    19 19
     from nbxmpp.util import is_error_result
    
    20 20
     
    
    21 21
     from gajim.common import app
    
    22
    -from gajim.common.caps_cache import muc_caps_cache
    
    23 22
     from gajim.common.nec import NetworkIncomingEvent
    
    24 23
     from gajim.common.modules.base import BaseModule
    
    25
    -from gajim.common.connection_handlers_events import InformationEvent
    
    24
    +from gajim.common.nec import NetworkEvent
    
    26 25
     
    
    27 26
     
    
    28 27
     class Discovery(BaseModule):
    
    ... ... @@ -197,39 +196,27 @@ class Discovery(BaseModule):
    197 196
             self._con.connection.send(iq)
    
    198 197
             raise nbxmpp.NodeProcessed
    
    199 198
     
    
    200
    -    def disco_muc(self, jid, callback, update=False):
    
    199
    +    def disco_muc(self, jid, callback=None):
    
    201 200
             if not app.account_is_connected(self._account):
    
    202 201
                 return
    
    203
    -        if muc_caps_cache.is_cached(jid) and not update:
    
    204
    -            callback()
    
    205
    -            return
    
    206 202
     
    
    207
    -        self._log.info('Request MUC info %s', jid)
    
    203
    +        self._log.info('Request MUC info for %s', jid)
    
    208 204
     
    
    209 205
             self.disco_info(jid,
    
    210
    -                        callback=self._muc_info_response,
    
    206
    +                        callback=self._muc_info_received,
    
    211 207
                             user_data=callback)
    
    212 208
     
    
    213
    -    def _muc_info_response(self, result, callback):
    
    214
    -        if is_error_result(result):
    
    215
    -            if result.condition == 'item-not-found':
    
    216
    -                # Groupchat does not exist
    
    217
    -                self._log.info('MUC does not exist: %s', result.jid)
    
    218
    -                callback()
    
    219
    -            else:
    
    220
    -                self._log.info('MUC disco error: %s', result)
    
    221
    -                app.nec.push_incoming_event(
    
    222
    -                    InformationEvent(
    
    223
    -                        None,
    
    224
    -                        dialog_name='unable-join-groupchat',
    
    225
    -                        kwargs={
    
    226
    -                            'server': result.jid,
    
    227
    -                            'error': str(result)}))
    
    228
    -            return
    
    229
    -
    
    209
    +    def _muc_info_received(self, result, callback):
    
    230 210
             self._log.info('MUC info received: %s', result.jid)
    
    231
    -        muc_caps_cache.append(result)
    
    232
    -        callback()
    
    211
    +        if not is_error_result(result):
    
    212
    +            app.logger.set_last_disco_info(result.jid, result)
    
    213
    +            app.nec.push_incoming_event(NetworkEvent(
    
    214
    +                'muc-disco-update',
    
    215
    +                account=self._account,
    
    216
    +                room_jid=result.jid))
    
    217
    +
    
    218
    +        if callback is not None:
    
    219
    +            callback(result)
    
    233 220
     
    
    234 221
     
    
    235 222
     def get_instance(*args, **kwargs):
    

  • gajim/common/modules/iq.py
    ... ... @@ -15,7 +15,6 @@
    15 15
     # Iq handler
    
    16 16
     
    
    17 17
     import nbxmpp
    
    18
    -from nbxmpp.const import Error
    
    19 18
     from nbxmpp.structs import StanzaHandler
    
    20 19
     
    
    21 20
     from gajim.common import app
    
    ... ... @@ -37,13 +36,13 @@ class Iq(BaseModule):
    37 36
     
    
    38 37
         def _iq_error_received(self, _con, _stanza, properties):
    
    39 38
             self._log.info('Error: %s', properties.error)
    
    40
    -        if properties.error.type in (Error.JID_MALFORMED,
    
    41
    -                                     Error.FORBIDDEN,
    
    42
    -                                     Error.NOT_ACCEPTABLE):
    
    39
    +        if properties.error.condition in ('jid-malformed',
    
    40
    +                                          'forbidden',
    
    41
    +                                          'not-acceptable'):
    
    43 42
                 sid = self._get_sid(properties.id)
    
    44 43
                 file_props = FilesProp.getFileProp(self._account, sid)
    
    45 44
                 if file_props:
    
    46
    -                if properties.error.type == Error.JID_MALFORMED:
    
    45
    +                if properties.error.condition == 'jid-malformed':
    
    47 46
                         file_props.error = -3
    
    48 47
                     else:
    
    49 48
                         file_props.error = -4
    
    ... ... @@ -52,11 +51,11 @@ class Iq(BaseModule):
    52 51
                                      conn=self._con,
    
    53 52
                                      jid=properties.jid.getBare(),
    
    54 53
                                      file_props=file_props,
    
    55
    -                                 error_msg=properties.error.message))
    
    54
    +                                 error_msg=properties.error.get_text()))
    
    56 55
                     self._con.get_module('Bytestream').disconnect_transfer(file_props)
    
    57 56
                     raise nbxmpp.NodeProcessed
    
    58 57
     
    
    59
    -        if properties.error.type == Error.ITEM_NOT_FOUND:
    
    58
    +        if properties.error.condition == 'item-not-found':
    
    60 59
                 sid = self._get_sid(properties.id)
    
    61 60
                 file_props = FilesProp.getFileProp(self._account, sid)
    
    62 61
                 if file_props:
    

  • gajim/common/modules/mam.py
    ... ... @@ -26,7 +26,6 @@ from gajim.common.nec import NetworkIncomingEvent
    26 26
     from gajim.common.const import ArchiveState
    
    27 27
     from gajim.common.const import KindConstant
    
    28 28
     from gajim.common.const import SyncThreshold
    
    29
    -from gajim.common.caps_cache import muc_caps_cache
    
    30 29
     from gajim.common.helpers import get_sync_threshold
    
    31 30
     from gajim.common.helpers import AdditionalDataDict
    
    32 31
     from gajim.common.modules.misc import parse_oob
    
    ... ... @@ -112,7 +111,8 @@ class MAM(BaseModule):
    112 111
     
    
    113 112
             if properties.type.is_groupchat:
    
    114 113
                 archive_jid = properties.jid.getBare()
    
    115
    -            namespace = muc_caps_cache.get_mam_namespace(archive_jid)
    
    114
    +            disco_info = app.logger.get_last_disco_info(archive_jid)
    
    115
    +            namespace = disco_info.mam_namespace
    
    116 116
                 timestamp = properties.timestamp
    
    117 117
                 if namespace is None:
    
    118 118
                     # MUC History
    
    ... ... @@ -536,10 +536,12 @@ class MAM(BaseModule):
    536 536
         def _get_archive_query(self, query_id, jid=None, start=None, end=None,
    
    537 537
                                with_=None, after=None, max_=70):
    
    538 538
             # Muc archive query?
    
    539
    -        namespace = muc_caps_cache.get_mam_namespace(jid)
    
    540
    -        if namespace is None:
    
    539
    +        disco_info = app.logger.get_last_disco_info(jid)
    
    540
    +        if disco_info is None:
    
    541 541
                 # Query to our own archive
    
    542 542
                 namespace = self.archiving_namespace
    
    543
    +        else:
    
    544
    +            namespace = disco_info.mam_namespace
    
    543 545
     
    
    544 546
             iq = nbxmpp.Iq('set', to=jid)
    
    545 547
             query = iq.addChild('query', namespace=namespace)
    

  • gajim/common/modules/message.py
    ... ... @@ -25,7 +25,6 @@ from gajim.common.i18n import _
    25 25
     from gajim.common.nec import NetworkIncomingEvent
    
    26 26
     from gajim.common.nec import NetworkEvent
    
    27 27
     from gajim.common.helpers import AdditionalDataDict
    
    28
    -from gajim.common.caps_cache import muc_caps_cache
    
    29 28
     from gajim.common.const import KindConstant
    
    30 29
     from gajim.common.modules.base import BaseModule
    
    31 30
     from gajim.common.modules.util import get_eme_message
    
    ... ... @@ -293,8 +292,8 @@ class Message(BaseModule):
    293 292
                                             message_id=event.message_id)
    
    294 293
     
    
    295 294
         def _check_for_mam_compliance(self, room_jid, stanza_id):
    
    296
    -        namespace = muc_caps_cache.get_mam_namespace(room_jid)
    
    297
    -        if stanza_id is None and namespace == nbxmpp.NS_MAM_2:
    
    295
    +        disco_info = app.logger.get_last_disco_info(room_jid)
    
    296
    +        if stanza_id is None and disco_info.mam_namespace == nbxmpp.NS_MAM_2:
    
    298 297
                 self._log.warning('%s announces mam:2 without stanza-id', room_jid)
    
    299 298
     
    
    300 299
         def _get_unique_id(self, properties):
    
    ... ... @@ -306,8 +305,9 @@ class Message(BaseModule):
    306 305
                 return None, None
    
    307 306
     
    
    308 307
             if properties.type.is_groupchat:
    
    309
    -            namespace = muc_caps_cache.get_mam_namespace(
    
    308
    +            disco_info = app.logger.get_last_disco_info(
    
    310 309
                     properties.jid.getBare())
    
    310
    +            namespace = disco_info.mam_namespace
    
    311 311
                 archive = properties.jid
    
    312 312
             else:
    
    313 313
                 namespace = self._con.get_module('MAM').archiving_namespace
    

  • gajim/common/modules/muc.py
    ... ... @@ -20,12 +20,10 @@ import logging
    20 20
     from datetime import datetime
    
    21 21
     from datetime import timedelta
    
    22 22
     from datetime import timezone
    
    23
    -from functools import partial
    
    24 23
     
    
    25 24
     import nbxmpp
    
    26 25
     from nbxmpp.const import InviteType
    
    27 26
     from nbxmpp.const import PresenceType
    
    28
    -from nbxmpp.const import Error
    
    29 27
     from nbxmpp.structs import StanzaHandler
    
    30 28
     from nbxmpp.util import is_error_result
    
    31 29
     
    
    ... ... @@ -41,7 +39,6 @@ from gajim.common.helpers import get_default_muc_config
    41 39
     from gajim.common.helpers import get_sync_threshold
    
    42 40
     from gajim.common.helpers import to_user_string
    
    43 41
     from gajim.common import idle
    
    44
    -from gajim.common.caps_cache import muc_caps_cache
    
    45 42
     from gajim.common.nec import NetworkEvent
    
    46 43
     from gajim.common.modules.bits_of_binary import store_bob_data
    
    47 44
     from gajim.common.modules.base import BaseModule
    
    ... ... @@ -145,7 +142,20 @@ class MUC(BaseModule):
    145 142
             self._muc_data[muc_data.jid] = muc_data
    
    146 143
     
    
    147 144
             self._con.get_module('Discovery').disco_muc(
    
    148
    -            muc_data.jid, partial(self._join, muc_data))
    
    145
    +            muc_data.jid,
    
    146
    +            callback=self._on_disco_result)
    
    147
    +
    
    148
    +    def _on_disco_result(self, result):
    
    149
    +        if is_error_result(result):
    
    150
    +            self._log.info('Disco %s failed: %s', result.jid, result.get_text())
    
    151
    +            app.nec.push_incoming_event(
    
    152
    +                NetworkEvent('muc-join-failed',
    
    153
    +                             account=self._account,
    
    154
    +                             room_jid=result.jid.getBare(),
    
    155
    +                             error=result))
    
    156
    +            return
    
    157
    +
    
    158
    +        self._join(self._get_muc_data(result.jid))
    
    149 159
     
    
    150 160
         def _join(self, muc_data):
    
    151 161
             show = helpers.get_xmpp_show(app.SHOW_LIST[self._con.connected])
    
    ... ... @@ -228,8 +238,7 @@ class MUC(BaseModule):
    228 238
                 account=self._account,
    
    229 239
                 room_jid=result.jid))
    
    230 240
     
    
    231
    -        self._con.get_module('Discovery').disco_muc(
    
    232
    -            result.jid, partial(self._on_disco_update, result.jid), update=True)
    
    241
    +        self._con.get_module('Discovery').disco_muc(result.jid)
    
    233 242
     
    
    234 243
             # If this is an automatic room creation
    
    235 244
             try:
    
    ... ... @@ -245,12 +254,6 @@ class MUC(BaseModule):
    245 254
             for jid in invites:
    
    246 255
                 self.invite(result.jid, jid)
    
    247 256
     
    
    248
    -    def _on_disco_update(self, room_jid):
    
    249
    -        app.nec.push_incoming_event(NetworkEvent(
    
    250
    -            'muc-disco-update',
    
    251
    -            account=self._account,
    
    252
    -            room_jid=room_jid))
    
    253
    -
    
    254 257
         def update_presence(self, auto=False):
    
    255 258
             mucs = self.get_mucs_with_state([MUCJoinedState.JOINED,
    
    256 259
                                              MUCJoinedState.JOINING])
    
    ... ... @@ -291,7 +294,8 @@ class MUC(BaseModule):
    291 294
                 status=self._con.status)
    
    292 295
     
    
    293 296
         def _add_history_query(self, muc_x, room_jid):
    
    294
    -        if muc_caps_cache.has_mam(room_jid):
    
    297
    +        disco_info = app.logger.get_last_disco_info(room_jid)
    
    298
    +        if disco_info.has_mam:
    
    295 299
                 # The room is MAM capable dont get MUC History
    
    296 300
                 muc_x.setTag('history', {'maxchars': '0'})
    
    297 301
             else:
    
    ... ... @@ -322,23 +326,27 @@ class MUC(BaseModule):
    322 326
                 return
    
    323 327
     
    
    324 328
             if muc_data.state == MUCJoinedState.JOINING:
    
    325
    -            if properties.error.type == Error.CONFLICT:
    
    329
    +            if properties.error.condition == 'conflict':
    
    326 330
                     muc_data.nick += '_'
    
    327 331
                     self._log.info('Nickname conflict: %s change to %s',
    
    328 332
                                    muc_data.jid, muc_data.nick)
    
    329 333
                     self._join(muc_data)
    
    330
    -            elif properties.error.type == Error.NOT_AUTHORIZED:
    
    334
    +            elif properties.error.condition == 'not-authorized':
    
    331 335
                     self._raise_muc_event('muc-password-required', properties)
    
    332 336
                 else:
    
    333
    -                self._raise_muc_event('muc-join-failed', properties)
    
    334 337
                     self._set_muc_state(room_jid, MUCJoinedState.NOT_JOINED)
    
    338
    +                app.nec.push_incoming_event(
    
    339
    +                    NetworkEvent('muc-join-failed',
    
    340
    +                                 account=self._account,
    
    341
    +                                 room_jid=room_jid,
    
    342
    +                                 error=properties.error))
    
    335 343
     
    
    336 344
             elif muc_data.state == MUCJoinedState.CAPTCHA_REQUEST:
    
    337 345
                 app.nec.push_incoming_event(
    
    338 346
                     NetworkEvent('muc-captcha-error',
    
    339 347
                                  account=self._account,
    
    340 348
                                  room_jid=room_jid,
    
    341
    -                             error_text=properties.error.message))
    
    349
    +                             error_text=properties.error.get_text()))
    
    342 350
                 self._set_muc_state(room_jid, MUCJoinedState.CAPTCHA_FAILED)
    
    343 351
                 self._set_muc_state(room_jid, MUCJoinedState.NOT_JOINED)
    
    344 352
     
    

  • gajim/dialog_messages.py
    ... ... @@ -104,11 +104,6 @@ messages = {
    104 104
               '%(error_msg)s'),
    
    105 105
             ErrorDialog),
    
    106 106
     
    
    107
    -    'unable-join-groupchat': Message(
    
    108
    -        _('Unable to Join Group Chat'),
    
    109
    -        '%(server)s\n%(error)s',
    
    110
    -        ErrorDialog),
    
    111
    -
    
    112 107
         'gstreamer-error': Message(
    
    113 108
             _('GStreamer Error'),
    
    114 109
             _('Error: %(error)s\nDebug: %(debug)s'),
    

  • gajim/gajim.py
    ... ... @@ -26,7 +26,7 @@ from distutils.version import LooseVersion as V
    26 26
     
    
    27 27
     from gajim.common import i18n
    
    28 28
     
    
    29
    -_MIN_NBXMPP_VER = "0.9.91"
    
    29
    +_MIN_NBXMPP_VER = "0.9.92"
    
    30 30
     _MIN_GTK_VER = "3.22.0"
    
    31 31
     _MIN_CAIRO_VER = "1.14.0"
    
    32 32
     
    

  • gajim/groupchat_control.py
    ... ... @@ -53,7 +53,6 @@ from gajim import message_control
    53 53
     from gajim import vcard
    
    54 54
     
    
    55 55
     from gajim.common.const import AvatarSize
    
    56
    -from gajim.common.caps_cache import muc_caps_cache
    
    57 56
     from gajim.common import events
    
    58 57
     from gajim.common import app
    
    59 58
     from gajim.common import helpers
    
    ... ... @@ -371,6 +370,10 @@ class GroupchatControl(ChatControlBase):
    371 370
         def room_name(self):
    
    372 371
             return self.contact.get_shown_name()
    
    373 372
     
    
    373
    +    @property
    
    374
    +    def disco_info(self):
    
    375
    +        return app.logger.get_last_disco_info(self.contact.jid)
    
    376
    +
    
    374 377
         def add_actions(self):
    
    375 378
             super().add_actions()
    
    376 379
             actions = [
    
    ... ... @@ -421,8 +424,10 @@ class GroupchatControl(ChatControlBase):
    421 424
             self.parent_win.window.add_action(act)
    
    422 425
     
    
    423 426
             # Enable notify on all for private rooms
    
    424
    -        members_only = muc_caps_cache.supports(self.contact.jid,
    
    425
    -                                               'muc#roomconfig_membersonly')
    
    427
    +        members_only = False
    
    428
    +        if self.disco_info is not None:
    
    429
    +            members_only = self.disco_info.muc_is_members_only
    
    430
    +
    
    426 431
             value = app.config.get_per(
    
    427 432
                 'rooms', self.contact.jid, 'notify_on_all_messages', members_only)
    
    428 433
     
    
    ... ... @@ -486,12 +491,9 @@ class GroupchatControl(ChatControlBase):
    486 491
                                                            role.is_visitor)
    
    487 492
     
    
    488 493
             # Change Subject
    
    489
    -        subject = False
    
    490
    -        if contact is not None:
    
    491
    -            subject = muc_caps_cache.is_subject_change_allowed(
    
    492
    -                self.room_jid, contact.affiliation)
    
    494
    +        subject_change = self._is_subject_change_allowed()
    
    493 495
             self._get_action('change-subject-').set_enabled(self.is_connected and
    
    494
    -                                                        subject)
    
    496
    +                                                        subject_change)
    
    495 497
     
    
    496 498
             # Change Nick
    
    497 499
             self._get_action('change-nickname-').set_enabled(self.is_connected)
    
    ... ... @@ -517,7 +519,9 @@ class GroupchatControl(ChatControlBase):
    517 519
             self.sendfile_button.set_tooltip_text(tooltip_text)
    
    518 520
     
    
    519 521
             # Upload Avatar
    
    520
    -        vcard_support = muc_caps_cache.supports(self.room_jid, nbxmpp.NS_VCARD)
    
    522
    +        vcard_support = False
    
    523
    +        if self.disco_info is not None:
    
    524
    +            vcard_support = self.disco_info.supports(nbxmpp.NS_VCARD)
    
    521 525
             self._get_action('upload-avatar-').set_enabled(
    
    522 526
                 self.is_connected and
    
    523 527
                 vcard_support and
    
    ... ... @@ -537,6 +541,19 @@ class GroupchatControl(ChatControlBase):
    537 541
             self._get_action('print-status-').set_state(
    
    538 542
                 GLib.Variant.new_boolean(value))
    
    539 543
     
    
    544
    +    def _is_subject_change_allowed(self):
    
    545
    +        contact = app.contacts.get_gc_contact(
    
    546
    +            self.account, self.room_jid, self.nick)
    
    547
    +        if contact is None:
    
    548
    +            return False
    
    549
    +
    
    550
    +        if contact.affiliation in (Affiliation.OWNER, Affiliation.ADMIN):
    
    551
    +            return True
    
    552
    +
    
    553
    +        if self.disco_info is None:
    
    554
    +            return False
    
    555
    +        return self.disco_info.muc_subjectmod
    
    556
    +
    
    540 557
         def _get_action(self, name):
    
    541 558
             win = self.parent_win.window
    
    542 559
             return win.lookup_action(name + self.control_id)
    
    ... ... @@ -623,14 +640,13 @@ class GroupchatControl(ChatControlBase):
    623 640
             self.update_actions()
    
    624 641
     
    
    625 642
             # After the room has been created, reevaluate threshold
    
    626
    -        if muc_caps_cache.has_mam(self.contact.jid):
    
    643
    +        if self.disco_info.has_mam:
    
    627 644
                 archive_info = app.logger.get_archive_infos(self.contact.jid)
    
    628 645
                 threshold = helpers.get_sync_threshold(self.contact.jid,
    
    629 646
                                                        archive_info)
    
    630 647
                 win.change_action_state('choose-sync-%s' % self.control_id,
    
    631 648
                                         GLib.Variant('s', str(threshold)))
    
    632 649
     
    
    633
    -
    
    634 650
         def _connect_window_state_change(self, parent_win):
    
    635 651
             if self._state_change_handler_id is None:
    
    636 652
                 id_ = parent_win.window.connect('notify::is-maximized',
    
    ... ... @@ -643,8 +659,7 @@ class GroupchatControl(ChatControlBase):
    643 659
             self.leave()
    
    644 660
     
    
    645 661
         def _on_information(self, action, param):
    
    646
    -        disco_info = muc_caps_cache.cache[self.contact.jid]
    
    647
    -        self._muc_info_box.set_from_disco_info(disco_info)
    
    662
    +        self._muc_info_box.set_from_disco_info(self.disco_info)
    
    648 663
             if self._subject_data is not None:
    
    649 664
                 self._muc_info_box.set_subject(self._subject_data.subject)
    
    650 665
                 self._muc_info_box.set_author(self._subject_data.nickname,
    
    ... ... @@ -1386,7 +1401,7 @@ class GroupchatControl(ChatControlBase):
    1386 1401
                 changes.append(_('A setting not related to privacy has been '
    
    1387 1402
                                  'changed'))
    
    1388 1403
                 app.connections[self.account].get_module('Discovery').disco_muc(
    
    1389
    -                self.room_jid, self.update_actions, update=True)
    
    1404
    +                self.room_jid)
    
    1390 1405
     
    
    1391 1406
             if StatusCode.CONFIG_ROOM_LOGGING in event.status_codes:
    
    1392 1407
                 # Can be a presence (see chg_contact_status in groupchat_control.py)
    
    ... ... @@ -1479,7 +1494,7 @@ class GroupchatControl(ChatControlBase):
    1479 1494
                 GLib.source_remove(self.autorejoin)
    
    1480 1495
             self.autorejoin = None
    
    1481 1496
     
    
    1482
    -        if muc_caps_cache.has_mam(self.room_jid):
    
    1497
    +        if self.disco_info.has_mam:
    
    1483 1498
                 # Request MAM
    
    1484 1499
                 con = app.connections[self.account]
    
    1485 1500
                 con.get_module('MAM').request_archive_on_muc_join(
    
    ... ... @@ -1925,17 +1940,14 @@ class GroupchatControl(ChatControlBase):
    1925 1940
     
    
    1926 1941
         @event_filter(['account', 'room_jid'])
    
    1927 1942
         def _on_muc_join_failed(self, event):
    
    1928
    -        self.xml.error_label.set_text(event.properties.error.message)
    
    1943
    +        self.xml.error_label.set_text(event.error.get_text())
    
    1929 1944
             self._show_page('error')
    
    1930 1945
             self.autorejoin = False
    
    1931 1946
     
    
    1932 1947
         @event_filter(['account', 'room_jid'])
    
    1933 1948
         def _on_presence_error(self, event):
    
    1934
    -        error_type = event.properties.error.type
    
    1935
    -        error_message = event.properties.error.message
    
    1936
    -
    
    1937
    -        self.add_info_message(
    
    1938
    -            'Error %s: %s' % (error_type.value, error_message))
    
    1949
    +        error_message = event.properties.error.get_text()
    
    1950
    +        self.add_info_message('Error: %s' % error_message)
    
    1939 1951
     
    
    1940 1952
         def add_contact_to_roster(self, nick):
    
    1941 1953
             contact = app.contacts.get_gc_contact(self.account,
    

  • gajim/gtk/groupchat_config.py
    ... ... @@ -22,7 +22,6 @@ from gi.repository import Gtk
    22 22
     from gajim.common import app
    
    23 23
     from gajim.common.i18n import _
    
    24 24
     from gajim.common.const import MUCUser
    
    25
    -from gajim.common.caps_cache import muc_caps_cache
    
    26 25
     
    
    27 26
     from gajim.gtk.dialogs import ErrorDialog
    
    28 27
     from gajim.gtk.dataform import DataFormWidget
    
    ... ... @@ -53,7 +52,8 @@ class GroupchatConfig(Gtk.ApplicationWindow):
    53 52
                 self._ui.add_button.set_sensitive(True)
    
    54 53
                 self._ui.add_button.set_tooltip_text('')
    
    55 54
     
    
    56
    -        visible = muc_caps_cache.supports(jid, nbxmpp.NS_REGISTER)
    
    55
    +        disco_info = app.logger.get_last_disco_info(self.jid)
    
    56
    +        visible = disco_info.supports(nbxmpp.NS_REGISTER)
    
    57 57
             self._ui.reserved_name_column.set_visible(visible)
    
    58 58
             self._ui.info_button.set_sensitive(False)
    
    59 59
     
    

  • gajim/gtk/groupchat_join.py
    ... ... @@ -73,17 +73,14 @@ class GroupchatJoin(Gtk.ApplicationWindow):
    73 73
             self.add(self._main_box)
    
    74 74
             self.show_all()
    
    75 75
     
    
    76
    -        self._disco_muc(jid)
    
    76
    +        con = app.connections[self.account]
    
    77
    +        con.get_module('Discovery').disco_muc(
    
    78
    +            jid, callback=self._disco_info_received)
    
    77 79
     
    
    78 80
         def _on_page_changed(self, stack, _param):
    
    79 81
             name = stack.get_visible_child_name()
    
    80 82
             self._join_button.set_sensitive(name == 'info')
    
    81 83
     
    
    82
    -    def _disco_muc(self, jid):
    
    83
    -        con = app.connections[self.account]
    
    84
    -        con.get_module('Discovery').disco_info(
    
    85
    -            jid, callback=self._disco_info_received)
    
    86
    -
    
    87 84
         @ensure_not_destroyed
    
    88 85
         def _disco_info_received(self, result):
    
    89 86
             if is_error_result(result):
    

  • gajim/gtk/start_chat.py
    ... ... @@ -14,6 +14,7 @@
    14 14
     
    
    15 15
     import locale
    
    16 16
     from enum import IntEnum
    
    17
    +from functools import partial
    
    17 18
     
    
    18 19
     from gi.repository import Gdk
    
    19 20
     from gi.repository import Gtk
    
    ... ... @@ -275,11 +276,11 @@ class StartChatDialog(Gtk.ApplicationWindow):
    275 276
         def _disco_muc(self, account, jid):
    
    276 277
             self._ui.stack.set_visible_child_name('progress')
    
    277 278
             con = app.connections[account]
    
    278
    -        con.get_module('Discovery').disco_info(
    
    279
    -            jid, callback=self._disco_info_received, user_data=account)
    
    279
    +        con.get_module('Discovery').disco_muc(
    
    280
    +            jid, callback=partial(self._disco_info_received, account))
    
    280 281
     
    
    281 282
         @ensure_not_destroyed
    
    282
    -    def _disco_info_received(self, result, account):
    
    283
    +    def _disco_info_received(self, account, result):
    
    283 284
             if is_error_result(result):
    
    284 285
                 self._set_error(result)
    
    285 286
     
    

  • setup.cfg
    ... ... @@ -22,7 +22,7 @@ test_suite = test
    22 22
     install_requires =
    
    23 23
         cssutils>=1.0.2
    
    24 24
         keyring
    
    25
    -    nbxmpp>=0.9.91
    
    25
    +    nbxmpp>=0.9.92
    
    26 26
         precis-i18n>=1.0.0
    
    27 27
         pyOpenSSL>=16.2
    
    28 28
         pycairo>=1.14.0
    

  • test/no_gui/unit/test_caps_cache.py
    ... ... @@ -61,7 +61,7 @@ class TestCapsCache(CommonCapsTest):
    61 61
         def test_set_and_store(self):
    
    62 62
             ''' Test client_caps update gets logged into db '''
    
    63 63
     
    
    64
    -        disco_info = DiscoInfo(None, None, self.identities, self.features, [])
    
    64
    +        disco_info = DiscoInfo(None, self.identities, self.features, [])
    
    65 65
     
    
    66 66
             item = self.cc[self.client_caps]
    
    67 67
             item.set_and_store(disco_info)