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

Commits:

14 changed files:

Changes:

  • gajim/__init__.py
    1 1
     import subprocess
    
    2 2
     
    
    3
    -__version__ = "0.16.11.1"
    
    3
    +__version__ = "0.16.11.2"
    
    4 4
     
    
    5 5
     try:
    
    6 6
         node = subprocess.Popen('git rev-parse --short=12 HEAD', shell=True,
    

  • gajim/common/caps_cache.py
    ... ... @@ -33,10 +33,12 @@ through ClientCaps objects which are hold by contact instances.
    33 33
     
    
    34 34
     import base64
    
    35 35
     import hashlib
    
    36
    +from collections import namedtuple
    
    36 37
     
    
    37 38
     import logging
    
    38 39
     log = logging.getLogger('gajim.c.caps_cache')
    
    39 40
     
    
    41
    +import nbxmpp
    
    40 42
     from nbxmpp import (NS_XHTML_IM, NS_ESESSION, NS_CHATSTATES,
    
    41 43
         NS_JINGLE_ICE_UDP, NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO,
    
    42 44
         NS_JINGLE_FILE_TRANSFER_5)
    
    ... ... @@ -44,7 +46,7 @@ from nbxmpp import (NS_XHTML_IM, NS_ESESSION, NS_CHATSTATES,
    44 46
     FEATURE_BLACKLIST = [NS_CHATSTATES, NS_XHTML_IM, NS_ESESSION,
    
    45 47
         NS_JINGLE_ICE_UDP, NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO,
    
    46 48
         NS_JINGLE_FILE_TRANSFER_5]
    
    47
    -
    
    49
    +from gajim.common import app
    
    48 50
     # Query entry status codes
    
    49 51
     NEW = 0
    
    50 52
     QUERIED = 1
    
    ... ... @@ -56,12 +58,15 @@ FAKED = 3 # allow NullClientCaps to behave as it has a cached item
    56 58
     ################################################################################
    
    57 59
     
    
    58 60
     capscache = None
    
    61
    +muc_caps_cache = None
    
    59 62
     def initialize(logger):
    
    60 63
         """
    
    61 64
         Initialize this module
    
    62 65
         """
    
    63 66
         global capscache
    
    67
    +    global muc_caps_cache
    
    64 68
         capscache = CapsCache(logger)
    
    69
    +    muc_caps_cache = MucCapsCache()
    
    65 70
     
    
    66 71
     def client_supports(client_caps, requested_feature):
    
    67 72
         lookup_item = client_caps.get_cache_lookup_strategy()
    
    ... ... @@ -438,3 +443,51 @@ class CapsCache(object):
    438 443
             key = (hash_method, hash)
    
    439 444
             if key in self.__cache:
    
    440 445
                 del self.__cache[key]
    
    446
    +
    
    447
    +
    
    448
    +class MucCapsCache:
    
    449
    +
    
    450
    +    DiscoInfo = namedtuple('DiscoInfo', ['identities', 'features', 'data'])
    
    451
    +
    
    452
    +    def __init__(self):
    
    453
    +        self.cache = {}
    
    454
    +
    
    455
    +    def append(self, stanza):
    
    456
    +        jid = stanza.getFrom()
    
    457
    +        identities, features, data = [], [], []
    
    458
    +        query_childs = stanza.getQueryChildren()
    
    459
    +        if not query_childs:
    
    460
    +            app.log('gajim.muc').warning('%s returned empty disco info', jid)
    
    461
    +            return
    
    462
    +
    
    463
    +        for child in query_childs:
    
    464
    +            if child.getName() == 'identity':
    
    465
    +                attr = {}
    
    466
    +                for key in child.getAttrs().keys():
    
    467
    +                    attr[key] = child.getAttr(key)
    
    468
    +                identities.append(attr)
    
    469
    +            elif child.getName() == 'feature':
    
    470
    +                features.append(child.getAttr('var'))
    
    471
    +            elif child.getName() == 'x':
    
    472
    +                if child.getNamespace() == nbxmpp.NS_DATA:
    
    473
    +                    data.append(nbxmpp.DataForm(node=child))
    
    474
    +
    
    475
    +        self.cache[jid] = self.DiscoInfo(identities, features, data)
    
    476
    +
    
    477
    +    def is_cached(self, jid):
    
    478
    +        return jid in self.cache
    
    479
    +
    
    480
    +    def supports(self, jid, feature):
    
    481
    +        if jid in self.cache:
    
    482
    +            if feature in self.cache[jid].features:
    
    483
    +                return True
    
    484
    +        return False
    
    485
    +
    
    486
    +    def has_mam(self, jid):
    
    487
    +        try:
    
    488
    +            if nbxmpp.NS_MAM_2 in self.cache[jid].features:
    
    489
    +                return True
    
    490
    +            if nbxmpp.NS_MAM_1 in self.cache[jid].features:
    
    491
    +                return True
    
    492
    +        except (KeyError, AttributeError):
    
    493
    +            return False

  • gajim/common/check_paths.py
    ... ... @@ -65,6 +65,7 @@ def create_log_db():
    65 65
     
    
    66 66
                 CREATE TABLE logs(
    
    67 67
                         log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
    
    68
    +                    account_id INTEGER,
    
    68 69
                         jid_id INTEGER,
    
    69 70
                         contact_name TEXT,
    
    70 71
                         time INTEGER,
    
    ... ... @@ -80,6 +81,13 @@ def create_log_db():
    80 81
                         marker INTEGER
    
    81 82
                 );
    
    82 83
     
    
    84
    +            CREATE TABLE last_archive_message(
    
    85
    +                    jid_id INTEGER PRIMARY KEY UNIQUE,
    
    86
    +                    last_mam_id TEXT,
    
    87
    +                    oldest_mam_timestamp TEXT,
    
    88
    +                    last_muc_timestamp TEXT
    
    89
    +            );
    
    90
    +
    
    83 91
                 CREATE INDEX idx_logs_jid_id_time ON logs (jid_id, time DESC);
    
    84 92
                 '''
    
    85 93
                 )
    

  • gajim/common/config.py
    ... ... @@ -409,8 +409,6 @@ class Config:
    409 409
                         'oauth2_client_id': [ opt_str, '0000000044077801', _('client_id for OAuth 2.0 authentication.')],
    
    410 410
                         'oauth2_redirect_url': [ opt_str, 'https%3A%2F%2Fgajim.org%2Fmsnauth%2Findex.cgi', _('redirect_url for OAuth 2.0 authentication.')],
    
    411 411
                         'opened_chat_controls': [opt_str, '', _('Space separated list of JIDs for which we want to re-open a chat window on next startup.')],
    
    412
    -                    'last_mam_id': [opt_str, '', _('Last MAM id we are syncronized with')],
    
    413
    -                    'mam_start_date': [opt_int, 0, _('The earliest date we requested MAM history for')],
    
    414 412
                 }, {}),
    
    415 413
                 'statusmsg': ({
    
    416 414
                         'message': [ opt_str, '' ],
    

  • gajim/common/connection.py
    ... ... @@ -42,6 +42,7 @@ import locale
    42 42
     import hmac
    
    43 43
     import hashlib
    
    44 44
     import json
    
    45
    +from functools import partial
    
    45 46
     
    
    46 47
     try:
    
    47 48
         randomsource = random.SystemRandom()
    
    ... ... @@ -432,7 +433,7 @@ class CommonConnection:
    432 433
             if obj.message is None:
    
    433 434
                 return
    
    434 435
     
    
    435
    -        app.logger.insert_into_logs(jid, obj.timestamp, obj.kind,
    
    436
    +        app.logger.insert_into_logs(self.name, jid, obj.timestamp, obj.kind,
    
    436 437
                                         message=obj.message,
    
    437 438
                                         subject=obj.subject,
    
    438 439
                                         additional_data=obj.additional_data,
    
    ... ... @@ -1913,8 +1914,6 @@ class Connection(CommonConnection, ConnectionHandlers):
    1913 1914
                         self.archiving_namespace = nbxmpp.NS_MAM_2
    
    1914 1915
                     elif nbxmpp.NS_MAM_1 in obj.features:
    
    1915 1916
                         self.archiving_namespace = nbxmpp.NS_MAM_1
    
    1916
    -                elif nbxmpp.NS_MAM in obj.features:
    
    1917
    -                    self.archiving_namespace = nbxmpp.NS_MAM
    
    1918 1917
                     if self.archiving_namespace:
    
    1919 1918
                         self.archiving_supported = True
    
    1920 1919
                         self.archiving_313_supported = True
    
    ... ... @@ -2583,6 +2582,11 @@ class Connection(CommonConnection, ConnectionHandlers):
    2583 2582
                 # Never join a room when invisible
    
    2584 2583
                 return
    
    2585 2584
     
    
    2585
    +        self.discoverMUC(
    
    2586
    +            room_jid, partial(self._join_gc, nick, show, room_jid,
    
    2587
    +                              password, change_nick, rejoin))
    
    2588
    +
    
    2589
    +    def _join_gc(self, nick, show, room_jid, password, change_nick, rejoin):
    
    2586 2590
             # Check time first in the FAST table
    
    2587 2591
             last_date = app.logger.get_room_last_message_time(
    
    2588 2592
                 self.name, room_jid)
    
    ... ... @@ -2599,11 +2603,19 @@ class Connection(CommonConnection, ConnectionHandlers):
    2599 2603
             if app.config.get('send_sha_in_gc_presence'):
    
    2600 2604
                 p = self.add_sha(p)
    
    2601 2605
             self.add_lang(p)
    
    2602
    -        if not change_nick:
    
    2603
    -            t = p.setTag(nbxmpp.NS_MUC + ' x')
    
    2606
    +        if change_nick:
    
    2607
    +            self.connection.send(p)
    
    2608
    +            return
    
    2609
    +
    
    2610
    +        t = p.setTag(nbxmpp.NS_MUC + ' x')
    
    2611
    +        if muc_caps_cache.has_mam(room_jid):
    
    2612
    +            # The room is MAM capable dont get MUC History
    
    2613
    +            t.setTag('history', {'maxchars': '0'})
    
    2614
    +        else:
    
    2615
    +            # Request MUC History (not MAM)
    
    2604 2616
                 tags = {}
    
    2605 2617
                 timeout = app.config.get_per('rooms', room_jid,
    
    2606
    -                'muc_restore_timeout')
    
    2618
    +                                         'muc_restore_timeout')
    
    2607 2619
                 if timeout is None or timeout == -2:
    
    2608 2620
                     timeout = app.config.get('muc_restore_timeout')
    
    2609 2621
                 if last_date == 0 and timeout >= 0:
    
    ... ... @@ -2621,8 +2633,9 @@ class Connection(CommonConnection, ConnectionHandlers):
    2621 2633
                     tags['maxstanzas'] = nb
    
    2622 2634
                 if tags:
    
    2623 2635
                     t.setTag('history', tags)
    
    2624
    -            if password:
    
    2625
    -                t.setTagData('password', password)
    
    2636
    +
    
    2637
    +        if password:
    
    2638
    +            t.setTagData('password', password)
    
    2626 2639
             self.connection.send(p)
    
    2627 2640
     
    
    2628 2641
         def _nec_gc_message_outgoing(self, obj):
    

  • gajim/common/connection_handlers.py
    ... ... @@ -47,6 +47,7 @@ from gajim.common import helpers
    47 47
     from gajim.common import app
    
    48 48
     from gajim.common import dataforms
    
    49 49
     from gajim.common import jingle_xtls
    
    50
    +from gajim.common.caps_cache import muc_caps_cache
    
    50 51
     from gajim.common.commands import ConnectionCommands
    
    51 52
     from gajim.common.pubsub import ConnectionPubSub
    
    52 53
     from gajim.common.protocol.caps import ConnectionCaps
    
    ... ... @@ -96,6 +97,29 @@ class ConnectionDisco:
    96 97
             id_ = self._discover(nbxmpp.NS_DISCO_INFO, jid, node, id_prefix)
    
    97 98
             self.disco_info_ids.append(id_)
    
    98 99
     
    
    100
    +    def discoverMUC(self, jid, callback):
    
    101
    +        disco_info = nbxmpp.Iq(typ='get', to=jid, queryNS=nbxmpp.NS_DISCO_INFO)
    
    102
    +        self.connection.SendAndCallForResponse(
    
    103
    +            disco_info, self.received_muc_info, {'callback': callback})
    
    104
    +
    
    105
    +    def received_muc_info(self, conn, stanza, callback):
    
    106
    +        if nbxmpp.isResultNode(stanza):
    
    107
    +            app.log('gajim.muc').info(
    
    108
    +                'Received MUC DiscoInfo for %s', stanza.getFrom())
    
    109
    +            muc_caps_cache.append(stanza)
    
    110
    +            callback()
    
    111
    +        else:
    
    112
    +            error = stanza.getError()
    
    113
    +            if error == 'item-not-found':
    
    114
    +                # Groupchat does not exist
    
    115
    +                callback()
    
    116
    +                return
    
    117
    +            app.nec.push_incoming_event(
    
    118
    +                InformationEvent(None, conn=self,
    
    119
    +                                 level='error',
    
    120
    +                                 pri_txt=_('Unable to join Groupchat'),
    
    121
    +                                 sec_txt=error))
    
    122
    +
    
    99 123
         def request_register_agent_info(self, agent):
    
    100 124
             if not self.connection or self.connected < 2:
    
    101 125
                 return None
    
    ... ... @@ -760,6 +784,8 @@ class ConnectionHandlersBase:
    760 784
                 self._nec_message_received)
    
    761 785
             app.ged.register_event_handler('mam-message-received', ged.CORE,
    
    762 786
                 self._nec_message_received)
    
    787
    +        app.ged.register_event_handler('mam-gc-message-received', ged.CORE,
    
    788
    +            self._nec_message_received)
    
    763 789
             app.ged.register_event_handler('decrypted-message-received', ged.CORE,
    
    764 790
                 self._nec_decrypted_message_received)
    
    765 791
             app.ged.register_event_handler('gc-message-received', ged.CORE,
    
    ... ... @@ -776,6 +802,8 @@ class ConnectionHandlersBase:
    776 802
                 self._nec_message_received)
    
    777 803
             app.ged.remove_event_handler('mam-message-received', ged.CORE,
    
    778 804
                 self._nec_message_received)
    
    805
    +        app.ged.remove_event_handler('mam-gc-message-received', ged.CORE,
    
    806
    +            self._nec_message_received)
    
    779 807
             app.ged.remove_event_handler('decrypted-message-received', ged.CORE,
    
    780 808
                 self._nec_decrypted_message_received)
    
    781 809
             app.ged.remove_event_handler('gc-message-received', ged.CORE,
    
    ... ... @@ -918,7 +946,8 @@ class ConnectionHandlersBase:
    918 946
             app.config.should_log(self.name, obj.jid):
    
    919 947
                 show = app.logger.convert_show_values_to_db_api_values(obj.show)
    
    920 948
                 if show is not None:
    
    921
    -                app.logger.insert_into_logs(nbxmpp.JID(obj.jid).getStripped(),
    
    949
    +                app.logger.insert_into_logs(self.name,
    
    950
    +                                            nbxmpp.JID(obj.jid).getStripped(),
    
    922 951
                                                 time_time(),
    
    923 952
                                                 KindConstant.STATUS,
    
    924 953
                                                 message=obj.status,
    
    ... ... @@ -1044,7 +1073,8 @@ class ConnectionHandlersBase:
    1044 1073
                 # if not obj.nick, it means message comes from room itself
    
    1045 1074
                 # usually it hold description and can be send at each connection
    
    1046 1075
                 # so don't store it in logs
    
    1047
    -            app.logger.insert_into_logs(obj.jid,
    
    1076
    +            app.logger.insert_into_logs(self.name,
    
    1077
    +                                        obj.jid,
    
    1048 1078
                                             obj.timestamp,
    
    1049 1079
                                             KindConstant.GC_MSG,
    
    1050 1080
                                             message=obj.msgtxt,
    
    ... ... @@ -1064,7 +1094,8 @@ class ConnectionHandlersBase:
    1064 1094
             subject = msg.getSubject()
    
    1065 1095
     
    
    1066 1096
             if session.is_loggable():
    
    1067
    -            app.logger.insert_into_logs(nbxmpp.JID(frm).getStripped(),
    
    1097
    +            app.logger.insert_into_logs(self.name,
    
    1098
    +                                        nbxmpp.JID(frm).getStripped(),
    
    1068 1099
                                             tim,
    
    1069 1100
                                             KindConstant.ERROR,
    
    1070 1101
                                             message=error_msg,
    
    ... ... @@ -1266,10 +1297,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
    1266 1297
             app.nec.register_incoming_event(ArchivingErrorReceivedEvent)
    
    1267 1298
             app.nec.register_incoming_event(
    
    1268 1299
                 Archiving313PreferencesChangedReceivedEvent)
    
    1269
    -        app.nec.register_incoming_event(
    
    1270
    -            ArchivingFinishedLegacyReceivedEvent)
    
    1271
    -        app.nec.register_incoming_event(
    
    1272
    -            ArchivingFinishedReceivedEvent)
    
    1273 1300
             app.nec.register_incoming_event(NotificationEvent)
    
    1274 1301
     
    
    1275 1302
             app.ged.register_event_handler('http-auth-received', ged.CORE,
    
    ... ... @@ -2210,7 +2237,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
    2210 2237
             con.RegisterHandler('iq', self._IqPingCB, 'get', nbxmpp.NS_PING)
    
    2211 2238
             con.RegisterHandler('iq', self._SearchCB, 'result', nbxmpp.NS_SEARCH)
    
    2212 2239
             con.RegisterHandler('iq', self._PrivacySetCB, 'set', nbxmpp.NS_PRIVACY)
    
    2213
    -        con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM)
    
    2214 2240
             con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM_1)
    
    2215 2241
             con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM_2)
    
    2216 2242
             con.RegisterHandler('iq', self._PubSubCB, 'result')
    

  • gajim/common/connection_handlers_events.py
    ... ... @@ -954,7 +954,8 @@ class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    954 954
                 show = app.logger.convert_show_values_to_db_api_values(self.show)
    
    955 955
                 if show is not None:
    
    956 956
                     fjid = nbxmpp.JID(self.fjid)
    
    957
    -                app.logger.insert_into_logs(fjid.getStripped(),
    
    957
    +                app.logger.insert_into_logs(self.conn.name,
    
    958
    +                                            fjid.getStripped(),
    
    958 959
                                                 time_time(),
    
    959 960
                                                 KindConstant.GCSTATUS,
    
    960 961
                                                 contact_name=fjid.getResource(),
    
    ... ... @@ -1052,36 +1053,33 @@ class MamMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    1052 1053
             :stanza:        Complete stanza Node
    
    1053 1054
             :forwarded:     Forwarded Node
    
    1054 1055
             :result:        Result Node
    
    1055
    -        :unique_id:     The unique stable id
    
    1056 1056
             '''
    
    1057 1057
             self._set_base_event_vars_as_attributes(base_event)
    
    1058 1058
             self.additional_data = {}
    
    1059 1059
             self.encrypted = False
    
    1060 1060
             self.groupchat = False
    
    1061
    +        self.nick = None
    
    1061 1062
     
    
    1062 1063
         def generate(self):
    
    1063 1064
             archive_jid = self.stanza.getFrom()
    
    1064
    -        own_jid = self.conn.get_own_jid()
    
    1065
    +        own_jid = self.conn.get_own_jid().getStripped()
    
    1065 1066
             if archive_jid and not archive_jid.bareMatch(own_jid):
    
    1066 1067
                 # MAM Message not from our Archive
    
    1067
    -            log.info('MAM message not from our user archive')
    
    1068 1068
                 return False
    
    1069 1069
     
    
    1070 1070
             self.msg_ = self.forwarded.getTag('message', protocol=True)
    
    1071 1071
     
    
    1072 1072
             if self.msg_.getType() == 'groupchat':
    
    1073
    -            log.info('Received groupchat message from user archive')
    
    1074 1073
                 return False
    
    1075 1074
     
    
    1076 1075
             # use stanza-id as unique-id
    
    1077 1076
             self.unique_id, origin_id = self.get_unique_id()
    
    1078 1077
     
    
    1079 1078
             # Check for duplicates
    
    1080
    -        if app.logger.find_stanza_id(self.unique_id, origin_id):
    
    1079
    +        if app.logger.find_stanza_id(own_jid, self.unique_id, origin_id):
    
    1081 1080
                 return
    
    1082 1081
     
    
    1083 1082
             self.msgtxt = self.msg_.getTagData('body')
    
    1084
    -        self.query_id = self.result.getAttr('queryid')
    
    1085 1083
     
    
    1086 1084
             frm = self.msg_.getFrom()
    
    1087 1085
             # Some servers dont set the 'to' attribute when
    
    ... ... @@ -1123,26 +1121,83 @@ class MamMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    1123 1121
             return True
    
    1124 1122
     
    
    1125 1123
         def get_unique_id(self):
    
    1124
    +        stanza_id = self.get_stanza_id(self.result, query=True)
    
    1126 1125
             if self.conn.get_own_jid().bareMatch(self.msg_.getFrom()):
    
    1127
    -            # On our own Messages we have to check for both
    
    1128
    -            # stanza-id and origin-id, because other resources
    
    1129
    -            # maybe not support origin-id
    
    1130
    -            stanza_id = None
    
    1131
    -            if self.result.getNamespace() == nbxmpp.NS_MAM_2:
    
    1132
    -                # Only mam:2 ensures valid stanza-id
    
    1133
    -                stanza_id = self.get_stanza_id(self.result, query=True)
    
    1134
    -
    
    1135
    -            # try always to get origin-id because its a message
    
    1136
    -            # we sent.
    
    1126
    +            # message we sent
    
    1137 1127
                 origin_id = self.msg_.getOriginID()
    
    1138
    -
    
    1139 1128
                 return stanza_id, origin_id
    
    1140 1129
     
    
    1141 1130
             # A message we received
    
    1142
    -        elif self.result.getNamespace() == nbxmpp.NS_MAM_2:
    
    1143
    -            # Only mam:2 ensures valid stanza-id
    
    1144
    -            return self.get_stanza_id(self.result, query=True), None
    
    1145
    -        return None, None
    
    1131
    +        return stanza_id, None
    
    1132
    +
    
    1133
    +class MamGcMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    
    1134
    +    name = 'mam-gc-message-received'
    
    1135
    +    base_network_events = ['raw-mam-message-received']
    
    1136
    +
    
    1137
    +    def __init__(self, name, base_event):
    
    1138
    +        '''
    
    1139
    +        Pre-Generated attributes on self:
    
    1140
    +
    
    1141
    +        :conn:          Connection instance
    
    1142
    +        :stanza:        Complete stanza Node
    
    1143
    +        :forwarded:     Forwarded Node
    
    1144
    +        :result:        Result Node
    
    1145
    +        '''
    
    1146
    +        self._set_base_event_vars_as_attributes(base_event)
    
    1147
    +        self.additional_data = {}
    
    1148
    +        self.encrypted = False
    
    1149
    +        self.groupchat = True
    
    1150
    +        self.kind = KindConstant.GC_MSG
    
    1151
    +
    
    1152
    +    def generate(self):
    
    1153
    +        self.msg_ = self.forwarded.getTag('message', protocol=True)
    
    1154
    +
    
    1155
    +        if self.msg_.getType() != 'groupchat':
    
    1156
    +            return False
    
    1157
    +
    
    1158
    +        self.room_jid = self.stanza.getFrom().getStripped()
    
    1159
    +
    
    1160
    +        self.unique_id = self.get_stanza_id(self.result, query=True)
    
    1161
    +
    
    1162
    +        # Check for duplicates
    
    1163
    +        if app.logger.find_stanza_id(self.room_jid, self.unique_id,
    
    1164
    +                                     groupchat=True):
    
    1165
    +            return
    
    1166
    +
    
    1167
    +        self.msgtxt = self.msg_.getTagData('body')
    
    1168
    +        self.with_ = self.msg_.getFrom().getStripped()
    
    1169
    +        self.nick = self.msg_.getFrom().getResource()
    
    1170
    +
    
    1171
    +        # Get the real jid if we have it
    
    1172
    +        self.real_jid = None
    
    1173
    +        muc_user = self.msg_.getTag('x', namespace=nbxmpp.NS_MUC_USER)
    
    1174
    +        if muc_user is not None:
    
    1175
    +            self.real_jid = muc_user.getTagAttr('item', 'jid')
    
    1176
    +
    
    1177
    +        delay = self.forwarded.getTagAttr(
    
    1178
    +            'delay', 'stamp', namespace=nbxmpp.NS_DELAY2)
    
    1179
    +        if delay is None:
    
    1180
    +            log.error('Received MAM message without timestamp')
    
    1181
    +            return
    
    1182
    +
    
    1183
    +        self.timestamp = helpers.parse_datetime(
    
    1184
    +            delay, check_utc=True, epoch=True)
    
    1185
    +        if self.timestamp is None:
    
    1186
    +            log.error('Received MAM message with invalid timestamp: %s', delay)
    
    1187
    +            return
    
    1188
    +
    
    1189
    +        # Save timestamp added by the user
    
    1190
    +        user_delay = self.msg_.getTagAttr(
    
    1191
    +            'delay', 'stamp', namespace=nbxmpp.NS_DELAY2)
    
    1192
    +        if user_delay is not None:
    
    1193
    +            self.user_timestamp = helpers.parse_datetime(
    
    1194
    +                user_delay, check_utc=True, epoch=True)
    
    1195
    +            if self.user_timestamp is None:
    
    1196
    +                log.warning('Received MAM message with '
    
    1197
    +                            'invalid user timestamp: %s', user_delay)
    
    1198
    +
    
    1199
    +        log.debug('Received mam-gc-message: unique id: %s', self.unique_id)
    
    1200
    +        return True
    
    1146 1201
     
    
    1147 1202
     class MamDecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    
    1148 1203
         name = 'mam-decrypted-message-received'
    
    ... ... @@ -1156,6 +1211,9 @@ class MamDecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    1156 1211
     
    
    1157 1212
             self.get_oob_data(self.msg_)
    
    1158 1213
     
    
    1214
    +        if self.groupchat:
    
    1215
    +            return True
    
    1216
    +
    
    1159 1217
             self.is_pm = app.logger.jid_is_room_jid(self.with_.getStripped())
    
    1160 1218
             if self.is_pm is None:
    
    1161 1219
                 # Check if this event is triggered after a disco, so we dont
    
    ... ... @@ -1226,7 +1284,9 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    1226 1284
             # Check groupchat messages for duplicates,
    
    1227 1285
             # We do this because of MUC History messages
    
    1228 1286
             if self.stanza.getType() == 'groupchat':
    
    1229
    -            if app.logger.find_stanza_id(self.unique_id):
    
    1287
    +            if app.logger.find_stanza_id(self.stanza.getFrom().getStripped(),
    
    1288
    +                                         self.unique_id,
    
    1289
    +                                         groupchat=True):
    
    1230 1290
                     return
    
    1231 1291
     
    
    1232 1292
             address_tag = self.stanza.getTag('addresses',
    
    ... ... @@ -1285,9 +1345,14 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    1285 1345
                     self.forwarded = True
    
    1286 1346
     
    
    1287 1347
             result = self.stanza.getTag('result', protocol=True)
    
    1288
    -        if result and result.getNamespace() in (nbxmpp.NS_MAM,
    
    1289
    -                                                nbxmpp.NS_MAM_1,
    
    1348
    +        if result and result.getNamespace() in (nbxmpp.NS_MAM_1,
    
    1290 1349
                                                     nbxmpp.NS_MAM_2):
    
    1350
    +
    
    1351
    +            if result.getAttr('queryid') not in self.conn.mam_query_ids:
    
    1352
    +                log.warning('Invalid MAM Message: unknown query id')
    
    1353
    +                log.debug(self.stanza)
    
    1354
    +                return
    
    1355
    +
    
    1291 1356
                 forwarded = result.getTag('forwarded',
    
    1292 1357
                                           namespace=nbxmpp.NS_FORWARD,
    
    1293 1358
                                           protocol=True)
    
    ... ... @@ -1300,8 +1365,7 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    1300 1365
                                  conn=self.conn,
    
    1301 1366
                                  stanza=self.stanza,
    
    1302 1367
                                  forwarded=forwarded,
    
    1303
    -                             result=result,
    
    1304
    -                             stanza_id=self.unique_id))
    
    1368
    +                             result=result))
    
    1305 1369
                 return
    
    1306 1370
     
    
    1307 1371
             # Mediated invitation?
    
    ... ... @@ -1799,6 +1863,27 @@ class ArchivingErrorReceivedEvent(nec.NetworkIncomingEvent):
    1799 1863
                 self.error_msg = self.stanza.getErrorMsg()
    
    1800 1864
                 return True
    
    1801 1865
     
    
    1866
    +class ArchivingCountReceived(nec.NetworkIncomingEvent):
    
    1867
    +    name = 'archiving-count-received'
    
    1868
    +    base_network_events = []
    
    1869
    +
    
    1870
    +    def generate(self):
    
    1871
    +        return True
    
    1872
    +
    
    1873
    +class ArchivingIntervalFinished(nec.NetworkIncomingEvent):
    
    1874
    +    name = 'archiving-interval-finished'
    
    1875
    +    base_network_events = []
    
    1876
    +
    
    1877
    +    def generate(self):
    
    1878
    +        return True
    
    1879
    +
    
    1880
    +class ArchivingQueryID(nec.NetworkIncomingEvent):
    
    1881
    +    name = 'archiving-query-id'
    
    1882
    +    base_network_events = []
    
    1883
    +
    
    1884
    +    def generate(self):
    
    1885
    +        return True
    
    1886
    +
    
    1802 1887
     class Archiving313PreferencesChangedReceivedEvent(nec.NetworkIncomingEvent):
    
    1803 1888
         name = 'archiving-313-preferences-changed-received'
    
    1804 1889
         base_network_events = ['archiving-received']
    
    ... ... @@ -1826,43 +1911,6 @@ class Archiving313PreferencesChangedReceivedEvent(nec.NetworkIncomingEvent):
    1826 1911
     
    
    1827 1912
             return True
    
    1828 1913
     
    
    1829
    -class ArchivingFinishedReceivedEvent(nec.NetworkIncomingEvent):
    
    1830
    -    name = 'archiving-finished'
    
    1831
    -    base_network_events = ['archiving-received']
    
    1832
    -
    
    1833
    -    def generate(self):
    
    1834
    -        self.conn = self.base_event.conn
    
    1835
    -        self.stanza = self.base_event.stanza
    
    1836
    -        self.type_ = self.base_event.type_
    
    1837
    -        self.fin = self.stanza.getTag('fin')
    
    1838
    -
    
    1839
    -        if self.type_ != 'result' or not self.fin:
    
    1840
    -            return
    
    1841
    -
    
    1842
    -        self.query_id = self.fin.getAttr('queryid')
    
    1843
    -        if not self.query_id:
    
    1844
    -            return
    
    1845
    -
    
    1846
    -        return True
    
    1847
    -
    
    1848
    -class ArchivingFinishedLegacyReceivedEvent(nec.NetworkIncomingEvent):
    
    1849
    -    name = 'archiving-finished-legacy'
    
    1850
    -    base_network_events = ['raw-message-received']
    
    1851
    -
    
    1852
    -    def generate(self):
    
    1853
    -        self.conn = self.base_event.conn
    
    1854
    -        self.stanza = self.base_event.stanza
    
    1855
    -        self.fin = self.stanza.getTag('fin', namespace=nbxmpp.NS_MAM)
    
    1856
    -
    
    1857
    -        if not self.fin:
    
    1858
    -            return
    
    1859
    -
    
    1860
    -        self.query_id = self.fin.getAttr('queryid')
    
    1861
    -        if not self.query_id:
    
    1862
    -            return
    
    1863
    -
    
    1864
    -        return True
    
    1865
    -
    
    1866 1914
     class AccountCreatedEvent(nec.NetworkIncomingEvent):
    
    1867 1915
         name = 'account-created'
    
    1868 1916
         base_network_events = []
    

  • gajim/common/const.py
    ... ... @@ -36,6 +36,10 @@ class AvatarSize(IntEnum):
    36 36
         TOOLTIP = 125
    
    37 37
         VCARD = 200
    
    38 38
     
    
    39
    +class ArchiveState(IntEnum):
    
    40
    +    NEVER = 0
    
    41
    +    ALL = 1
    
    42
    +
    
    39 43
     
    
    40 44
     THANKS = u"""\
    
    41 45
     Alexander Futász
    

  • gajim/common/logger.py
    ... ... @@ -282,6 +282,10 @@ class Logger:
    282 282
                 return [user['jid'] for user in family]
    
    283 283
             return [jid]
    
    284 284
     
    
    285
    +    def get_account_id(self, account):
    
    286
    +        jid = app.get_jid_from_account(account)
    
    287
    +        return self.get_jid_id(jid, type_=JIDConstant.NORMAL_TYPE)
    
    288
    +
    
    285 289
         def get_jid_id(self, jid, kind=None, type_=None):
    
    286 290
             """
    
    287 291
             Get the jid id from a jid.
    
    ... ... @@ -1084,12 +1088,19 @@ class Logger:
    1084 1088
                 return True
    
    1085 1089
             return False
    
    1086 1090
     
    
    1087
    -    def find_stanza_id(self, stanza_id, origin_id=None):
    
    1091
    +    def find_stanza_id(self, archive_jid, stanza_id, origin_id=None,
    
    1092
    +                       groupchat=False):
    
    1088 1093
             """
    
    1089 1094
             Checks if a stanza-id is already in the `logs` table
    
    1090 1095
     
    
    1096
    +        :param archive_jid: The jid of the archive the stanza-id belongs to
    
    1097
    +
    
    1091 1098
             :param stanza_id:   The stanza-id
    
    1092 1099
     
    
    1100
    +        :param origin_id:   The origin-id
    
    1101
    +
    
    1102
    +        :param groupchat:   stanza-id is from a groupchat
    
    1103
    +
    
    1093 1104
             return True if the stanza-id was found
    
    1094 1105
             """
    
    1095 1106
             ids = []
    
    ... ... @@ -1101,12 +1112,19 @@ class Logger:
    1101 1112
             if not ids:
    
    1102 1113
                 return False
    
    1103 1114
     
    
    1115
    +        archive_id = self.get_jid_id(archive_jid)
    
    1116
    +        if groupchat:
    
    1117
    +            column = 'jid_id'
    
    1118
    +        else:
    
    1119
    +            column = 'account_id'
    
    1120
    +
    
    1104 1121
             sql = '''
    
    1105 1122
                   SELECT stanza_id FROM logs
    
    1106
    -              WHERE stanza_id IN ({values}) LIMIT 1
    
    1107
    -              '''.format(values=', '.join('?' * len(ids)))
    
    1123
    +              WHERE stanza_id IN ({values}) AND {archive} = ? LIMIT 1
    
    1124
    +              '''.format(values=', '.join('?' * len(ids)),
    
    1125
    +                         archive=column)
    
    1108 1126
     
    
    1109
    -        result = self.con.execute(sql, tuple(ids)).fetchone()
    
    1127
    +        result = self.con.execute(sql, tuple(ids) + (archive_id,)).fetchone()
    
    1110 1128
     
    
    1111 1129
             if result is not None:
    
    1112 1130
                 log.info('Found duplicated message, stanza-id: %s, origin-id: %s',
    
    ... ... @@ -1127,7 +1145,8 @@ class Logger:
    1127 1145
             """
    
    1128 1146
             return self.get_jid_id(jid, kind, type_)
    
    1129 1147
     
    
    1130
    -    def insert_into_logs(self, jid, time_, kind, unread=True, **kwargs):
    
    1148
    +    def insert_into_logs(self, account, jid, time_, kind,
    
    1149
    +                         unread=True, **kwargs):
    
    1131 1150
             """
    
    1132 1151
             Insert a new message into the `logs` table
    
    1133 1152
     
    
    ... ... @@ -1144,20 +1163,22 @@ class Logger:
    1144 1163
                             a field in the `logs` table
    
    1145 1164
             """
    
    1146 1165
             jid_id = self.get_jid_id(jid, kind=kind)
    
    1147
    - 
    
    1166
    +        account_id = self.get_account_id(account)
    
    1167
    +
    
    1148 1168
             if 'additional_data' in kwargs:
    
    1149 1169
                 if not kwargs['additional_data']:
    
    1150 1170
                     del kwargs['additional_data']
    
    1151 1171
                 else:
    
    1152 1172
                     kwargs['additional_data'] = json.dumps(kwargs["additional_data"])
    
    1153
    - 
    
    1173
    +
    
    1154 1174
             sql = '''
    
    1155
    -              INSERT INTO logs (jid_id, time, kind, {columns})
    
    1156
    -              VALUES (?, ?, ?, {values})
    
    1175
    +              INSERT INTO logs (account_id, jid_id, time, kind, {columns})
    
    1176
    +              VALUES (?, ?, ?, ?, {values})
    
    1157 1177
                   '''.format(columns=', '.join(kwargs.keys()),
    
    1158 1178
                              values=', '.join('?' * len(kwargs)))
    
    1159 1179
     
    
    1160
    -        lastrowid = self.con.execute(sql, (jid_id, time_, kind) + tuple(kwargs.values())).lastrowid
    
    1180
    +        lastrowid = self.con.execute(
    
    1181
    +            sql, (account_id, jid_id, time_, kind) + tuple(kwargs.values())).lastrowid
    
    1161 1182
     
    
    1162 1183
             log.info('Insert into DB: jid: %s, time: %s, kind: %s, stanza_id: %s',
    
    1163 1184
                      jid, time_, kind, kwargs.get('stanza_id', None))
    
    ... ... @@ -1192,3 +1213,45 @@ class Logger:
    1192 1213
                 '''
    
    1193 1214
             self.con.execute(sql, (sha, account_jid_id, jid_id))
    
    1194 1215
             self._timeout_commit()
    
    1216
    +
    
    1217
    +    def get_archive_timestamp(self, jid, type_=None):
    
    1218
    +        """
    
    1219
    +        Get the last archive id/timestamp for a jid
    
    1220
    +
    
    1221
    +        :param jid:     The jid that belongs to the avatar
    
    1222
    +
    
    1223
    +        """
    
    1224
    +        jid_id = self.get_jid_id(jid, type_=type_)
    
    1225
    +        sql = '''SELECT * FROM last_archive_message WHERE jid_id = ?'''
    
    1226
    +        return self.con.execute(sql, (jid_id,)).fetchone()
    
    1227
    +
    
    1228
    +    def set_archive_timestamp(self, jid, **kwargs):
    
    1229
    +        """
    
    1230
    +        Set the last archive id/timestamp
    
    1231
    +
    
    1232
    +        :param jid:                     The jid that belongs to the avatar
    
    1233
    +
    
    1234
    +        :param last_mam_id:             The last MAM result id
    
    1235
    +
    
    1236
    +        :param oldest_mam_timestamp:    The oldest date we requested MAM
    
    1237
    +                                        history for
    
    1238
    +
    
    1239
    +        :param last_muc_timestamp:      The timestamp of the last message we
    
    1240
    +                                        received in a MUC
    
    1241
    +
    
    1242
    +        """
    
    1243
    +        jid_id = self.get_jid_id(jid)
    
    1244
    +        exists = self.get_archive_timestamp(jid)
    
    1245
    +        if not exists:
    
    1246
    +            sql = '''INSERT INTO last_archive_message VALUES (?, ?, ?, ?)'''
    
    1247
    +            self.con.execute(sql, (jid_id,
    
    1248
    +                                   kwargs.get('last_mam_id', None),
    
    1249
    +                                   kwargs.get('oldest_mam_timestamp', None),
    
    1250
    +                                   kwargs.get('last_muc_timestamp', None)))
    
    1251
    +        else:
    
    1252
    +            args = ' = ?, '.join(kwargs.keys()) + ' = ?'
    
    1253
    +            sql = '''UPDATE last_archive_message SET {}
    
    1254
    +                     WHERE jid_id = ?'''.format(args)
    
    1255
    +            self.con.execute(sql, tuple(kwargs.values()) + (jid_id,))
    
    1256
    +        log.info('Save archive timestamps: %s', kwargs)
    
    1257
    +        self._timeout_commit()

  • gajim/common/message_archiving.py
    ... ... @@ -26,6 +26,7 @@ import nbxmpp
    26 26
     from gajim.common import app
    
    27 27
     from gajim.common import ged
    
    28 28
     from gajim.common.logger import KindConstant, JIDConstant
    
    29
    +from gajim.common.const import ArchiveState
    
    29 30
     import gajim.common.connection_handlers_events as ev
    
    30 31
     
    
    31 32
     log = logging.getLogger('gajim.c.message_archiving')
    
    ... ... @@ -36,13 +37,9 @@ class ConnectionArchive313:
    36 37
             self.archiving_313_supported = False
    
    37 38
             self.mam_awaiting_disco_result = {}
    
    38 39
             self.iq_answer = []
    
    39
    -        self.mam_query_date = None
    
    40
    -        self.mam_query_id = None
    
    40
    +        self.mam_query_ids = []
    
    41 41
             app.nec.register_incoming_event(ev.MamMessageReceivedEvent)
    
    42
    -        app.ged.register_event_handler('archiving-finished-legacy', ged.CORE,
    
    43
    -            self._nec_result_finished)
    
    44
    -        app.ged.register_event_handler('archiving-finished', ged.CORE,
    
    45
    -            self._nec_result_finished)
    
    42
    +        app.nec.register_incoming_event(ev.MamGcMessageReceivedEvent)
    
    46 43
             app.ged.register_event_handler('agent-info-error-received', ged.CORE,
    
    47 44
                 self._nec_agent_info_error)
    
    48 45
             app.ged.register_event_handler('agent-info-received', ged.CORE,
    
    ... ... @@ -54,10 +51,6 @@ class ConnectionArchive313:
    54 51
                 self._nec_archiving_313_preferences_changed_received)
    
    55 52
     
    
    56 53
         def cleanup(self):
    
    57
    -        app.ged.remove_event_handler('archiving-finished-legacy', ged.CORE,
    
    58
    -            self._nec_result_finished)
    
    59
    -        app.ged.remove_event_handler('archiving-finished', ged.CORE,
    
    60
    -            self._nec_result_finished)
    
    61 54
             app.ged.remove_event_handler('agent-info-error-received', ged.CORE,
    
    62 55
                 self._nec_agent_info_error)
    
    63 56
             app.ged.remove_event_handler('agent-info-received', ged.CORE,
    
    ... ... @@ -101,28 +94,111 @@ class ConnectionArchive313:
    101 94
                         None, disco=True, **vars(msg_obj)))
    
    102 95
             del self.mam_awaiting_disco_result[obj.jid]
    
    103 96
     
    
    104
    -    def _nec_result_finished(self, obj):
    
    105
    -        if obj.conn.name != self.name:
    
    97
    +    @staticmethod
    
    98
    +    def parse_iq(stanza, query_id):
    
    99
    +        if not nbxmpp.isResultNode(stanza):
    
    100
    +            log.error('Error on MAM query: %s', stanza.getError())
    
    101
    +            raise InvalidMamIQ
    
    102
    +
    
    103
    +        fin = stanza.getTag('fin')
    
    104
    +        if fin is None:
    
    105
    +            log.error('Malformed MAM query result received: %s', stanza)
    
    106
    +            raise InvalidMamIQ
    
    107
    +
    
    108
    +        if fin.getAttr('queryid') != query_id:
    
    109
    +            log.error('Result with unknown query id received')
    
    110
    +            raise InvalidMamIQ
    
    111
    +
    
    112
    +        set_ = fin.getTag('set', namespace=nbxmpp.NS_RSM)
    
    113
    +        if set_ is None:
    
    114
    +            log.error(
    
    115
    +                'Malformed MAM query result received (no "set" Node): %s',
    
    116
    +                stanza)
    
    117
    +            raise InvalidMamIQ
    
    118
    +        return fin, set_
    
    119
    +
    
    120
    +    def parse_from_jid(self, stanza):
    
    121
    +        jid = stanza.getFrom()
    
    122
    +        if jid is None:
    
    123
    +            # No from means, iq from our own archive
    
    124
    +            jid = self.get_own_jid().getStripped()
    
    125
    +        else:
    
    126
    +            jid = jid.getStripped()
    
    127
    +        return jid
    
    128
    +
    
    129
    +    def _result_finished(self, conn, stanza, query_id, start_date, groupchat):
    
    130
    +        try:
    
    131
    +            fin, set_ = self.parse_iq(stanza, query_id)
    
    132
    +        except InvalidMamIQ:
    
    106 133
                 return
    
    107 134
     
    
    108
    -        if obj.query_id != self.mam_query_id:
    
    135
    +        last = set_.getTagData('last')
    
    136
    +        if last is None:
    
    137
    +            log.info('End of MAM query, no items retrieved')
    
    138
    +            return
    
    139
    +
    
    140
    +        jid = self.parse_from_jid(stanza)
    
    141
    +        complete = fin.getAttr('complete')
    
    142
    +        app.logger.set_archive_timestamp(jid, last_mam_id=last)
    
    143
    +        if complete != 'true':
    
    144
    +            self.mam_query_ids.remove(query_id)
    
    145
    +            query_id = self.get_query_id()
    
    146
    +            query = self.get_archive_query(query_id, jid=jid, after=last)
    
    147
    +            self._send_archive_query(query, query_id, groupchat=groupchat)
    
    148
    +        else:
    
    149
    +            self.mam_query_ids.remove(query_id)
    
    150
    +            if start_date is not None:
    
    151
    +                app.logger.set_archive_timestamp(
    
    152
    +                    jid,
    
    153
    +                    last_mam_id=last,
    
    154
    +                    oldest_mam_timestamp=start_date.timestamp())
    
    155
    +            log.info('End of MAM query, last mam id: %s', last)
    
    156
    +
    
    157
    +    def _intervall_result_finished(self, conn, stanza, query_id,
    
    158
    +                                   start_date, end_date, event_id):
    
    159
    +        try:
    
    160
    +            fin, set_ = self.parse_iq(stanza, query_id)
    
    161
    +        except InvalidMamIQ:
    
    162
    +            return
    
    163
    +
    
    164
    +        self.mam_query_ids.remove(query_id)
    
    165
    +        jid = self.parse_from_jid(stanza)
    
    166
    +        if start_date:
    
    167
    +            timestamp = start_date.timestamp()
    
    168
    +        else:
    
    169
    +            timestamp = ArchiveState.ALL
    
    170
    +
    
    171
    +        last = set_.getTagData('last')
    
    172
    +        if last is None:
    
    173
    +            app.nec.push_incoming_event(ev.ArchivingIntervalFinished(
    
    174
    +                None, event_id=event_id))
    
    175
    +            app.logger.set_archive_timestamp(
    
    176
    +                jid, oldest_mam_timestamp=timestamp)
    
    177
    +            log.info('End of MAM query, no items retrieved')
    
    178
    +            return
    
    179
    +
    
    180
    +        complete = fin.getAttr('complete')
    
    181
    +        if complete != 'true':
    
    182
    +            self.request_archive_interval(event_id, start_date, end_date, last)
    
    183
    +        else:
    
    184
    +            log.info('query finished')
    
    185
    +            app.logger.set_archive_timestamp(
    
    186
    +                jid, oldest_mam_timestamp=timestamp)
    
    187
    +            app.nec.push_incoming_event(ev.ArchivingIntervalFinished(
    
    188
    +                None, event_id=event_id, stanza=stanza))
    
    189
    +
    
    190
    +    def _received_count(self, conn, stanza, query_id, event_id):
    
    191
    +        try:
    
    192
    +            _, set_ = self.parse_iq(stanza, query_id)
    
    193
    +        except InvalidMamIQ:
    
    109 194
                 return
    
    110 195
     
    
    111
    -        set_ = obj.fin.getTag('set', namespace=nbxmpp.NS_RSM)
    
    112
    -        if set_:
    
    113
    -            last = set_.getTagData('last')
    
    114
    -            complete = obj.fin.getAttr('complete')
    
    115
    -            if last:
    
    116
    -                app.config.set_per('accounts', self.name, 'last_mam_id', last)
    
    117
    -                if complete != 'true':
    
    118
    -                    self.request_archive(self.get_query_id(), after=last)
    
    119
    -            if complete == 'true':
    
    120
    -                self.mam_query_id = None
    
    121
    -                if self.mam_query_date:
    
    122
    -                    app.config.set_per(
    
    123
    -                        'accounts', self.name,
    
    124
    -                        'mam_start_date', self.mam_query_date.timestamp())
    
    125
    -                    self.mam_query_date = None
    
    196
    +        self.mam_query_ids.remove(query_id)
    
    197
    +
    
    198
    +        count = set_.getTagData('count')
    
    199
    +        log.info('message count received: %s', count)
    
    200
    +        app.nec.push_incoming_event(ev.ArchivingCountReceived(
    
    201
    +            None, event_id=event_id, count=count))
    
    126 202
     
    
    127 203
         def _nec_mam_decrypted_message_received(self, obj):
    
    128 204
             if obj.conn.name != self.name:
    
    ... ... @@ -132,33 +208,96 @@ class ConnectionArchive313:
    132 208
             duplicate = app.logger.search_for_duplicate(
    
    133 209
                 obj.with_, obj.timestamp, obj.msgtxt)
    
    134 210
             if duplicate:
    
    135
    -            return
    
    136
    -        app.logger.insert_into_logs(
    
    137
    -            obj.with_, obj.timestamp, obj.kind,
    
    138
    -            unread=False,
    
    139
    -            message=obj.msgtxt,
    
    140
    -            additional_data=obj.additional_data,
    
    141
    -            stanza_id=obj.unique_id)
    
    211
    +            # dont propagate the event further
    
    212
    +            return True
    
    213
    +        app.logger.insert_into_logs(self.name,
    
    214
    +                                    obj.with_,
    
    215
    +                                    obj.timestamp,
    
    216
    +                                    obj.kind,
    
    217
    +                                    unread=False,
    
    218
    +                                    message=obj.msgtxt,
    
    219
    +                                    contact_name=obj.nick,
    
    220
    +                                    additional_data=obj.additional_data,
    
    221
    +                                    stanza_id=obj.unique_id)
    
    142 222
     
    
    143 223
         def get_query_id(self):
    
    144
    -        self.mam_query_id = self.connection.getAnID()
    
    145
    -        return self.mam_query_id
    
    224
    +        query_id = self.connection.getAnID()
    
    225
    +        self.mam_query_ids.append(query_id)
    
    226
    +        return query_id
    
    146 227
     
    
    147 228
         def request_archive_on_signin(self):
    
    148
    -        mam_id = app.config.get_per('accounts', self.name, 'last_mam_id')
    
    229
    +        own_jid = self.get_own_jid().getStripped()
    
    230
    +        archive = app.logger.get_archive_timestamp(own_jid)
    
    231
    +
    
    232
    +        # Migration of last_mam_id from config to DB
    
    233
    +        if archive is not None:
    
    234
    +            mam_id = archive.last_mam_id
    
    235
    +        else:
    
    236
    +            mam_id = app.config.get_per('accounts', self.name, 'last_mam_id')
    
    237
    +
    
    238
    +        start_date = None
    
    149 239
             query_id = self.get_query_id()
    
    150 240
             if mam_id:
    
    151
    -            self.request_archive(query_id, after=mam_id)
    
    241
    +            log.info('MAM query after: %s', mam_id)
    
    242
    +            query = self.get_archive_query(query_id, after=mam_id)
    
    152 243
             else:
    
    153 244
                 # First Start, we request the last week
    
    154
    -            self.mam_query_date = datetime.utcnow() - timedelta(days=7)
    
    155
    -            log.info('First start: query archive start: %s', self.mam_query_date)
    
    156
    -            self.request_archive(query_id, start=self.mam_query_date)
    
    245
    +            start_date = datetime.utcnow() - timedelta(days=7)
    
    246
    +            log.info('First start: query archive start: %s', start_date)
    
    247
    +            query = self.get_archive_query(query_id, start=start_date)
    
    248
    +        self._send_archive_query(query, query_id, start_date)
    
    249
    +
    
    250
    +    def request_archive_on_muc_join(self, jid):
    
    251
    +        archive = app.logger.get_archive_timestamp(
    
    252
    +            jid, type_=JIDConstant.ROOM_TYPE)
    
    253
    +        query_id = self.get_query_id()
    
    254
    +        start_date = None
    
    255
    +        if archive is not None:
    
    256
    +            log.info('Query Groupchat MAM Archive %s after %s:',
    
    257
    +                     jid, archive.last_mam_id)
    
    258
    +            query = self.get_archive_query(
    
    259
    +                query_id, jid=jid, after=archive.last_mam_id)
    
    260
    +        else:
    
    261
    +            # First Start, we dont request history
    
    262
    +            # Depending on what a MUC saves, there could be thousands
    
    263
    +            # of Messages even in just one day.
    
    264
    +            start_date = datetime.utcnow() - timedelta(days=1)
    
    265
    +            log.info('First join: query archive %s from: %s', jid, start_date)
    
    266
    +            query = self.get_archive_query(query_id, jid=jid, start=start_date)
    
    267
    +        self._send_archive_query(query, query_id, start_date, groupchat=True)
    
    268
    +
    
    269
    +    def request_archive_count(self, event_id, start_date, end_date):
    
    270
    +        query_id = self.get_query_id()
    
    271
    +        query = self.get_archive_query(
    
    272
    +            query_id, start=start_date, end=end_date, max_=0)
    
    273
    +        self.connection.SendAndCallForResponse(
    
    274
    +            query, self._received_count, {'query_id': query_id,
    
    275
    +                                          'event_id': event_id})
    
    276
    +
    
    277
    +    def request_archive_interval(self, event_id, start_date,
    
    278
    +                                 end_date, after=None):
    
    279
    +        query_id = self.get_query_id()
    
    280
    +        query = self.get_archive_query(query_id, start=start_date,
    
    281
    +                                       end=end_date, after=after, max_=30)
    
    282
    +        app.nec.push_incoming_event(ev.ArchivingQueryID(
    
    283
    +            None, event_id=event_id, query_id=query_id))
    
    284
    +        self.connection.SendAndCallForResponse(
    
    285
    +            query, self._intervall_result_finished, {'query_id': query_id,
    
    286
    +                                                     'start_date': start_date,
    
    287
    +                                                     'end_date': end_date,
    
    288
    +                                                     'event_id': event_id})
    
    157 289
     
    
    158
    -    def request_archive(self, query_id, start=None, end=None, with_=None,
    
    159
    -                        after=None, max_=30):
    
    290
    +    def _send_archive_query(self, query, query_id, start_date=None,
    
    291
    +                            groupchat=False):
    
    292
    +        self.connection.SendAndCallForResponse(
    
    293
    +            query, self._result_finished, {'query_id': query_id,
    
    294
    +                                           'start_date': start_date,
    
    295
    +                                           'groupchat': groupchat})
    
    296
    +
    
    297
    +    def get_archive_query(self, query_id, jid=None, start=None, end=None, with_=None,
    
    298
    +                          after=None, max_=30):
    
    160 299
             namespace = self.archiving_namespace
    
    161
    -        iq = nbxmpp.Iq('set')
    
    300
    +        iq = nbxmpp.Iq('set', to=jid)
    
    162 301
             query = iq.addChild('query', namespace=namespace)
    
    163 302
             form = query.addChild(node=nbxmpp.DataForm(typ='submit'))
    
    164 303
             field = nbxmpp.DataField(typ='hidden',
    
    ... ... @@ -184,9 +323,7 @@ class ConnectionArchive313:
    184 323
             if after:
    
    185 324
                 set_.setTagData('after', after)
    
    186 325
             query.setAttr('queryid', query_id)
    
    187
    -        id_ = self.connection.getAnID()
    
    188
    -        iq.setID(id_)
    
    189
    -        self.connection.send(iq)
    
    326
    +        return iq
    
    190 327
     
    
    191 328
         def request_archive_preferences(self):
    
    192 329
             if not app.account_is_connected(self.name):
    
    ... ... @@ -219,3 +356,7 @@ class ConnectionArchive313:
    219 356
             app.nec.push_incoming_event(ev.ArchivingReceivedEvent(None, conn=self,
    
    220 357
                 stanza=iq_obj))
    
    221 358
             raise nbxmpp.NodeProcessed
    
    359
    +
    
    360
    +
    
    361
    +class InvalidMamIQ(Exception):
    
    362
    +    pass

  • gajim/common/optparser.py
    ... ... @@ -242,6 +242,8 @@ class OptionsParser:
    242 242
                 self.update_config_to_016105()
    
    243 243
             if old < [0, 16, 11, 1] and new >= [0, 16, 11, 1]:
    
    244 244
                 self.update_config_to_016111()
    
    245
    +        if old < [0, 16, 11, 2] and new >= [0, 16, 11, 2]:
    
    246
    +            self.update_config_to_016112()
    
    245 247
     
    
    246 248
             app.logger.init_vars()
    
    247 249
             app.logger.attach_cache_database()
    
    ... ... @@ -1029,3 +1031,24 @@ class OptionsParser:
    1029 1031
                 log.exception('Error')
    
    1030 1032
             con.close()
    
    1031 1033
             app.config.set('version', '0.16.11.1')
    
    1034
    +
    
    1035
    +    def update_config_to_016112(self):
    
    1036
    +        con = sqlite.connect(logger.LOG_DB_PATH)
    
    1037
    +        cur = con.cursor()
    
    1038
    +        try:
    
    1039
    +            cur.executescript(
    
    1040
    +                    '''
    
    1041
    +                    CREATE TABLE IF NOT EXISTS last_archive_message(
    
    1042
    +                        jid_id INTEGER PRIMARY KEY UNIQUE,
    
    1043
    +                        last_mam_id TEXT,
    
    1044
    +                        oldest_mam_timestamp TEXT,
    
    1045
    +                        last_muc_timestamp TEXT
    
    1046
    +                        );
    
    1047
    +                    ALTER TABLE logs ADD COLUMN 'account_id' INTEGER;
    
    1048
    +                    '''
    
    1049
    +            )
    
    1050
    +            con.commit()
    
    1051
    +        except sqlite.OperationalError:
    
    1052
    +            log.exception('Error')
    
    1053
    +        con.close()
    
    1054
    +        app.config.set('version', '0.16.11.2')

  • gajim/groupchat_control.py
    ... ... @@ -47,6 +47,7 @@ from gajim import vcard
    47 47
     from gajim import cell_renderer_image
    
    48 48
     from gajim import dataforms_widget
    
    49 49
     from gajim.common.const import AvatarSize
    
    50
    +from gajim.common.caps_cache import muc_caps_cache
    
    50 51
     import nbxmpp
    
    51 52
     
    
    52 53
     from enum import IntEnum, unique
    
    ... ... @@ -478,6 +479,8 @@ class GroupchatControl(ChatControlBase):
    478 479
                 self._nec_gc_presence_received)
    
    479 480
             app.ged.register_event_handler('gc-message-received', ged.GUI1,
    
    480 481
                 self._nec_gc_message_received)
    
    482
    +        app.ged.register_event_handler('mam-decrypted-message-received',
    
    483
    +            ged.GUI1, self._nec_mam_decrypted_message_received)
    
    481 484
             app.ged.register_event_handler('vcard-published', ged.GUI1,
    
    482 485
                 self._nec_vcard_published)
    
    483 486
             app.ged.register_event_handler('update-gc-avatar', ged.GUI1,
    
    ... ... @@ -1053,6 +1056,17 @@ class GroupchatControl(ChatControlBase):
    1053 1056
                                     obj.contact.name, obj.contact.avatar_sha)
    
    1054 1057
             self.draw_avatar(obj.contact)
    
    1055 1058
     
    
    1059
    +    def _nec_mam_decrypted_message_received(self, obj):
    
    1060
    +        if not obj.groupchat:
    
    1061
    +            return
    
    1062
    +        if obj.room_jid != self.room_jid:
    
    1063
    +            return
    
    1064
    +        self.print_conversation(
    
    1065
    +            obj.msgtxt, contact=obj.nick,
    
    1066
    +            tim=obj.timestamp, encrypted=obj.encrypted,
    
    1067
    +            msg_stanza_id=obj.unique_id,
    
    1068
    +            additional_data=obj.additional_data)
    
    1069
    +
    
    1056 1070
         def _nec_gc_message_received(self, obj):
    
    1057 1071
             if obj.room_jid != self.room_jid or obj.conn.name != self.account:
    
    1058 1072
                 return
    
    ... ... @@ -1455,6 +1469,11 @@ class GroupchatControl(ChatControlBase):
    1455 1469
                 GLib.source_remove(self.autorejoin)
    
    1456 1470
             self.autorejoin = None
    
    1457 1471
     
    
    1472
    +        if muc_caps_cache.has_mam(self.room_jid):
    
    1473
    +            # Request MAM
    
    1474
    +            app.connections[self.account].request_archive_on_muc_join(
    
    1475
    +                self.room_jid)
    
    1476
    +
    
    1458 1477
             app.gc_connected[self.account][self.room_jid] = True
    
    1459 1478
             ChatControlBase.got_connected(self)
    
    1460 1479
             self.list_treeview.set_model(self.model)
    

  • gajim/history_sync.py
    ... ... @@ -19,7 +19,7 @@
    19 19
     
    
    20 20
     import logging
    
    21 21
     from enum import IntEnum
    
    22
    -from datetime import datetime, timedelta, timezone
    
    22
    +from datetime import datetime, timedelta
    
    23 23
     
    
    24 24
     import nbxmpp
    
    25 25
     from gi.repository import Gtk, GLib
    
    ... ... @@ -27,17 +27,16 @@ from gi.repository import Gtk, GLib
    27 27
     from gajim.common import app
    
    28 28
     from gajim.common import ged
    
    29 29
     from gajim.gtkgui_helpers import get_icon_pixmap
    
    30
    +from gajim.common.const import ArchiveState
    
    30 31
     
    
    31 32
     log = logging.getLogger('gajim.c.message_archiving')
    
    32 33
     
    
    34
    +
    
    33 35
     class Pages(IntEnum):
    
    34 36
         TIME = 0
    
    35 37
         SYNC = 1
    
    36 38
         SUMMARY = 2
    
    37 39
     
    
    38
    -class ArchiveState(IntEnum):
    
    39
    -    NEVER = 0
    
    40
    -    ALL = 1
    
    41 40
     
    
    42 41
     class HistorySyncAssistant(Gtk.Assistant):
    
    43 42
         def __init__(self, account, parent):
    
    ... ... @@ -52,13 +51,22 @@ class HistorySyncAssistant(Gtk.Assistant):
    52 51
             self.timedelta = None
    
    53 52
             self.now = datetime.utcnow()
    
    54 53
             self.query_id = None
    
    55
    -        self.count_query_id = None
    
    56 54
             self.start = None
    
    57 55
             self.end = None
    
    58 56
             self.next = None
    
    59 57
             self.hide_buttons()
    
    58
    +        self.event_id = id(self)
    
    59
    +
    
    60
    +        own_jid = self.con.get_own_jid().getStripped()
    
    61
    +        archive = app.logger.get_archive_timestamp(own_jid)
    
    62
    +
    
    63
    +        if archive is not None:
    
    64
    +            mam_start = float(archive.oldest_mam_timestamp)
    
    65
    +        else:
    
    66
    +            # Migration from old config value
    
    67
    +            mam_start = app.config.get_per(
    
    68
    +                'accounts', account, 'mam_start_date')
    
    60 69
     
    
    61
    -        mam_start = app.config.get_per('accounts', account, 'mam_start_date')
    
    62 70
             if not mam_start or mam_start == ArchiveState.NEVER:
    
    63 71
                 self.current_start = self.now
    
    64 72
             elif mam_start == ArchiveState.ALL:
    
    ... ... @@ -72,7 +80,8 @@ class HistorySyncAssistant(Gtk.Assistant):
    72 80
     
    
    73 81
             self.download_history = DownloadHistoryPage(self)
    
    74 82
             self.append_page(self.download_history)
    
    75
    -        self.set_page_type(self.download_history, Gtk.AssistantPageType.PROGRESS)
    
    83
    +        self.set_page_type(self.download_history,
    
    84
    +                           Gtk.AssistantPageType.PROGRESS)
    
    76 85
             self.set_page_complete(self.download_history, True)
    
    77 86
     
    
    78 87
             self.summary = SummaryPage(self)
    
    ... ... @@ -80,12 +89,18 @@ class HistorySyncAssistant(Gtk.Assistant):
    80 89
             self.set_page_type(self.summary, Gtk.AssistantPageType.SUMMARY)
    
    81 90
             self.set_page_complete(self.summary, True)
    
    82 91
     
    
    83
    -        app.ged.register_event_handler('archiving-finished',
    
    84
    -                                         ged.PRECORE,
    
    85
    -                                         self._nec_archiving_finished)
    
    92
    +        app.ged.register_event_handler('archiving-count-received',
    
    93
    +                                       ged.GUI1,
    
    94
    +                                       self._received_count)
    
    95
    +        app.ged.register_event_handler('archiving-query-id',
    
    96
    +                                       ged.GUI1,
    
    97
    +                                       self._new_query_id)
    
    98
    +        app.ged.register_event_handler('archiving-interval-finished',
    
    99
    +                                       ged.GUI1,
    
    100
    +                                       self._received_finished)
    
    86 101
             app.ged.register_event_handler('raw-mam-message-received',
    
    87
    -                                         ged.PRECORE,
    
    88
    -                                         self._nec_mam_message_received)
    
    102
    +                                       ged.PRECORE,
    
    103
    +                                       self._nec_mam_message_received)
    
    89 104
     
    
    90 105
             self.connect('prepare', self.on_page_change)
    
    91 106
             self.connect('destroy', self.on_destroy)
    
    ... ... @@ -96,9 +111,9 @@ class HistorySyncAssistant(Gtk.Assistant):
    96 111
                 self.set_current_page(Pages.SUMMARY)
    
    97 112
                 self.summary.nothing_to_do()
    
    98 113
     
    
    99
    -        if self.con.mam_query_id:
    
    100
    -            self.set_current_page(Pages.SUMMARY)
    
    101
    -            self.summary.query_already_running()
    
    114
    +        # if self.con.mam_query_ids:
    
    115
    +        #     self.set_current_page(Pages.SUMMARY)
    
    116
    +        #     self.summary.query_already_running()
    
    102 117
     
    
    103 118
             self.show_all()
    
    104 119
     
    
    ... ... @@ -129,27 +144,42 @@ class HistorySyncAssistant(Gtk.Assistant):
    129 144
                 self.start = self.now - self.timedelta
    
    130 145
             self.end = self.current_start
    
    131 146
     
    
    132
    -        log.info('config: get mam_start_date: %s', self.current_start)
    
    147
    +        log.info('get mam_start_date: %s', self.current_start)
    
    133 148
             log.info('now: %s', self.now)
    
    134 149
             log.info('start: %s', self.start)
    
    135 150
             log.info('end: %s', self.end)
    
    136 151
     
    
    137
    -        self.query_count()
    
    152
    +        self.con.request_archive_count(self.event_id, self.start, self.end)
    
    153
    +
    
    154
    +    def _received_count(self, event):
    
    155
    +        if event.event_id != self.event_id:
    
    156
    +            return
    
    157
    +        if event.count is not None:
    
    158
    +            self.download_history.count = int(event.count)
    
    159
    +        self.con.request_archive_interval(self.event_id, self.start, self.end)
    
    160
    +
    
    161
    +    def _received_finished(self, event):
    
    162
    +        if event.event_id != self.event_id:
    
    163
    +            return
    
    164
    +        log.info('query finished')
    
    165
    +        GLib.idle_add(self.download_history.finished)
    
    166
    +        self.set_current_page(Pages.SUMMARY)
    
    167
    +        self.summary.finished()
    
    168
    +
    
    169
    +    def _new_query_id(self, event):
    
    170
    +        if event.event_id != self.event_id:
    
    171
    +            return
    
    172
    +        self.query_id = event.query_id
    
    173
    +
    
    174
    +    def _nec_mam_message_received(self, obj):
    
    175
    +        if obj.conn.name != self.account:
    
    176
    +            return
    
    138 177
     
    
    139
    -    def query_count(self):
    
    140
    -        self.count_query_id = self.con.connection.getAnID()
    
    141
    -        self.con.request_archive(self.count_query_id,
    
    142
    -                                 start=self.start,
    
    143
    -                                 end=self.end,
    
    144
    -                                 max_=0)
    
    178
    +        if obj.result.getAttr('queryid') != self.query_id:
    
    179
    +            return
    
    145 180
     
    
    146
    -    def query_messages(self, last=None):
    
    147
    -        self.query_id = self.con.connection.getAnID()
    
    148
    -        self.con.request_archive(self.query_id,
    
    149
    -                                 start=self.start,
    
    150
    -                                 end=self.end,
    
    151
    -                                 after=last,
    
    152
    -                                 max_=30)
    
    181
    +        log.debug('received message')
    
    182
    +        GLib.idle_add(self.download_history.set_fraction)
    
    153 183
     
    
    154 184
         def on_row_selected(self, listbox, row):
    
    155 185
             self.timedelta = row.get_child().get_delta()
    
    ... ... @@ -164,71 +194,23 @@ class HistorySyncAssistant(Gtk.Assistant):
    164 194
                 self.prepare_query()
    
    165 195
     
    
    166 196
         def on_destroy(self, *args):
    
    167
    -        app.ged.remove_event_handler('archiving-finished',
    
    168
    -                                       ged.PRECORE,
    
    169
    -                                       self._nec_archiving_finished)
    
    197
    +        app.ged.remove_event_handler('archiving-count-received',
    
    198
    +                                     ged.GUI1,
    
    199
    +                                     self._received_count)
    
    200
    +        app.ged.remove_event_handler('archiving-query-id',
    
    201
    +                                     ged.GUI1,
    
    202
    +                                     self._new_query_id)
    
    203
    +        app.ged.remove_event_handler('archiving-interval-finished',
    
    204
    +                                     ged.GUI1,
    
    205
    +                                     self._received_finished)
    
    170 206
             app.ged.remove_event_handler('raw-mam-message-received',
    
    171
    -                                       ged.PRECORE,
    
    172
    -                                       self._nec_mam_message_received)
    
    207
    +                                     ged.PRECORE,
    
    208
    +                                     self._nec_mam_message_received)
    
    173 209
             del app.interface.instances[self.account]['history_sync']
    
    174 210
     
    
    175 211
         def on_close_clicked(self, *args):
    
    176 212
             self.destroy()
    
    177 213
     
    
    178
    -    def _nec_mam_message_received(self, obj):
    
    179
    -        if obj.conn.name != self.account:
    
    180
    -            return
    
    181
    -
    
    182
    -        if obj.result.getAttr('queryid') != self.query_id:
    
    183
    -            return
    
    184
    -
    
    185
    -        log.debug('received message')
    
    186
    -        GLib.idle_add(self.download_history.set_fraction)
    
    187
    -
    
    188
    -    def _nec_archiving_finished(self, obj):
    
    189
    -        if obj.conn.name != self.account:
    
    190
    -            return
    
    191
    -
    
    192
    -        if obj.query_id not in (self.query_id, self.count_query_id):
    
    193
    -            return
    
    194
    -
    
    195
    -        set_ = obj.fin.getTag('set', namespace=nbxmpp.NS_RSM)
    
    196
    -        if not set_:
    
    197
    -            log.error('invalid result')
    
    198
    -            log.error(obj.fin)
    
    199
    -            return
    
    200
    -
    
    201
    -        if obj.query_id == self.count_query_id:
    
    202
    -            count = set_.getTagData('count')
    
    203
    -            log.info('message count received: %s', count)
    
    204
    -            if count:
    
    205
    -                self.download_history.count = int(count)
    
    206
    -            self.query_messages()
    
    207
    -            return
    
    208
    -
    
    209
    -        if obj.query_id == self.query_id:
    
    210
    -            last = set_.getTagData('last')
    
    211
    -            complete = obj.fin.getAttr('complete')
    
    212
    -            if not last and complete != 'true':
    
    213
    -                log.error('invalid result')
    
    214
    -                log.error(obj.fin)
    
    215
    -                return
    
    216
    -
    
    217
    -            if complete != 'true':
    
    218
    -                self.query_messages(last)
    
    219
    -            else:
    
    220
    -                log.info('query finished')
    
    221
    -                GLib.idle_add(self.download_history.finished)
    
    222
    -                if self.start:
    
    223
    -                    timestamp = self.start.timestamp()
    
    224
    -                else:
    
    225
    -                    timestamp = ArchiveState.ALL
    
    226
    -                app.config.set_per('accounts', self.account,
    
    227
    -                                     'mam_start_date', timestamp)
    
    228
    -                log.debug('config: set mam_start_date: %s', timestamp)
    
    229
    -                self.set_current_page(Pages.SUMMARY)
    
    230
    -                self.summary.finished()
    
    231
    -
    
    232 214
     
    
    233 215
     class SelectTimePage(Gtk.Box):
    
    234 216
         def __init__(self, assistant):
    
    ... ... @@ -257,6 +239,7 @@ class SelectTimePage(Gtk.Box):
    257 239
             self.pack_start(label, True, True, 0)
    
    258 240
             self.pack_start(listbox, False, False, 0)
    
    259 241
     
    
    242
    +
    
    260 243
     class DownloadHistoryPage(Gtk.Box):
    
    261 244
         def __init__(self, assistant):
    
    262 245
             super().__init__(orientation=Gtk.Orientation.VERTICAL)
    
    ... ... @@ -292,6 +275,7 @@ class DownloadHistoryPage(Gtk.Box):
    292 275
         def finished(self):
    
    293 276
             self.progress.set_fraction(1)
    
    294 277
     
    
    278
    +
    
    295 279
     class SummaryPage(Gtk.Box):
    
    296 280
         def __init__(self, assistant):
    
    297 281
             super().__init__(orientation=Gtk.Orientation.VERTICAL)
    
    ... ... @@ -307,25 +291,26 @@ class SummaryPage(Gtk.Box):
    307 291
         def finished(self):
    
    308 292
             received = self.assistant.download_history.received
    
    309 293
             finished = _('''
    
    310
    -        Finshed synchronising your History. 
    
    294
    +        Finshed synchronising your History.
    
    311 295
             {received} Messages downloaded.
    
    312 296
             '''.format(received=received))
    
    313 297
             self.label.set_text(finished)
    
    314 298
     
    
    315 299
         def nothing_to_do(self):
    
    316 300
             nothing_to_do = _('''
    
    317
    -        Gajim is fully synchronised 
    
    301
    +        Gajim is fully synchronised
    
    318 302
             with the Archive.
    
    319 303
             ''')
    
    320 304
             self.label.set_text(nothing_to_do)
    
    321 305
     
    
    322 306
         def query_already_running(self):
    
    323 307
             already_running = _('''
    
    324
    -        There is already a synchronisation in 
    
    308
    +        There is already a synchronisation in
    
    325 309
             progress. Please try later.
    
    326 310
             ''')
    
    327 311
             self.label.set_text(already_running)
    
    328 312
     
    
    313
    +
    
    329 314
     class TimeOption(Gtk.Label):
    
    330 315
         def __init__(self, label, months=None):
    
    331 316
             super().__init__(label=label)
    

  • gajim/session.py
    ... ... @@ -117,7 +117,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
    117 117
                     jid = obj.jid
    
    118 118
     
    
    119 119
                 obj.msg_log_id = app.logger.insert_into_logs(
    
    120
    -                jid, obj.timestamp, log_type,
    
    120
    +                self.conn.name, jid, obj.timestamp, log_type,
    
    121 121
                     message=msg_to_log,
    
    122 122
                     subject=obj.subject,
    
    123 123
                     additional_data=obj.additional_data,