Commit d4ae74bf authored by Martin's avatar Martin

Merge tag 'upstream/0.16.11.git20171109' into debian/experimental

parents e6e51378 0aba4647
......@@ -440,6 +440,7 @@ class GenericOptionPage(Gtk.Box):
'account_label', value or self.account)
if app.config.get_per('accounts', self.account, 'active'):
app.interface.roster.draw_account(self.account)
gui_menu_builder.build_accounts_menu()
def update(self):
self.set_entry_text(self.toggle, update=True)
......
......@@ -170,7 +170,7 @@ class CommonConnection:
self.roster_supported = True
self.blocking_supported = False
self.addressing_supported = False
self.carbons_enabled = False
self.carbons_available = False
self.muc_jid = {} # jid of muc server for each transport type
self._stun_servers = [] # STUN servers of our jabber server
......@@ -1942,13 +1942,14 @@ class Connection(CommonConnection, ConnectionHandlers):
self.blocking_supported = True
if nbxmpp.NS_ADDRESS in obj.features:
self.addressing_supported = True
if nbxmpp.NS_CARBONS in obj.features and app.config.get_per(
'accounts', self.name, 'enable_message_carbons'):
self.carbons_enabled = True
# Server supports carbons, activate it
iq = nbxmpp.Iq('set')
iq.setTag('enable', namespace=nbxmpp.NS_CARBONS)
self.connection.send(iq)
if nbxmpp.NS_CARBONS in obj.features:
self.carbons_available = True
if app.config.get_per('accounts', self.name,
'enable_message_carbons'):
# Server supports carbons, activate it
iq = nbxmpp.Iq('set')
iq.setTag('enable', namespace=nbxmpp.NS_CARBONS)
self.connection.send(iq)
if nbxmpp.NS_PRIVACY in obj.features:
self.privacy_rules_supported = True
get_action(self.name + '-privacylists').set_enabled(True)
......
......@@ -292,7 +292,7 @@ class ConnectionVcard:
'%s has no avatar published (vCard)', obj.jid)
# Remove avatar
app.log('avatar').info('Remove: %s', obj.jid)
app.log('avatar').debug('Remove: %s', obj.jid)
app.contacts.set_avatar(self.name, obj.jid, None)
own_jid = self.get_own_jid().getStripped()
app.logger.set_avatar_sha(own_jid, obj.jid, None)
......@@ -328,7 +328,7 @@ class ConnectionVcard:
# Empty <photo/> tag, means no avatar is advertised, remove avatar
app.log('avatar').info(
'%s has no avatar published (vCard)', obj.nick)
app.log('avatar').info('Remove: %s', obj.nick)
app.log('avatar').debug('Remove: %s', obj.nick)
gc_contact.avatar_sha = None
app.interface.update_avatar(contact=gc_contact)
else:
......@@ -470,8 +470,19 @@ class ConnectionVcard:
resource = frm_jid.getResource()
jid = frm_jid.getStripped()
vcard = self._node_to_dict(stanza.getChildren()[0])
# handle no vcard set
stanza_error = stanza.getError()
if stanza_error in ('service-unavailable', 'item-not-found',
'not-allowed'):
app.log('avatar').info('vCard not available: %s %s',
frm_jid, stanza_error)
return
vcard_node = stanza.getTag('vCard', namespace=nbxmpp.NS_VCARD)
if vcard_node is None:
app.log('avatar').info('vCard not available: %s', frm_jid)
app.log('avatar').debug(stanza)
return
vcard = self._node_to_dict(vcard_node)
if self.get_own_jid().bareMatch(jid):
if 'NICKNAME' in vcard:
......@@ -725,8 +736,6 @@ class ConnectionHandlersBase:
# IDs of sent messages (https://trac.gajim.org/ticket/8222)
self.sent_message_ids = []
self.received_message_hashes = []
# We decrypt GPG messages one after the other. Keep queue in mem
self.gpg_messages_to_decrypt = []
......@@ -946,7 +955,8 @@ class ConnectionHandlersBase:
def _on_message_received(self, obj):
if isinstance(obj, MessageReceivedEvent):
app.nec.push_incoming_event(
DecryptedMessageReceivedEvent(None, conn=self, msg_obj=obj))
DecryptedMessageReceivedEvent(
None, conn=self, msg_obj=obj, stanza_id=obj.unique_id))
else:
app.nec.push_incoming_event(
MamDecryptedMessageReceivedEvent(None, **vars(obj)))
......@@ -1012,14 +1022,14 @@ class ConnectionHandlersBase:
return True
elif obj.mtype == 'groupchat':
app.nec.push_incoming_event(GcMessageReceivedEvent(None,
conn=self, msg_obj=obj))
conn=self, msg_obj=obj, stanza_id=obj.unique_id))
return True
def _nec_gc_message_received(self, obj):
if obj.conn.name != self.name:
return
if (app.config.should_log(obj.conn.name, obj.jid) and
obj.msgtxt and obj.nick):
obj.msgtxt and obj.nick):
# if not obj.nick, it means message comes from room itself
# usually it hold description and can be send at each connection
# so don't store it in logs
......@@ -1028,7 +1038,8 @@ class ConnectionHandlersBase:
KindConstant.GC_MSG,
message=obj.msgtxt,
contact_name=obj.nick,
additional_data=obj.additional_data)
additional_data=obj.additional_data,
stanza_id=obj.unique_id)
app.logger.set_room_last_message_time(obj.room_jid, obj.timestamp)
# process and dispatch an error message
......@@ -2048,8 +2059,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
app.nec.push_incoming_event(SignedInEvent(None, conn=self))
self.send_awaiting_pep()
self.continue_connect_info = None
# hashes of already received messages
self.received_message_hashes = []
def _SearchCB(self, con, iq_obj):
log.debug('SearchCB')
......
......@@ -139,6 +139,28 @@ class HelperEvent:
if oob_desc is not None:
self.additional_data['gajim']['oob_desc'] = oob_desc
def get_stanza_id(self, stanza, query=False):
if query:
# On a MAM query the stanza-id is maybe not set, so
# get the id of the stanza
return stanza.getAttr('id')
stanza_id, by = stanza.getStanzaIDAttrs()
if by is None:
# We can not verify who set this stanza-id, ignore it.
return
elif not self.conn.get_own_jid().bareMatch(by):
# by attribute does not match the server, ignore it.
return
return stanza_id
@staticmethod
def get_forwarded_message(stanza):
forwarded = stanza.getTag('forwarded',
namespace=nbxmpp.NS_FORWARD,
protocol=True)
if forwarded is not None:
return forwarded.getTag('message', protocol=True)
class HttpAuthReceivedEvent(nec.NetworkIncomingEvent):
name = 'http-auth-received'
base_network_events = []
......@@ -1026,6 +1048,7 @@ class MamMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
:stanza: Complete stanza Node
:forwarded: Forwarded Node
:result: Result Node
:unique_id: The unique stable id
'''
self._set_base_event_vars_as_attributes(base_event)
self.additional_data = {}
......@@ -1046,6 +1069,13 @@ class MamMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
log.info('Received groupchat message from user archive')
return False
# use stanza-id as unique-id
self.unique_id, origin_id = self.get_unique_id()
# Check for duplicates
if app.logger.find_stanza_id(self.unique_id, origin_id):
return
self.msgtxt = self.msg_.getTagData('body')
self.query_id = self.result.getAttr('queryid')
......@@ -1057,24 +1087,12 @@ class MamMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
to = own_jid
if frm.bareMatch(own_jid):
self.stanza_id = self.msg_.getOriginID()
if self.stanza_id is None:
self.stanza_id = self.msg_.getID()
self.with_ = to
self.kind = KindConstant.CHAT_MSG_SENT
else:
if self.result.getNamespace() == nbxmpp.NS_MAM_2:
self.stanza_id = self.result.getID()
else:
self.stanza_id = self.msg_.getID()
self.with_ = frm
self.kind = KindConstant.CHAT_MSG_RECV
if self.stanza_id is None:
log.debug('Could not retrieve stanza-id')
delay = self.forwarded.getTagAttr(
'delay', 'stamp', namespace=nbxmpp.NS_DELAY2)
if delay is None:
......@@ -1097,9 +1115,31 @@ class MamMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
log.warning('Received MAM message with '
'invalid user timestamp: %s', user_delay)
log.debug('Received mam-message: stanza id: %s', self.stanza_id)
log.debug('Received mam-message: unique id: %s', self.unique_id)
return True
def get_unique_id(self):
if self.conn.get_own_jid().bareMatch(self.msg_.getFrom()):
# On our own Messages we have to check for both
# stanza-id and origin-id, because other resources
# maybe not support origin-id
stanza_id = None
if self.result.getNamespace() == nbxmpp.NS_MAM_2:
# Only mam:2 ensures valid stanza-id
stanza_id = self.get_stanza_id(self.result, query=True)
# try always to get origin-id because its a message
# we sent.
origin_id = self.msg_.getOriginID()
return stanza_id, origin_id
# A message we received
elif self.result.getNamespace() == nbxmpp.NS_MAM_2:
# Only mam:2 ensures valid stanza-id
return self.get_stanza_id(self.result, query=True), None
return None, None
class MamDecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'mam-decrypted-message-received'
base_network_events = []
......@@ -1177,6 +1217,14 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
self.stanza.getFrom())
return
# Check for duplicates
self.unique_id = self.get_unique_id()
# Check groupchat messages for duplicates,
# We do this because of MUC History messages
if self.stanza.getType() == 'groupchat':
if app.logger.find_stanza_id(self.unique_id):
return
address_tag = self.stanza.getTag('addresses',
namespace=nbxmpp.NS_ADDRESS)
# Be sure it comes from one of our resource, else ignore address element
......@@ -1248,7 +1296,8 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
conn=self.conn,
stanza=self.stanza,
forwarded=forwarded,
result=result))
result=result,
stanza_id=self.unique_id))
return
# Mediated invitation?
......@@ -1320,6 +1369,38 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
return True
def get_unique_id(self):
if self.stanza.getType() == 'groupchat':
# TODO: Disco the MUC check if 'urn:xmpp:mam:2' is announced
return self.get_stanza_id(self.stanza)
elif self.stanza.getType() != 'chat':
return
# Messages we receive live
if self.conn.archiving_namespace != nbxmpp.NS_MAM_2:
# Only mam:2 ensures valid stanza-id
return
# Sent Carbon
sent_carbon = self.stanza.getTag('sent',
namespace=nbxmpp.NS_CARBONS,
protocol=True)
if sent_carbon is not None:
message = self.get_forwarded_message(sent_carbon)
return self.get_stanza_id(message)
# Received Carbon
received_carbon = self.stanza.getTag('received',
namespace=nbxmpp.NS_CARBONS,
protocol=True)
if received_carbon is not None:
message = self.get_forwarded_message(received_carbon)
return self.get_stanza_id(message)
# Normal Message
return self.get_stanza_id(self.stanza)
class ZeroconfMessageReceivedEvent(MessageReceivedEvent):
name = 'message-received'
base_network_events = []
......@@ -1416,6 +1497,7 @@ class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
self.stanza = self.msg_obj.stanza
self.additional_data = self.msg_obj.additional_data
self.id_ = self.msg_obj.id_
self.unique_id = self.msg_obj.unique_id
self.jid = self.msg_obj.jid
self.fjid = self.msg_obj.fjid
self.resource = self.msg_obj.resource
......@@ -1472,22 +1554,6 @@ class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
if replace:
self.correct_id = replace.getAttr('id')
# ignore message duplicates
if self.msgtxt and self.id_ and self.jid:
self.msghash = hashlib.sha256(("%s|%s|%s" % (
hashlib.sha256(self.msgtxt.encode('utf-8')).hexdigest(),
hashlib.sha256(self.id_.encode('utf-8')).hexdigest(),
hashlib.sha256(self.jid.encode('utf-8')).hexdigest())).encode(
'utf-8')).digest()
if self.msghash in self.conn.received_message_hashes:
log.info("Ignoring duplicated message from '%s' with id '%s'" % (self.jid, self.id_))
return False
else:
log.debug("subhashes: msgtxt, id_, jid = ('%s', '%s', '%s')" % (hashlib.sha256(self.msgtxt.encode('utf-8')).hexdigest(), hashlib.sha256(self.id_.encode('utf-8')).hexdigest(), hashlib.sha256(self.jid.encode('utf-8')).hexdigest()))
self.conn.received_message_hashes.append(self.msghash)
# only record the last 20000 hashes (should be about 1MB [32 bytes per hash]
# and about 24 hours if you receive a message every 5 seconds)
self.conn.received_message_hashes = self.conn.received_message_hashes[-20000:]
return True
class ChatstateReceivedEvent(nec.NetworkIncomingEvent):
......@@ -1513,6 +1579,7 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
else:
self.additional_data = self.msg_obj.additional_data
self.id_ = self.msg_obj.stanza.getID()
self.unique_id = self.msg_obj.unique_id
self.fjid = self.msg_obj.fjid
self.msgtxt = self.msg_obj.msgtxt
self.jid = self.msg_obj.jid
......
......@@ -95,8 +95,8 @@ class GlobalEventsDispatcher(object):
except NodeProcessed:
node_processed = True
except Exception:
log.error('Error while running an even handler: %s' % \
handler)
log.error('Error while running an event handler: %s',
handler)
traceback.print_exc()
if node_processed:
raise NodeProcessed
......@@ -1064,9 +1064,9 @@ class Logger:
:param msg: The message text
"""
# Add 5 minutes around the timestamp
start_time = timestamp - 300
end_time = timestamp + 300
# Add 10 seconds around the timestamp
start_time = timestamp - 10
end_time = timestamp + 10
log.debug('start: %s, end: %s, jid: %s, message: %s',
start_time, end_time, jid, msg)
......@@ -1084,6 +1084,36 @@ class Logger:
return True
return False
def find_stanza_id(self, stanza_id, origin_id=None):
"""
Checks if a stanza-id is already in the `logs` table
:param stanza_id: The stanza-id
return True if the stanza-id was found
"""
ids = []
if stanza_id is not None:
ids.append(stanza_id)
if origin_id is not None:
ids.append(origin_id)
if not ids:
return False
sql = '''
SELECT stanza_id FROM logs
WHERE stanza_id IN ({values}) LIMIT 1
'''.format(values=', '.join('?' * len(ids)))
result = self.con.execute(sql, tuple(ids)).fetchone()
if result is not None:
log.info('Found duplicated message, stanza-id: %s, origin-id: %s',
stanza_id, origin_id)
return True
return False
def insert_jid(self, jid, kind=None, type_=JIDConstant.NORMAL_TYPE):
"""
Insert a new jid into the `jids` table.
......@@ -1129,6 +1159,9 @@ class Logger:
lastrowid = self.con.execute(sql, (jid_id, time_, kind) + tuple(kwargs.values())).lastrowid
log.info('Insert into DB: jid: %s, time: %s, kind: %s, stanza_id: %s',
jid, time_, kind, kwargs.get('stanza_id', None))
if unread and kind == KindConstant.CHAT_MSG_RECV:
sql = '''INSERT INTO unread_messages (message_id, jid_id)
VALUES (?, (SELECT jid_id FROM jids WHERE jid = ?))'''
......
......@@ -127,14 +127,18 @@ class ConnectionArchive313:
def _nec_mam_decrypted_message_received(self, obj):
if obj.conn.name != self.name:
return
# if self.archiving_namespace != nbxmpp.NS_MAM_2:
# Fallback duplicate search without stanza-id
duplicate = app.logger.search_for_duplicate(
obj.with_, obj.timestamp, obj.msgtxt)
if not duplicate:
app.logger.insert_into_logs(
obj.with_, obj.timestamp, obj.kind,
unread=False,
message=obj.msgtxt,
additional_data=obj.additional_data)
if duplicate:
return
app.logger.insert_into_logs(
obj.with_, obj.timestamp, obj.kind,
unread=False,
message=obj.msgtxt,
additional_data=obj.additional_data,
stanza_id=obj.unique_id)
def get_query_id(self):
self.mam_query_id = self.connection.getAnID()
......
......@@ -58,6 +58,7 @@ class Color:
GREEN = Gdk.RGBA(red=115/255, green=210/255, blue=22/255, alpha=1)
RED = Gdk.RGBA(red=204/255, green=0, blue=0, alpha=1)
GREY = Gdk.RGBA(red=195/255, green=195/255, blue=192/255, alpha=1)
ORANGE = Gdk.RGBA(red=245/255, green=121/255, blue=0/255, alpha=1)
def get_icon_pixmap(icon_name, size=16, color=None, quiet=False):
try:
......
......@@ -780,8 +780,9 @@ def build_accounts_menu():
return
if len(accounts_list) > 1:
for acc in accounts_list:
label = app.config.get_per('accounts', acc, 'account_label')
acc_menu.append_submenu(
acc, get_account_menu(acc))
label or acc, get_account_menu(acc))
else:
acc_menu = get_account_menu(accounts_list[0])
menubar.remove(menu_position)
......@@ -805,7 +806,9 @@ def build_bookmark_menu(account):
if acc_menu.get_item_link(0, 'submenu'):
for i in range(acc_menu.get_n_items()):
label = acc_menu.get_item_attribute_value(i, 'label')
if label.get_string() == account:
account_label = app.config.get_per('accounts', account,
'account_label')
if label.get_string() in (account_label, account):
menu = acc_menu.get_item_link(i, 'submenu')
else:
# We have only one Account active
......
......@@ -5116,7 +5116,9 @@ class RosterWindow:
accounts.append(account)
accounts.sort()
for account in accounts:
item = Gtk.MenuItem.new_with_label(account)
label = app.config.get_per('accounts', account,
'account_label')
item = Gtk.MenuItem.new_with_label(label or account)
account_menu = self.build_account_menu(account)
item.set_submenu(account_menu)
menu.append(item)
......
......@@ -18,14 +18,19 @@
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from collections import namedtuple
from datetime import timedelta
import logging
import nbxmpp
from gi.repository import Gtk
import nbxmpp
from gajim.common import app
from gajim.common import ged
from gajim.gtkgui_helpers import get_icon_pixmap, Color
log = logging.getLogger('gajim.serverinfo')
class ServerInfoDialog(Gtk.Dialog):
def __init__(self, account):
flags = Gtk.DialogFlags.DESTROY_WITH_PARENT
......@@ -59,16 +64,18 @@ class ServerInfoDialog(Gtk.Dialog):
self.connect('destroy', self.on_destroy)
app.ged.register_event_handler('version-result-received',
ged.CORE,
self._nec_version_result_received)
ged.CORE,
self._nec_version_result_received)
app.ged.register_event_handler('agent-info-received',
ged.GUI1,
self._nec_agent_info_received)
ged.GUI1,
self._nec_agent_info_received)
self.version = ''
self.uptime = ''
self.hostname = app.get_hostname_from_account(account)
app.connections[account].request_os_info(self.hostname, None)
self.request_last_activity()
for feature in self.get_features():
self.add_feature(feature)
......@@ -92,7 +99,36 @@ class ServerInfoDialog(Gtk.Dialog):
for index, item in enumerate(func()):
row = listbox.get_row_at_index(index)
row.get_child().update(item)
row.set_tooltip_text(item.tooltip)
row.set_tooltip_text(row.get_child().tooltip)
def request_last_activity(self):
if not app.account_is_connected(self.account):
return
con = app.connections[self.account]
iq = nbxmpp.Iq(to=self.hostname, typ='get', queryNS=nbxmpp.NS_LAST)
con.connection.SendAndCallForResponse(iq, self._on_last_activity)
def _on_last_activity(self, stanza):
if 'server_info' not in app.interface.instances[self.account]:
# Window got closed in the meantime
return
if not nbxmpp.isResultNode(stanza):
log.warning('Received malformed result: %s', stanza)
return
if stanza.getQueryNS() != nbxmpp.NS_LAST:
log.warning('Wrong namespace on result: %s', stanza)
return
try:
seconds = int(stanza.getQuery().getAttr('seconds'))
except (ValueError, TypeError, AttributeError):
log.exception('Received malformed last activity result')
else:
delta = timedelta(seconds=seconds)
hours = 0
if seconds >= 3600:
hours = delta.seconds // 3600
self.uptime = _('%s days, %s hours') % (delta.days, hours)
self.update(self.get_infos, self.info_listbox)
def _nec_version_result_received(self, obj):
if obj.jid != self.hostname:
......@@ -101,30 +137,45 @@ class ServerInfoDialog(Gtk.Dialog):
self.update(self.get_infos, self.info_listbox)
def _nec_agent_info_received(self, obj):
if not 'Gajim_' in obj.id_:
if 'Gajim_' not in obj.id_:
return
self.update(self.get_features, self.feature_listbox)
def add_feature(self, feature):
item = FeatureItem(feature)
self.feature_listbox.add(item)
item.get_parent().set_tooltip_text(feature.tooltip)
item.get_parent().set_tooltip_text(item.tooltip)
def get_features(self):
con = app.connections[self.account]
Feature = namedtuple('Feature', ['name', 'enabled', 'tooltip'])
Feature = namedtuple('Feature',
['name', 'available', 'tooltip', 'enabled'])
carbons_enabled = app.config.get_per('accounts', self.account,
'enable_message_carbons')
mam_enabled = app.config.get_per('accounts', self.account,
'sync_logs_with_server')
return [
Feature('XEP-0016: Privacy Lists', con.privacy_rules_supported, None),
Feature('XEP-0045: Multi-User Chat', con.muc_jid, None),
Feature('XEP-0054: vcard-temp', con.vcard_supported, None),
Feature('XEP-0163: Personal Eventing Protocol', con.pep_supported, None),
Feature('XEP-0163: #publish-options', con.pubsub_publish_options_supported, None),
Feature('XEP-0191: Blocking Command', con.blocking_supported, nbxmpp.NS_BLOCKING),
Feature('XEP-0198: Stream Management', con.sm.enabled, nbxmpp.NS_STREAM_MGMT),
Feature('XEP-0280: Message Carbons', con.carbons_enabled, nbxmpp.NS_CARBONS),
Feature('XEP-0313: Message Archive Management', con.archiving_namespace, con.archiving_namespace),
Feature('XEP-0363: HTTP File Upload', con.httpupload, nbxmpp.NS_HTTPUPLOAD)]
Feature('XEP-0016: Privacy Lists',
con.privacy_rules_supported, '', None),
Feature('XEP-0045: Multi-User Chat', con.muc_jid, '', None),
Feature('XEP-0054: vcard-temp', con.vcard_supported, '', None),
Feature('XEP-0163: Personal Eventing Protocol',
con.pep_supported, '', None),
Feature('XEP-0163: #publish-options',
con.pubsub_publish_options_supported, '', None),
Feature('XEP-0191: Blocking Command',
con.blocking_supported, nbxmpp.NS_BLOCKING, None),
Feature('XEP-0198: Stream Management',
con.sm.enabled, nbxmpp.NS_STREAM_MGMT, None),
Feature('XEP-0280: Message Carbons',
con.carbons_available, nbxmpp.NS_CARBONS, carbons_enabled),
Feature('XEP-0313: Message Archive Management',
con.archiving_namespace, con.archiving_namespace,
mam_enabled),
Feature('XEP-0363: HTTP File Upload',
con.httpupload, nbxmpp.NS_HTTPUPLOAD, None)]
def add_info(self, info):
self.info_listbox.add(ServerInfoItem(info))
......@@ -133,7 +184,8 @@ class ServerInfoDialog(Gtk.Dialog):
Info = namedtuple('Info', ['name', 'value', 'tooltip'])
return [
Info(_('Hostname'), self.hostname, None),
Info(_('Server Software'), self.version, None)]
Info(_('Server Software'), self.version, None),
Info(_('Server Uptime'), self.uptime, None)]
def on_response(self, dialog, response):
if response == Gtk.ResponseType.OK:
......@@ -142,41 +194,49 @@ class ServerInfoDialog(Gtk.Dialog):
def on_destroy(self, *args):
del app.interface.instances[self.account]['server_info']
app.ged.remove_event_handler('version-result-received',
ged.CORE,
self._nec_version_result_received)
ged.CORE,
self._nec_version_result_received)
app.ged.remove_event_handler('agent-info-received',
ged.GUI1,
self._nec_agent_info_received)
ged.GUI1,
self._nec_agent_info_received)
class FeatureItem(Gtk.Grid):
def __init__(self, feature):
super().__init__()
self.tooltip = feature.tooltip
self.set_column_spacing(6)
self.icon = Gtk.Image()
self.feature_label = Gtk.Label(label=feature.name)
self.set_feature_enabled(bool(feature.enabled))
self.set_feature(feature.available, feature.enabled)
self.add(self.icon)
self.add(self.feature_label)
def set_feature_enabled(self, enabled):
if enabled:
def set_feature(self, available, enabled):
if not available:
self.icon.set_from_pixbuf(
get_icon_pixmap('emblem-ok-symbolic', color=[Color.GREEN]))
get_icon_pixmap('window-close-symbolic', color=[Color.RED]))
elif enabled is False:
self.icon.set_from_pixbuf(
get_icon_pixmap('dialog-warning-symbolic',
color=[Color.ORANGE]))
self.tooltip += _('\nDisabled in config')
else:
self.icon.set_from_pixbuf(
get_icon_pixmap('window-close-symbolic', color=[Color.RED]))
get_icon_pixmap('emblem-ok-symbolic', color=[Color.GREEN]))
def update(self, feature):
self.set_feature_enabled(bool(feature.enabled))
self.tooltip = feature.tooltip
self.set_feature(feature.available, feature.enabled)
class ServerInfoItem(Gtk.Grid):
def __init__(self, info):
super().__init__()
self.tooltip = info.tooltip
self.set_hexpand(True)
self.insert_column(0)
self.set_column_homogeneous(True)
......@@ -187,6 +247,7 @@ class ServerInfoItem(Gtk.Grid):
self.value = Gtk.Label(label=info.value)
self.value.set_halign(Gtk.Align.START)
self.value.set_hexpand(True)
self.value.set_selectable(True)
self.add(self.info)
self.add(self.value)
......
......@@ -120,7 +120,8 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
jid, obj.timestamp, log_type,
message=msg_to_log,
subject=obj.subject,
additional_data=obj.additional_data)
additional_data=obj.additional_data,
stanza_id=obj.unique_id)