gajim-plural/gajim/gtk/server_info.py

297 lines
11 KiB
Python

# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import logging
from collections import namedtuple
from datetime import timedelta
import nbxmpp
from gi.repository import Gtk
from gi.repository import Gdk
from gajim.common import app
from gajim.common import ged
from gajim.common.i18n import _
log = logging.getLogger('gajim.gtk.serverinfo')
class ServerInfoDialog(Gtk.Dialog):
def __init__(self, account):
flags = Gtk.DialogFlags.DESTROY_WITH_PARENT
super().__init__(_('Server Info'), None, flags)
self.account = account
self.set_transient_for(app.interface.roster.window)
self.set_resizable(False)
grid = Gtk.Grid()
grid.set_name('ServerInfoGrid')
grid.set_row_spacing(10)
grid.set_hexpand(True)
self.info_listbox = Gtk.ListBox()
self.info_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.info_listbox.set_header_func(self.header_func, 'Information')
grid.attach(self.info_listbox, 0, 0, 1, 1)
self.feature_listbox = Gtk.ListBox()
self.feature_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.feature_listbox.set_header_func(self.header_func, 'Features')
grid.attach(self.feature_listbox, 0, 1, 1, 1)
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard_button = Gtk.Button(halign=Gtk.Align.END)
clp_image = Gtk.Image.new_from_icon_name('edit-copy-symbolic',
Gtk.IconSize.BUTTON)
clipboard_button.set_image(clp_image)
clipboard_button.set_tooltip_text(_('Copy info to clipboard'))
clipboard_button.connect('clicked', self.on_clipboard_button_clicked)
box = self.get_content_area()
box.pack_start(grid, True, True, 0)
box.pack_start(clipboard_button, False, True, 0)
box.set_property('margin', 12)
box.set_spacing(18)
self.connect('response', self.on_response)
self.connect('destroy', self.on_destroy)
app.ged.register_event_handler('version-result-received',
ged.CORE,
self._nec_version_result_received)
app.ged.register_event_handler('server-disco-received',
ged.GUI1,
self._server_disco_received)
self.version = ''
self.uptime = ''
self.hostname = app.get_hostname_from_account(account)
con = app.connections[account]
con.get_module('SoftwareVersion').request_os_info(self.hostname, None)
self.request_last_activity()
for feature in self.get_features():
self.add_feature(feature)
for info in self.get_infos():
self.add_info(info)
self.show_all()
@staticmethod
def header_func(row, before, user_data):
if before:
row.set_header(None)
else:
label = Gtk.Label(label=user_data)
label.set_halign(Gtk.Align.START)
row.set_header(label)
@staticmethod
def update(func, listbox):
for index, item in enumerate(func()):
row = listbox.get_row_at_index(index)
row.get_child().update(item)
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 = _('%(days)s days, %(hours)s hours') % {
'days': delta.days, 'hours': hours}
self.update(self.get_infos, self.info_listbox)
def _nec_version_result_received(self, obj):
if obj.jid != self.hostname:
return
self.version = obj.client_info or _('Unknown')
self.update(self.get_infos, self.info_listbox)
def _server_disco_received(self, obj):
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(item.tooltip)
def get_features(self):
con = app.connections[self.account]
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.get_module('PrivacyLists').supported, '', None),
Feature('XEP-0045: Multi-User Chat', con.muc_jid, '', None),
Feature('XEP-0054: vcard-temp',
con.get_module('VCardTemp').supported, '', None),
Feature('XEP-0163: Personal Eventing Protocol',
con.get_module('PEP').supported, '', None),
Feature('XEP-0163: #publish-options',
con.get_module('PubSub').publish_options, '', None),
Feature('XEP-0191: Blocking Command',
con.get_module('Blocking').supported,
nbxmpp.NS_BLOCKING, None),
Feature('XEP-0198: Stream Management',
con.sm.enabled, nbxmpp.NS_STREAM_MGMT, None),
Feature('XEP-0258: Security Labels in XMPP',
con.get_module('SecLabels').supported,
nbxmpp.NS_SECLABEL, None),
Feature('XEP-0280: Message Carbons',
con.get_module('Carbons').supported,
nbxmpp.NS_CARBONS, carbons_enabled),
Feature('XEP-0313: Message Archive Management',
con.get_module('MAM').archiving_namespace,
con.get_module('MAM').archiving_namespace,
mam_enabled),
Feature('XEP-0363: HTTP File Upload',
con.get_module('HTTPUpload').available,
con.get_module('HTTPUpload').httpupload_namespace, None),
Feature('XEP-0398: Avatar Conversion',
con.avatar_conversion, '', None)
]
def add_info(self, info):
self.info_listbox.add(ServerInfoItem(info))
def get_infos(self):
Info = namedtuple('Info', ['name', 'value', 'tooltip'])
return [
Info(_('Hostname'), self.hostname, None),
Info(_('Server Software'), self.version, None),
Info(_('Server Uptime'), self.uptime, None)]
def on_clipboard_button_clicked(self, widget):
server_software = 'Server Software: %s\n' % self.get_infos()[1].value
server_features = ''
for feature in self.get_features():
if feature.available:
available = 'Yes'
else:
available = 'No'
if feature.tooltip != '':
tooltip = '(%s)' % feature.tooltip
else:
tooltip = ''
server_features += '%s: %s %s\n' % (feature.name, available, tooltip)
clipboard_text = server_software + server_features
self.clipboard.set_text(clipboard_text, -1)
def on_response(self, dialog, response):
if response == Gtk.ResponseType.OK:
self.destroy()
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)
app.ged.remove_event_handler('server-disco-received',
ged.GUI1,
self._server_disco_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(feature.available, feature.enabled)
self.add(self.icon)
self.add(self.feature_label)
def set_feature(self, available, enabled):
self.icon.get_style_context().remove_class('error-color')
self.icon.get_style_context().remove_class('warning-color')
self.icon.get_style_context().remove_class('success-color')
if not available:
self.icon.set_from_icon_name('window-close-symbolic',
Gtk.IconSize.MENU)
self.icon.get_style_context().add_class('error-color')
elif enabled is False:
self.icon.set_from_icon_name('dialog-warning-symbolic',
Gtk.IconSize.MENU)
self.tooltip += _('\nDisabled in config')
self.icon.get_style_context().add_class('warning-color')
else:
self.icon.set_from_icon_name('emblem-ok-symbolic',
Gtk.IconSize.MENU)
self.icon.get_style_context().add_class('success-color')
def update(self, feature):
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)
self.info = Gtk.Label(label=info.name)
self.info.set_halign(Gtk.Align.START)
self.info.set_hexpand(True)
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)
def update(self, info):
self.value.set_text(info.value)