Philipp Hörist pushed to branch master at gajim / gajim
Commits:
-
75b1d54b
by Philipp Hörist at 2017-11-19T19:36:20+01:00
-
536a504f
by Philipp Hörist at 2017-11-19T19:36:25+01:00
-
52827c6a
by Philipp Hörist at 2017-11-19T19:36:25+01:00
-
9759dcf1
by Philipp Hörist at 2017-11-19T19:36:25+01:00
-
a07470a7
by Philipp Hörist at 2017-11-19T19:36:25+01:00
-
95357361
by Philipp Hörist at 2017-11-19T19:36:25+01:00
-
40f537cd
by Philipp Hörist at 2017-11-19T19:36:26+01:00
-
f57ec2fa
by Philipp Hörist at 2017-11-19T19:36:26+01:00
-
51c0a05a
by Philipp Hörist at 2017-11-19T19:36:26+01:00
-
44bb8e37
by Philipp Hörist at 2017-11-19T19:36:26+01:00
-
cac1185a
by Philipp Hörist at 2017-11-19T19:36:26+01:00
-
7f1776ed
by Philipp Hörist at 2017-11-19T19:36:26+01:00
-
3683f238
by Philipp Hörist at 2017-11-19T23:14:10+01:00
14 changed files:
- gajim/__init__.py
- gajim/common/caps_cache.py
- gajim/common/check_paths.py
- gajim/common/config.py
- gajim/common/connection.py
- gajim/common/connection_handlers.py
- gajim/common/connection_handlers_events.py
- gajim/common/const.py
- gajim/common/logger.py
- gajim/common/message_archiving.py
- gajim/common/optparser.py
- gajim/groupchat_control.py
- gajim/history_sync.py
- gajim/session.py
Changes:
| 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,
|
| ... | ... | @@ -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
|
| ... | ... | @@ -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 |
)
|
| ... | ... | @@ -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, '' ],
|
| ... | ... | @@ -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):
|
| ... | ... | @@ -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')
|
| ... | ... | @@ -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 = []
|
| ... | ... | @@ -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
|
| ... | ... | @@ -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()
|
| ... | ... | @@ -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
|
| ... | ... | @@ -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')
|
| ... | ... | @@ -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)
|
| ... | ... | @@ -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)
|
| ... | ... | @@ -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,
|