Philipp Hörist pushed to branch master at gajim / gajim
Commits:
-
d6178669
by Philipp Hörist at 2019-07-21T14:30:14Z
-
e97a6317
by Philipp Hörist at 2019-07-21T17:38:15Z
-
dc282f22
by Philipp Hörist at 2019-07-21T18:02:05Z
9 changed files:
- gajim/common/config.py
- gajim/common/logger.py
- gajim/common/modules/mam.py
- gajim/common/modules/message.py
- gajim/common/modules/muc.py
- gajim/common/structs.py
- gajim/groupchat_control.py
- gajim/gtk/xml_console.py
- gajim/session.py
Changes:
| ... | ... | @@ -170,8 +170,6 @@ class Config: |
| 170 | 170 |
'change_roster_title': [opt_bool, True, _('If enabled, Gajim will add * and [n] in contact list window title.')],
|
| 171 | 171 |
'restore_lines': [opt_int, 10, _('Number of messages from chat history to be restored when a chat tab/window is reopened.')],
|
| 172 | 172 |
'restore_timeout': [opt_int, -1, _('How far back in time (minutes) chat history is restored. -1 means no limit.')],
|
| 173 |
- 'muc_restore_lines': [opt_int, 100, _('How many lines to request from server when entering a group chat. -1 means no limit.')],
|
|
| 174 |
- 'muc_restore_timeout': [opt_int, -1, _('Minutes of backlog to request when entering a group chat. -1 means no limit.')],
|
|
| 175 | 173 |
'muc_autorejoin_timeout': [opt_int, 1, _('How many seconds to wait before trying to automatically rejoin a group chat you were disconnected from. Set to 0 to disable automatic rejoining.')],
|
| 176 | 174 |
'muc_autorejoin_on_kick': [opt_bool, False, _('Should automatic rejoin be activated when you were kicked from a group chat?')],
|
| 177 | 175 |
'send_on_ctrl_enter': [opt_bool, False, _('Send message on Ctrl+Enter and make a new line with Enter.')],
|
| ... | ... | @@ -429,8 +427,6 @@ class Config: |
| 429 | 427 |
}, {}),
|
| 430 | 428 |
'rooms': ({
|
| 431 | 429 |
'speller_language': [opt_str, '', _('Language used for spell checking.')],
|
| 432 |
- 'muc_restore_lines': [opt_int, -2, _('How many lines to request from server when entering a group chat. -1 means no limit, -2 means global value.')],
|
|
| 433 |
- 'muc_restore_timeout': [opt_int, -2, _('Minutes of backlog to request when entering a group chat. -1 means no limit, -2 means global value.')],
|
|
| 434 | 430 |
'notify_on_all_messages': [opt_bool, False, _('If enabled, a notification is created for every message in this group chat.')],
|
| 435 | 431 |
'print_status': [opt_bool, False, _('Show a status message for all status changes (away, dnd, etc.) of users in a group chat.')],
|
| 436 | 432 |
'print_join_left': [opt_bool, False, _('Show a status message for every join or leave in a group chat.')],
|
| ... | ... | @@ -103,10 +103,7 @@ CACHE_SQL_STATEMENT = ''' |
| 103 | 103 |
hash_method TEXT,
|
| 104 | 104 |
hash TEXT,
|
| 105 | 105 |
data TEXT,
|
| 106 |
- last_seen INTEGER);
|
|
| 107 |
- CREATE TABLE rooms_last_message_time(
|
|
| 108 |
- jid_id INTEGER PRIMARY KEY UNIQUE,
|
|
| 109 |
- time INTEGER
|
|
| 106 |
+ last_seen INTEGER
|
|
| 110 | 107 |
);
|
| 111 | 108 |
CREATE TABLE roster_entry(
|
| 112 | 109 |
account_jid_id INTEGER,
|
| ... | ... | @@ -962,45 +959,6 @@ class Logger: |
| 962 | 959 |
return self._con.execute(
|
| 963 | 960 |
sql, tuple(jids) + (start, end)).fetchone()
|
| 964 | 961 |
|
| 965 |
- def get_room_last_message_time(self, account, jid):
|
|
| 966 |
- """
|
|
| 967 |
- Get the timestamp of the last message we received in a room.
|
|
| 968 |
- |
|
| 969 |
- :param account: The account
|
|
| 970 |
- |
|
| 971 |
- :param jid: The jid for which we request the last timestamp
|
|
| 972 |
- |
|
| 973 |
- returns a timestamp or None
|
|
| 974 |
- """
|
|
| 975 |
- sql = '''
|
|
| 976 |
- SELECT time FROM rooms_last_message_time
|
|
| 977 |
- NATURAL JOIN jids WHERE jid = ?
|
|
| 978 |
- '''
|
|
| 979 |
- |
|
| 980 |
- row = self._con.execute(sql, (jid,)).fetchone()
|
|
| 981 |
- if not row:
|
|
| 982 |
- return self.get_last_date_that_has_logs(account, jid)
|
|
| 983 |
- return row.time
|
|
| 984 |
- |
|
| 985 |
- def set_room_last_message_time(self, jid, timestamp):
|
|
| 986 |
- """
|
|
| 987 |
- Set the timestamp of the last message we received in a room.
|
|
| 988 |
- |
|
| 989 |
- :param jid: The jid
|
|
| 990 |
- |
|
| 991 |
- :param timestamp: The timestamp in epoch
|
|
| 992 |
- |
|
| 993 |
- """
|
|
| 994 |
- |
|
| 995 |
- jid_id = self.get_jid_id(jid, type_=JIDConstant.ROOM_TYPE)
|
|
| 996 |
- sql = '''REPLACE INTO rooms_last_message_time
|
|
| 997 |
- VALUES (:jid_id, COALESCE(
|
|
| 998 |
- (SELECT time FROM rooms_last_message_time
|
|
| 999 |
- WHERE jid_id = :jid_id AND time >= :time), :time))'''
|
|
| 1000 |
- |
|
| 1001 |
- self._con.execute(sql, {"jid_id": jid_id, "time": timestamp})
|
|
| 1002 |
- self._timeout_commit()
|
|
| 1003 |
- |
|
| 1004 | 962 |
def save_transport_type(self, jid, type_):
|
| 1005 | 963 |
"""
|
| 1006 | 964 |
Save the type of the transport in DB
|
| ... | ... | @@ -1497,6 +1455,10 @@ class Logger: |
| 1497 | 1455 |
kwargs.get('sync_threshold', None)
|
| 1498 | 1456 |
))
|
| 1499 | 1457 |
else:
|
| 1458 |
+ for key, value in list(kwargs.items()):
|
|
| 1459 |
+ if value is None:
|
|
| 1460 |
+ del kwargs[key]
|
|
| 1461 |
+ |
|
| 1500 | 1462 |
args = ' = ?, '.join(kwargs.keys()) + ' = ?'
|
| 1501 | 1463 |
sql = '''UPDATE last_archive_message SET {}
|
| 1502 | 1464 |
WHERE jid_id = ?'''.format(args)
|
| ... | ... | @@ -73,6 +73,9 @@ class MAM(BaseModule): |
| 73 | 73 |
self._mam_query_ids.clear()
|
| 74 | 74 |
self._catch_up_finished.clear()
|
| 75 | 75 |
|
| 76 |
+ def is_catch_up_finished(self, jid):
|
|
| 77 |
+ return jid in self._catch_up_finished
|
|
| 78 |
+ |
|
| 76 | 79 |
def _from_valid_archive(self, _stanza, properties):
|
| 77 | 80 |
if properties.type.is_groupchat:
|
| 78 | 81 |
expected_archive = properties.jid
|
| ... | ... | @@ -531,20 +534,6 @@ class MAM(BaseModule): |
| 531 | 534 |
query.setAttr('queryid', query_id)
|
| 532 | 535 |
return iq
|
| 533 | 536 |
|
| 534 |
- def save_archive_id(self, jid, stanza_id, timestamp):
|
|
| 535 |
- if jid is None:
|
|
| 536 |
- jid = self._con.get_own_jid().getStripped()
|
|
| 537 |
- if jid not in self._catch_up_finished:
|
|
| 538 |
- return
|
|
| 539 |
- self._log.info('Save: %s: %s, %s', jid, stanza_id, timestamp)
|
|
| 540 |
- if stanza_id is None:
|
|
| 541 |
- # mam:1
|
|
| 542 |
- app.logger.set_archive_infos(jid, last_muc_timestamp=timestamp)
|
|
| 543 |
- else:
|
|
| 544 |
- # mam:2
|
|
| 545 |
- app.logger.set_archive_infos(
|
|
| 546 |
- jid, last_mam_id=stanza_id, last_muc_timestamp=timestamp)
|
|
| 547 |
- |
|
| 548 | 537 |
def request_mam_preferences(self):
|
| 549 | 538 |
self._log.info('Request MAM preferences')
|
| 550 | 539 |
iq = nbxmpp.Iq('get', self.archiving_namespace)
|
| ... | ... | @@ -26,6 +26,7 @@ from gajim.common.i18n import _ |
| 26 | 26 |
from gajim.common.nec import NetworkIncomingEvent
|
| 27 | 27 |
from gajim.common.nec import NetworkEvent
|
| 28 | 28 |
from gajim.common.helpers import AdditionalDataDict
|
| 29 |
+from gajim.common.caps_cache import muc_caps_cache
|
|
| 29 | 30 |
from gajim.common.const import KindConstant
|
| 30 | 31 |
from gajim.common.modules.base import BaseModule
|
| 31 | 32 |
from gajim.common.modules.util import get_eme_message
|
| ... | ... | @@ -251,7 +252,6 @@ class Message(BaseModule): |
| 251 | 252 |
event.nickname = event.resource
|
| 252 | 253 |
event.xhtml_msgtxt = event.xhtml
|
| 253 | 254 |
event.nick = event.resource or ''
|
| 254 |
- |
|
| 255 | 255 |
app.nec.push_incoming_event(NetworkEvent('gc-message-received',
|
| 256 | 256 |
**vars(event)))
|
| 257 | 257 |
# TODO: Some plugins modify msgtxt in the GUI event
|
| ... | ... | @@ -292,9 +292,16 @@ class Message(BaseModule): |
| 292 | 292 |
additional_data=event.additional_data,
|
| 293 | 293 |
stanza_id=event.stanza_id,
|
| 294 | 294 |
message_id=event.message_id)
|
| 295 |
- app.logger.set_room_last_message_time(event.room_jid, event.timestamp)
|
|
| 296 |
- self._con.get_module('MAM').save_archive_id(
|
|
| 297 |
- event.room_jid, event.stanza_id, event.timestamp)
|
|
| 295 |
+ |
|
| 296 |
+ if muc_caps_cache.has_mam(event.room_jid):
|
|
| 297 |
+ finished = self._con.get_module('MAM').is_catchup_finished(
|
|
| 298 |
+ event.room_jid)
|
|
| 299 |
+ if not finished:
|
|
| 300 |
+ return
|
|
| 301 |
+ |
|
| 302 |
+ app.logger.set_archive_infos(event.room_jid,
|
|
| 303 |
+ last_mam_id=event.stanza_id,
|
|
| 304 |
+ last_muc_timestamp=event.timestamp)
|
|
| 298 | 305 |
|
| 299 | 306 |
def _check_for_mam_compliance(self, room_jid, stanza_id):
|
| 300 | 307 |
namespace = caps_cache.muc_caps_cache.get_mam_namespace(room_jid)
|
| ... | ... | @@ -17,6 +17,8 @@ |
| 17 | 17 |
|
| 18 | 18 |
import time
|
| 19 | 19 |
import logging
|
| 20 |
+from datetime import datetime
|
|
| 21 |
+from datetime import timedelta
|
|
| 20 | 22 |
from functools import partial
|
| 21 | 23 |
|
| 22 | 24 |
import nbxmpp
|
| ... | ... | @@ -30,9 +32,11 @@ from gajim.common import app |
| 30 | 32 |
from gajim.common import helpers
|
| 31 | 33 |
from gajim.common.const import KindConstant
|
| 32 | 34 |
from gajim.common.const import MUCJoinedState
|
| 35 |
+from gajim.common.const import SyncThreshold
|
|
| 33 | 36 |
from gajim.common.structs import MUCData
|
| 34 | 37 |
from gajim.common.helpers import AdditionalDataDict
|
| 35 | 38 |
from gajim.common.helpers import get_default_muc_config
|
| 39 |
+from gajim.common.helpers import get_sync_threshold
|
|
| 36 | 40 |
from gajim.common import idle
|
| 37 | 41 |
from gajim.common.caps_cache import muc_caps_cache
|
| 38 | 42 |
from gajim.common.nec import NetworkEvent
|
| ... | ... | @@ -130,11 +134,11 @@ class MUC(BaseModule): |
| 130 | 134 |
def get_mucs_with_state(self, states):
|
| 131 | 135 |
return [muc for muc in self._muc_data.values() if muc.state in states]
|
| 132 | 136 |
|
| 133 |
- def join(self, room_jid, nick, password, rejoin=False, config=None):
|
|
| 137 |
+ def join(self, room_jid, nick, password, config=None):
|
|
| 134 | 138 |
if not app.account_is_connected(self._account):
|
| 135 | 139 |
return
|
| 136 | 140 |
|
| 137 |
- muc_data = MUCData(room_jid, nick, password, rejoin, config)
|
|
| 141 |
+ muc_data = MUCData(room_jid, nick, password, config)
|
|
| 138 | 142 |
self._muc_data[room_jid] = muc_data
|
| 139 | 143 |
|
| 140 | 144 |
self._con.get_module('Discovery').disco_muc(
|
| ... | ... | @@ -149,7 +153,7 @@ class MUC(BaseModule): |
| 149 | 153 |
status=self._con.status)
|
| 150 | 154 |
|
| 151 | 155 |
muc_x = presence.setTag(nbxmpp.NS_MUC + ' x')
|
| 152 |
- self._add_history_query(muc_x, str(muc_data.jid), muc_data.rejoin)
|
|
| 156 |
+ self._add_history_query(muc_x, str(muc_data.jid))
|
|
| 153 | 157 |
|
| 154 | 158 |
if muc_data.password is not None:
|
| 155 | 159 |
muc_x.setTagData('password', muc_data.password)
|
| ... | ... | @@ -281,37 +285,27 @@ class MUC(BaseModule): |
| 281 | 285 |
show=show,
|
| 282 | 286 |
status=self._con.status)
|
| 283 | 287 |
|
| 284 |
- def _add_history_query(self, muc_x, room_jid, rejoin):
|
|
| 285 |
- last_date = app.logger.get_room_last_message_time(
|
|
| 286 |
- self._account, room_jid)
|
|
| 287 |
- if not last_date:
|
|
| 288 |
- last_date = 0
|
|
| 289 |
- |
|
| 288 |
+ def _add_history_query(self, muc_x, room_jid):
|
|
| 290 | 289 |
if muc_caps_cache.has_mam(room_jid):
|
| 291 | 290 |
# The room is MAM capable dont get MUC History
|
| 292 | 291 |
muc_x.setTag('history', {'maxchars': '0'})
|
| 293 | 292 |
else:
|
| 294 | 293 |
# Request MUC History (not MAM)
|
| 295 |
- tags = {}
|
|
| 296 |
- timeout = app.config.get_per('rooms', room_jid,
|
|
| 297 |
- 'muc_restore_timeout')
|
|
| 298 |
- if timeout is None or timeout == -2:
|
|
| 299 |
- timeout = app.config.get('muc_restore_timeout')
|
|
| 300 |
- if last_date == 0 and timeout >= 0:
|
|
| 301 |
- last_date = time.time() - timeout * 60
|
|
| 302 |
- elif not rejoin and timeout >= 0:
|
|
| 303 |
- last_date = max(last_date, time.time() - timeout * 60)
|
|
| 304 |
- last_date = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(
|
|
| 305 |
- last_date))
|
|
| 306 |
- tags['since'] = last_date
|
|
| 307 |
- |
|
| 308 |
- nb = app.config.get_per('rooms', room_jid, 'muc_restore_lines')
|
|
| 309 |
- if nb is None or nb == -2:
|
|
| 310 |
- nb = app.config.get('muc_restore_lines')
|
|
| 311 |
- if nb >= 0:
|
|
| 312 |
- tags['maxstanzas'] = nb
|
|
| 313 |
- if tags:
|
|
| 314 |
- muc_x.setTag('history', tags)
|
|
| 294 |
+ archive = app.logger.get_archive_infos(room_jid)
|
|
| 295 |
+ threshold = get_sync_threshold(room_jid, archive)
|
|
| 296 |
+ |
|
| 297 |
+ since_epoch = 0
|
|
| 298 |
+ if archive is not None and archive.last_muc_timestamp is not None:
|
|
| 299 |
+ since_epoch = float(archive.last_muc_timestamp)
|
|
| 300 |
+ |
|
| 301 |
+ since_date = datetime.utcfromtimestamp(since_epoch)
|
|
| 302 |
+ if threshold != SyncThreshold.NO_THRESHOLD:
|
|
| 303 |
+ threshold_date = datetime.utcnow() - timedelta(days=threshold)
|
|
| 304 |
+ since_date = max(threshold_date, since_date)
|
|
| 305 |
+ |
|
| 306 |
+ date_string = since_date.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
| 307 |
+ muc_x.setTag('history', {'since': date_string})
|
|
| 308 |
+ self._log.info('Threshold for %s: %s', room_jid, threshold)
|
|
| 315 | 309 |
|
| 316 | 310 |
def _on_muc_presence(self, _con, _stanza, properties):
|
| 317 | 311 |
muc_data = self._get_muc_data(properties.muc_jid)
|
| ... | ... | @@ -26,11 +26,10 @@ CapsIdentity = namedtuple('CapsIdentity', 'category type name lang') |
| 26 | 26 |
|
| 27 | 27 |
|
| 28 | 28 |
class MUCData:
|
| 29 |
- def __init__(self, room_jid, nick, password, rejoin, config=None):
|
|
| 29 |
+ def __init__(self, room_jid, nick, password, config=None):
|
|
| 30 | 30 |
self._room_jid = JID(room_jid)
|
| 31 | 31 |
self._nick = nick
|
| 32 | 32 |
self._password = password
|
| 33 |
- self._rejoin = rejoin
|
|
| 34 | 33 |
self._config = config
|
| 35 | 34 |
self._state = MUCJoinedState.NOT_JOINED
|
| 36 | 35 |
|
| ... | ... | @@ -64,14 +63,6 @@ class MUCData: |
| 64 | 63 |
def state(self, value):
|
| 65 | 64 |
self._state = value
|
| 66 | 65 |
|
| 67 |
- @property
|
|
| 68 |
- def rejoin(self):
|
|
| 69 |
- return self._rejoin
|
|
| 70 |
- |
|
| 71 |
- @rejoin.setter
|
|
| 72 |
- def rejoin(self, value):
|
|
| 73 |
- self._rejoin = value
|
|
| 74 |
- |
|
| 75 | 66 |
@property
|
| 76 | 67 |
def config(self):
|
| 77 | 68 |
return self._config
|
| ... | ... | @@ -511,10 +511,6 @@ class GroupchatControl(ChatControlBase): |
| 511 | 511 |
vcard_support and
|
| 512 | 512 |
contact.affiliation.is_owner)
|
| 513 | 513 |
|
| 514 |
- # Sync Threshold
|
|
| 515 |
- has_mam = muc_caps_cache.has_mam(self.room_jid)
|
|
| 516 |
- self._get_action('choose-sync-').set_enabled(has_mam)
|
|
| 517 |
- |
|
| 518 | 514 |
# Print join/left
|
| 519 | 515 |
join_default = app.config.get('print_join_left_default')
|
| 520 | 516 |
value = app.config.get_per('rooms', self.contact.jid,
|
| ... | ... | @@ -1414,8 +1410,7 @@ class GroupchatControl(ChatControlBase): |
| 1414 | 1410 |
if obj.conn.name != self.account:
|
| 1415 | 1411 |
return
|
| 1416 | 1412 |
password = app.gc_passwords.get(self.room_jid, '')
|
| 1417 |
- obj.conn.get_module('MUC').join(self.room_jid, self.nick, password,
|
|
| 1418 |
- rejoin=True)
|
|
| 1413 |
+ obj.conn.get_module('MUC').join(self.room_jid, self.nick, password)
|
|
| 1419 | 1414 |
|
| 1420 | 1415 |
def _nec_decrypted_message_received(self, obj):
|
| 1421 | 1416 |
if obj.conn.name != self.account:
|
| ... | ... | @@ -1552,7 +1547,7 @@ class GroupchatControl(ChatControlBase): |
| 1552 | 1547 |
return False
|
| 1553 | 1548 |
password = app.gc_passwords.get(self.room_jid, '')
|
| 1554 | 1549 |
app.connections[self.account].get_module('MUC').join(
|
| 1555 |
- self.room_jid, self.nick, password, rejoin=True)
|
|
| 1550 |
+ self.room_jid, self.nick, password)
|
|
| 1556 | 1551 |
return True
|
| 1557 | 1552 |
|
| 1558 | 1553 |
def draw_roster(self):
|
| ... | ... | @@ -34,9 +34,6 @@ from gajim.gtk.const import SettingKind |
| 34 | 34 |
from gajim.gtk.const import SettingType
|
| 35 | 35 |
|
| 36 | 36 |
|
| 37 |
-UNDECLARED = 'http://www.gajim.org/xmlns/undeclared'
|
|
| 38 |
- |
|
| 39 |
- |
|
| 40 | 37 |
class XMLConsoleWindow(Gtk.ApplicationWindow):
|
| 41 | 38 |
def __init__(self, account):
|
| 42 | 39 |
Gtk.ApplicationWindow.__init__(self)
|
| ... | ... | @@ -141,19 +138,19 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 141 | 138 |
input_text = None
|
| 142 | 139 |
if text == 'Presence':
|
| 143 | 140 |
input_text = (
|
| 144 |
- '<presence>\n'
|
|
| 141 |
+ '<presence xmlns="jabber:client">\n'
|
|
| 145 | 142 |
'<show></show>\n'
|
| 146 | 143 |
'<status></status>\n'
|
| 147 | 144 |
'<priority></priority>\n'
|
| 148 | 145 |
'</presence>')
|
| 149 | 146 |
elif text == 'Message':
|
| 150 | 147 |
input_text = (
|
| 151 |
- '<message to="" type="">\n'
|
|
| 148 |
+ '<message to="" type="" xmlns="jabber:client">\n'
|
|
| 152 | 149 |
'<body></body>\n'
|
| 153 | 150 |
'</message>')
|
| 154 | 151 |
elif text == 'Iq':
|
| 155 | 152 |
input_text = (
|
| 156 |
- '<iq to="" type="">\n'
|
|
| 153 |
+ '<iq to="" type="" xmlns="jabber:client">\n'
|
|
| 157 | 154 |
'<query xmlns=""></query>\n'
|
| 158 | 155 |
'</iq>')
|
| 159 | 156 |
|
| ... | ... | @@ -176,8 +173,6 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 176 | 173 |
if stanza:
|
| 177 | 174 |
try:
|
| 178 | 175 |
node = nbxmpp.Protocol(node=stanza)
|
| 179 |
- if node.getNamespace() == UNDECLARED:
|
|
| 180 |
- node.setNamespace(nbxmpp.NS_CLIENT)
|
|
| 181 | 176 |
except Exception as error:
|
| 182 | 177 |
ErrorDialog(_('Invalid Node'), str(error))
|
| 183 | 178 |
return
|
| ... | ... | @@ -139,8 +139,11 @@ class ChatControlSession: |
| 139 | 139 |
stanza_id=obj.unique_id,
|
| 140 | 140 |
message_id=obj.message_id)
|
| 141 | 141 |
|
| 142 |
- self.conn.get_module('MAM').save_archive_id(
|
|
| 143 |
- None, obj.stanza_id, obj.timestamp)
|
|
| 142 |
+ jid = self.conn.get_own_jid().getStripped()
|
|
| 143 |
+ if self.conn.get_module('MAM').is_catchup_finished(jid):
|
|
| 144 |
+ app.logger.set_archive_infos(jid,
|
|
| 145 |
+ last_mam_id=obj.stanza_id,
|
|
| 146 |
+ last_muc_timestamp=obj.timestamp)
|
|
| 144 | 147 |
|
| 145 | 148 |
if obj.muc_pm and not obj.gc_control:
|
| 146 | 149 |
# This is a carbon of a PM from a MUC we are not currently
|