Commit f386b9ce authored by Martin's avatar Martin

Imported upstream master from 2017-10-22

parent 1f034751
...@@ -665,7 +665,6 @@ class Connection(CommonConnection, ConnectionHandlers): ...@@ -665,7 +665,6 @@ class Connection(CommonConnection, ConnectionHandlers):
self.annotations = {} self.annotations = {}
self.last_io = app.idlequeue.current_time() self.last_io = app.idlequeue.current_time()
self.last_sent = [] self.last_sent = []
self.last_history_time = {}
self.password = passwords.get_password(name) self.password = passwords.get_password(name)
self.music_track_info = 0 self.music_track_info = 0
...@@ -2576,20 +2575,11 @@ class Connection(CommonConnection, ConnectionHandlers): ...@@ -2576,20 +2575,11 @@ class Connection(CommonConnection, ConnectionHandlers):
# Never join a room when invisible # Never join a room when invisible
return return
# last date/time in history to avoid duplicate # Check time first in the FAST table
if room_jid not in self.last_history_time: last_date = app.logger.get_room_last_message_time(
# Not in memory, get it from DB self.name, room_jid)
last_log = 0 if not last_date:
if app.config.should_log(self.name, room_jid): last_date = 0
# Check time first in the FAST table
last_log = app.logger.get_room_last_message_time(
self.name, room_jid)
if not last_log:
last_log = 0
# Create self.last_history_time[room_jid] even if not logging,
# could be used in connection_handlers
self.last_history_time[room_jid] = last_log
p = nbxmpp.Presence(to='%s/%s' % (room_jid, nick), p = nbxmpp.Presence(to='%s/%s' % (room_jid, nick),
show=show, status=self.status) show=show, status=self.status)
...@@ -2608,7 +2598,6 @@ class Connection(CommonConnection, ConnectionHandlers): ...@@ -2608,7 +2598,6 @@ class Connection(CommonConnection, ConnectionHandlers):
'muc_restore_timeout') 'muc_restore_timeout')
if timeout is None or timeout == -2: if timeout is None or timeout == -2:
timeout = app.config.get('muc_restore_timeout') timeout = app.config.get('muc_restore_timeout')
last_date = self.last_history_time[room_jid]
if last_date == 0 and timeout >= 0: if last_date == 0 and timeout >= 0:
last_date = time.time() - timeout * 60 last_date = time.time() - timeout * 60
elif not rejoin and timeout >= 0: elif not rejoin and timeout >= 0:
...@@ -2736,16 +2725,6 @@ class Connection(CommonConnection, ConnectionHandlers): ...@@ -2736,16 +2725,6 @@ class Connection(CommonConnection, ConnectionHandlers):
# disconnect from jabber server # disconnect from jabber server
self.connection.send(p) self.connection.send(p)
def gc_got_disconnected(self, room_jid):
"""
A groupchat got disconnected. This can be or purpose or not
Save the time we had last message to avoid duplicate logs AND be faster
than get that date from DB. Save time that we have in mem in a small
table (with fast access)
"""
app.logger.set_room_last_message_time(room_jid, self.last_history_time[room_jid])
def gc_set_role(self, room_jid, nick, role, reason=''): def gc_set_role(self, room_jid, nick, role, reason=''):
""" """
Role is for all the life of the room so it's based on nick Role is for all the life of the room so it's based on nick
......
...@@ -294,7 +294,8 @@ class ConnectionVcard: ...@@ -294,7 +294,8 @@ class ConnectionVcard:
# Remove avatar # Remove avatar
app.log('avatar').info('Remove: %s', obj.jid) app.log('avatar').info('Remove: %s', obj.jid)
app.contacts.set_avatar(self.name, obj.jid, None) app.contacts.set_avatar(self.name, obj.jid, None)
app.logger.set_avatar_sha(self.name, obj.jid, None) own_jid = self.get_own_jid().getStripped()
app.logger.set_avatar_sha(own_jid, obj.jid, None)
app.interface.update_avatar(self.name, obj.jid) app.interface.update_avatar(self.name, obj.jid)
else: else:
app.log('avatar').info( app.log('avatar').info(
...@@ -313,6 +314,9 @@ class ConnectionVcard: ...@@ -313,6 +314,9 @@ class ConnectionVcard:
if server.startswith('irc') or obj.avatar_sha is None: if server.startswith('irc') or obj.avatar_sha is None:
return return
if obj.show == 'offline':
return
gc_contact = app.contacts.get_gc_contact( gc_contact = app.contacts.get_gc_contact(
self.name, obj.room_jid, obj.nick) self.name, obj.room_jid, obj.nick)
...@@ -1014,9 +1018,8 @@ class ConnectionHandlersBase: ...@@ -1014,9 +1018,8 @@ class ConnectionHandlersBase:
def _nec_gc_message_received(self, obj): def _nec_gc_message_received(self, obj):
if obj.conn.name != self.name: if obj.conn.name != self.name:
return return
if app.config.should_log(obj.conn.name, obj.jid) and not \ if (app.config.should_log(obj.conn.name, obj.jid) and
obj.timestamp < obj.conn.last_history_time[obj.jid] and obj.msgtxt and \ obj.msgtxt and obj.nick):
obj.nick:
# if not obj.nick, it means message comes from room itself # if not obj.nick, it means message comes from room itself
# usually it hold description and can be send at each connection # usually it hold description and can be send at each connection
# so don't store it in logs # so don't store it in logs
...@@ -1026,10 +1029,7 @@ class ConnectionHandlersBase: ...@@ -1026,10 +1029,7 @@ class ConnectionHandlersBase:
message=obj.msgtxt, message=obj.msgtxt,
contact_name=obj.nick, contact_name=obj.nick,
additional_data=obj.additional_data) additional_data=obj.additional_data)
# store in memory time of last message logged. app.logger.set_room_last_message_time(obj.room_jid, obj.timestamp)
# this will also be saved in rooms_last_message_time table
# when we quit this muc
obj.conn.last_history_time[obj.jid] = obj.timestamp
# process and dispatch an error message # process and dispatch an error message
def dispatch_error_message(self, msg, msgtxt, session, frm, tim): def dispatch_error_message(self, msg, msgtxt, session, frm, tim):
......
...@@ -1564,9 +1564,6 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent): ...@@ -1564,9 +1564,6 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
# Ignore message from room in which we are not # Ignore message from room in which we are not
self.displaymarking = seclabel.getTag('displaymarking') self.displaymarking = seclabel.getTag('displaymarking')
if self.jid not in self.conn.last_history_time:
return
self.captcha_form = None self.captcha_form = None
captcha_tag = self.stanza.getTag('captcha', namespace=nbxmpp.NS_CAPTCHA) captcha_tag = self.stanza.getTag('captcha', namespace=nbxmpp.NS_CAPTCHA)
if captcha_tag: if captcha_tag:
......
...@@ -107,7 +107,7 @@ class SubscriptionConstant(IntEnum): ...@@ -107,7 +107,7 @@ class SubscriptionConstant(IntEnum):
class Logger: class Logger:
def __init__(self): def __init__(self):
self.jids_already_in = [] # holds jids that we already have in DB self._jid_ids = {}
self.con = None self.con = None
self.commit_timout_id = None self.commit_timout_id = None
...@@ -188,7 +188,7 @@ class Logger: ...@@ -188,7 +188,7 @@ class Logger:
def init_vars(self): def init_vars(self):
self.open_db() self.open_db()
self.get_jids_already_in_db() self.get_jid_ids_from_db()
@staticmethod @staticmethod
def _get_timeout(): def _get_timeout():
...@@ -226,22 +226,17 @@ class Logger: ...@@ -226,22 +226,17 @@ class Logger:
self.cur.execute(sql_to_commit) self.cur.execute(sql_to_commit)
self._timeout_commit() self._timeout_commit()
def get_jids_already_in_db(self): def get_jid_ids_from_db(self):
try: """
self.cur.execute('SELECT jid FROM jids') Load all jid/jid_id tuples into a dict for faster access
rows = self.cur.fetchall() """
except sqlite.DatabaseError: rows = self.con.execute(
raise exceptions.DatabaseMalformed(LOG_DB_PATH) 'SELECT jid_id, jid, type FROM jids').fetchall()
self.jids_already_in = []
for row in rows: for row in rows:
if not row.jid: self._jid_ids[row.jid] = row
# malformed jid, ignore line
pass
else:
self.jids_already_in.append(row.jid)
def get_jids_in_db(self): def get_jids_in_db(self):
return self.jids_already_in return self._jid_ids.keys()
def jid_is_from_pm(self, jid): def jid_is_from_pm(self, jid):
""" """
...@@ -288,42 +283,41 @@ class Logger: ...@@ -288,42 +283,41 @@ class Logger:
return [user['jid'] for user in family] return [user['jid'] for user in family]
return [jid] return [jid]
def get_jid_id(self, jid, typestr=None): def get_jid_id(self, jid, kind=None, type_=None):
""" """
jids table has jid and jid_id logs table has log_id, jid_id, Get the jid id from a jid.
contact_name, time, kind, show, message so to ask logs we need jid_id In case the jid id is not found create a new one.
that matches our jid in jids table this method wants jid and returns the
jid_id for later sql-ing on logs typestr can be 'ROOM' or anything else :param jid: The JID
depending on the type of JID and is only needed to be specified when the
JID is new in DB :param type_: The JIDConstant type
"""
if jid.find('/') != -1: # if it has a / return the jid id
jid_is_from_pm = self.jid_is_from_pm(jid) """
if not jid_is_from_pm: # it's normal jid with resource
jid = jid.split('/', 1)[0] # remove the resource if kind in (KindConstant.GC_MSG, KindConstant.GCSTATUS):
if jid in self.jids_already_in: # we already have jids in DB type_ = JIDConstant.ROOM_TYPE
self.cur.execute('SELECT jid_id FROM jids WHERE jid=?', [jid])
row = self.cur.fetchone() result = self._jid_ids.get(jid, None)
if row: if result is not None:
return row.jid_id return result.jid_id
# oh! a new jid :), we add it now
if typestr == 'ROOM': sql = 'SELECT jid_id, jid, type FROM jids WHERE jid = ?'
typ = JIDConstant.ROOM_TYPE row = self.con.execute(sql, [jid]).fetchone()
else: if row is not None:
typ = JIDConstant.NORMAL_TYPE self._jid_ids[jid] = row
try: return row.jid_id
self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)', (jid,
typ)) if type_ is None:
self.con.commit() raise ValueError(
except sqlite.IntegrityError: 'Unable to insert new JID because type is missing')
# Jid already in DB, maybe added by another instance. re-read DB
self.get_jids_already_in_db() sql = 'INSERT INTO jids (jid, type) VALUES (?, ?)'
return self.get_jid_id(jid, typestr) lastrowid = self.con.execute(sql, (jid, type_)).lastrowid
except sqlite.OperationalError as e: Row = namedtuple('Row', 'jid_id jid type')
raise exceptions.PysqliteOperationalError(str(e)) self._jid_ids[jid] = Row(lastrowid, jid, type_)
jid_id = self.cur.lastrowid self._timeout_commit()
self.jids_already_in.append(jid) return lastrowid
return jid_id
def convert_kind_values_to_db_api_values(self, kind): def convert_kind_values_to_db_api_values(self, kind):
""" """
...@@ -758,13 +752,16 @@ class Logger: ...@@ -758,13 +752,16 @@ class Logger:
:param jid: The jid :param jid: The jid
:param timestamp: The timestamp in epoch :param timestamp: The timestamp in epoch
""" """
self.insert_jid(jid, type_=JIDConstant.ROOM_TYPE)
jid_id = self.get_jid_id(jid, type_=JIDConstant.ROOM_TYPE)
sql = '''REPLACE INTO rooms_last_message_time sql = '''REPLACE INTO rooms_last_message_time
VALUES ((SELECT jid_id FROM jids WHERE jid = ?), ?)''' VALUES (:jid_id, COALESCE(
(SELECT time FROM rooms_last_message_time
WHERE jid_id = :jid_id AND time >= :time), :time))'''
self.con.execute(sql, (jid, timestamp)) self.con.execute(sql, {"jid_id": jid_id, "time": timestamp})
self._timeout_commit() self._timeout_commit()
def save_transport_type(self, jid, type_): def save_transport_type(self, jid, type_):
...@@ -961,7 +958,7 @@ class Logger: ...@@ -961,7 +958,7 @@ class Logger:
try: try:
account_jid_id = self.get_jid_id(account_jid) account_jid_id = self.get_jid_id(account_jid)
jid_id = self.get_jid_id(jid) jid_id = self.get_jid_id(jid, type_=JIDConstant.NORMAL_TYPE)
except exceptions.PysqliteOperationalError as e: except exceptions.PysqliteOperationalError as e:
raise exceptions.PysqliteOperationalError(str(e)) raise exceptions.PysqliteOperationalError(str(e))
...@@ -993,7 +990,7 @@ class Logger: ...@@ -993,7 +990,7 @@ class Logger:
Return the accound_jid roster in NonBlockingRoster format Return the accound_jid roster in NonBlockingRoster format
""" """
data = {} data = {}
account_jid_id = self.get_jid_id(account_jid) account_jid_id = self.get_jid_id(account_jid, type_=JIDConstant.NORMAL_TYPE)
# First we fill data with roster_entry informations # First we fill data with roster_entry informations
self.cur.execute(''' self.cur.execute('''
...@@ -1085,6 +1082,7 @@ class Logger: ...@@ -1085,6 +1082,7 @@ class Logger:
def insert_jid(self, jid, kind=None, type_=JIDConstant.NORMAL_TYPE): def insert_jid(self, jid, kind=None, type_=JIDConstant.NORMAL_TYPE):
""" """
Insert a new jid into the `jids` table. Insert a new jid into the `jids` table.
This is an alias of get_jid_id() for better readablility.
:param jid: The jid as string :param jid: The jid as string
...@@ -1092,14 +1090,7 @@ class Logger: ...@@ -1092,14 +1090,7 @@ class Logger:
:param type_: A JIDConstant :param type_: A JIDConstant
""" """
if jid in self.jids_already_in: return self.get_jid_id(jid, kind, type_)
return
if kind in (KindConstant.GC_MSG, KindConstant.GCSTATUS):
type_ = JIDConstant.ROOM_TYPE
sql = 'INSERT OR IGNORE INTO jids (jid, type) VALUES (?, ?)'
self.con.execute(sql, (jid, type_))
self.jids_already_in.append(jid)
self._timeout_commit()
def insert_into_logs(self, jid, time_, kind, unread=True, **kwargs): def insert_into_logs(self, jid, time_, kind, unread=True, **kwargs):
""" """
...@@ -1117,15 +1108,15 @@ class Logger: ...@@ -1117,15 +1108,15 @@ class Logger:
:param kwargs: Every additional named argument must correspond to :param kwargs: Every additional named argument must correspond to
a field in the `logs` table a field in the `logs` table
""" """
self.insert_jid(jid, kind=kind) jid_id = self.get_jid_id(jid, kind=kind)
sql = ''' sql = '''
INSERT INTO logs (jid_id, time, kind, {columns}) INSERT INTO logs (jid_id, time, kind, {columns})
VALUES ((SELECT jid_id FROM jids WHERE jid = ?), ?, ?, {values}) VALUES (?, ?, ?, {values})
'''.format(columns=', '.join(kwargs.keys()), '''.format(columns=', '.join(kwargs.keys()),
values=', '.join('?' * len(kwargs))) values=', '.join('?' * len(kwargs)))
lastrowid = self.con.execute(sql, (jid, time_, kind) + tuple(kwargs.values())).lastrowid lastrowid = self.con.execute(sql, (jid_id, time_, kind) + tuple(kwargs.values())).lastrowid
if unread and kind == KindConstant.CHAT_MSG_RECV: if unread and kind == KindConstant.CHAT_MSG_RECV:
sql = '''INSERT INTO unread_messages (message_id, jid_id) sql = '''INSERT INTO unread_messages (message_id, jid_id)
...@@ -1149,7 +1140,7 @@ class Logger: ...@@ -1149,7 +1140,7 @@ class Logger:
""" """
account_jid_id = self.get_jid_id(account_jid) account_jid_id = self.get_jid_id(account_jid)
jid_id = self.get_jid_id(jid) jid_id = self.get_jid_id(jid, type_=JIDConstant.NORMAL_TYPE)
sql = ''' sql = '''
UPDATE roster_entry SET avatar_sha = ? UPDATE roster_entry SET avatar_sha = ?
......
...@@ -1499,8 +1499,6 @@ class GroupchatControl(ChatControlBase): ...@@ -1499,8 +1499,6 @@ class GroupchatControl(ChatControlBase):
app.contacts.remove_gc_contact(self.account, gc_contact) app.contacts.remove_gc_contact(self.account, gc_contact)
app.gc_connected[self.account][self.room_jid] = False app.gc_connected[self.account][self.room_jid] = False
ChatControlBase.got_disconnected(self) ChatControlBase.got_disconnected(self)
# Tell connection to note the date we disconnect to avoid duplicate logs
app.connections[self.account].gc_got_disconnected(self.room_jid)
# We don't redraw the whole banner here, because only icon change # We don't redraw the whole banner here, because only icon change
self._update_banner_state_image() self._update_banner_state_image()
if self.parent_win: if self.parent_win:
...@@ -2141,10 +2139,6 @@ class GroupchatControl(ChatControlBase): ...@@ -2141,10 +2139,6 @@ class GroupchatControl(ChatControlBase):
if self.room_jid in app.gc_connected[self.account] and \ if self.room_jid in app.gc_connected[self.account] and \
app.gc_connected[self.account][self.room_jid]: app.gc_connected[self.account][self.room_jid]:
# Tell connection to note the date we disconnect to avoid duplicate
# logs. We do it only when connected because if connection was lost
# there may be new messages since disconnection.
app.connections[self.account].gc_got_disconnected(self.room_jid)
app.connections[self.account].send_gc_status(self.nick, app.connections[self.account].send_gc_status(self.nick,
self.room_jid, show='offline', status=status) self.room_jid, show='offline', status=status)
nick_list = app.contacts.get_nick_list(self.account, self.room_jid) nick_list = app.contacts.get_nick_list(self.account, self.room_jid)
......
...@@ -2399,7 +2399,7 @@ class Interface: ...@@ -2399,7 +2399,7 @@ class Interface:
'avatar image using pillow', filename) 'avatar image using pillow', filename)
try: try:
avatar = Image.open(path).convert("RGBA") avatar = Image.open(path).convert("RGBA")
except NameError: except (NameError, OSError):
app.log('avatar').warning('Pillow convert failed: %s', filename) app.log('avatar').warning('Pillow convert failed: %s', filename)
app.log('avatar').debug('Error', exc_info=True) app.log('avatar').debug('Error', exc_info=True)
return return
......
...@@ -253,7 +253,7 @@ class ProfileWindow: ...@@ -253,7 +253,7 @@ class ProfileWindow:
else: else:
if i == 'DESC': if i == 'DESC':
self.xml.get_object('DESC_textview').get_buffer().set_text( self.xml.get_object('DESC_textview').get_buffer().set_text(
vcard_[i], len(vcard[i].encode('utf-8'))) vcard_[i], len(vcard_[i].encode('utf-8')))
else: else:
self.set_value(i + '_entry', vcard_[i]) self.set_value(i + '_entry', vcard_[i])
if self.update_progressbar_timeout_id is not None: if self.update_progressbar_timeout_id is not None:
......
...@@ -73,6 +73,7 @@ function install_deps { ...@@ -73,6 +73,7 @@ function install_deps {
mingw-w64-"${ARCH}"-"${PYTHON_ID}"-pip \ mingw-w64-"${ARCH}"-"${PYTHON_ID}"-pip \
mingw-w64-"${ARCH}"-gstreamer \ mingw-w64-"${ARCH}"-gstreamer \
mingw-w64-"${ARCH}"-adwaita-icon-theme \ mingw-w64-"${ARCH}"-adwaita-icon-theme \
mingw-w64-"${ARCH}"-libwebp \
mingw-w64-"${ARCH}"-sqlite3 \ mingw-w64-"${ARCH}"-sqlite3 \
mingw-w64-"${ARCH}"-goocanvas mingw-w64-"${ARCH}"-goocanvas
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment