9578053 Jan 22 2022 distfiles.gentoo.org/distfiles/gajim-1.3.3-2.tar.gz
This commit is contained in:
parent
a5b3822651
commit
4c1b226bff
1045 changed files with 753037 additions and 18 deletions
370
gajim/gtk/server_info.py
Normal file
370
gajim/gtk/server_info.py
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
# 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 nbxmpp.errors import StanzaError
|
||||
from nbxmpp.namespaces import Namespace
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Pango
|
||||
|
||||
from gajim.common import app
|
||||
from gajim.common import ged
|
||||
from gajim.common.helpers import open_uri
|
||||
from gajim.common.i18n import _
|
||||
|
||||
from .util import get_builder
|
||||
from .util import EventHelper
|
||||
from .util import open_window
|
||||
|
||||
log = logging.getLogger('gajim.gui.server_info')
|
||||
|
||||
|
||||
class ServerInfo(Gtk.ApplicationWindow, EventHelper):
|
||||
def __init__(self, account):
|
||||
Gtk.ApplicationWindow.__init__(self)
|
||||
EventHelper.__init__(self)
|
||||
self.set_name('ServerInfo')
|
||||
self.set_application(app.app)
|
||||
self.set_position(Gtk.WindowPosition.CENTER)
|
||||
self.set_default_size(400, 600)
|
||||
self.set_show_menubar(False)
|
||||
self.set_title(_('Server Info'))
|
||||
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
|
||||
|
||||
self.account = account
|
||||
self._destroyed = False
|
||||
|
||||
self._ui = get_builder('server_info.ui')
|
||||
self.add(self._ui.server_info_notebook)
|
||||
|
||||
self.connect('destroy', self.on_destroy)
|
||||
self.connect('key-press-event', self._on_key_press)
|
||||
self._ui.connect_signals(self)
|
||||
|
||||
self.register_events([
|
||||
('server-disco-received', ged.GUI1, self._server_disco_received),
|
||||
])
|
||||
|
||||
self.version = ''
|
||||
self.hostname = app.get_hostname_from_account(account)
|
||||
self._ui.server_hostname.set_text(self.hostname)
|
||||
con = app.connections[account]
|
||||
con.get_module('SoftwareVersion').request_software_version(
|
||||
self.hostname, callback=self._software_version_received)
|
||||
self.request_last_activity()
|
||||
|
||||
server_info = con.get_module('Discovery').server_info
|
||||
self._add_contact_addresses(server_info.dataforms)
|
||||
|
||||
self.cert = con.certificate
|
||||
self._add_connection_info()
|
||||
|
||||
self.feature_listbox = Gtk.ListBox()
|
||||
self.feature_listbox.set_name('ServerInfo')
|
||||
self.feature_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||
self._ui.features_scrolled.add(self.feature_listbox)
|
||||
for feature in self.get_features():
|
||||
self.add_feature(feature)
|
||||
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
|
||||
self.show_all()
|
||||
|
||||
def _on_key_press(self, _widget, event):
|
||||
if event.keyval == Gdk.KEY_Escape:
|
||||
self.destroy()
|
||||
|
||||
def _add_connection_info(self):
|
||||
# Connection type
|
||||
nbxmpp_client = app.connections[self.account].connection
|
||||
address = nbxmpp_client.current_address
|
||||
|
||||
self._ui.connection_type.set_text(address.type.value)
|
||||
if address.type.is_plain:
|
||||
self._ui.connection_type.get_style_context().add_class(
|
||||
'error-color')
|
||||
|
||||
# Connection proxy
|
||||
proxy = address.proxy
|
||||
if proxy is not None:
|
||||
self._ui.proxy_type.set_text(proxy.type)
|
||||
self._ui.proxy_host.set_text(proxy.host)
|
||||
|
||||
self._ui.cert_button.set_sensitive(self.cert)
|
||||
|
||||
self._ui.domain.set_text(address.domain)
|
||||
|
||||
visible = address.service is not None
|
||||
self._ui.dns_label.set_visible(visible)
|
||||
self._ui.dns.set_visible(visible)
|
||||
self._ui.dns.set_text(address.service or '')
|
||||
|
||||
visible = nbxmpp_client.remote_address is not None
|
||||
self._ui.ip_port_label.set_visible(visible)
|
||||
self._ui.ip_port.set_visible(visible)
|
||||
self._ui.ip_port.set_text(nbxmpp_client.remote_address or '')
|
||||
|
||||
visible = address.uri is not None
|
||||
self._ui.websocket_label.set_visible(visible)
|
||||
self._ui.websocket.set_visible(visible)
|
||||
self._ui.websocket.set_text(address.uri or '')
|
||||
|
||||
def _on_cert_button_clicked(self, _button):
|
||||
open_window('CertificateDialog',
|
||||
account=self.account,
|
||||
transient_for=self,
|
||||
cert=self.cert)
|
||||
|
||||
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=Namespace.LAST)
|
||||
con.connection.SendAndCallForResponse(iq, self._on_last_activity)
|
||||
|
||||
def _add_contact_addresses(self, dataforms):
|
||||
fields = {
|
||||
'admin-addresses': _('Admin'),
|
||||
'status-addresses': _('Status'),
|
||||
'support-addresses': _('Support'),
|
||||
'security-addresses': _('Security'),
|
||||
'feedback-addresses': _('Feedback'),
|
||||
'abuse-addresses': _('Abuse'),
|
||||
'sales-addresses': _('Sales'),
|
||||
}
|
||||
|
||||
addresses = self._get_addresses(fields, dataforms)
|
||||
if addresses is None:
|
||||
self._ui.no_addresses_label.set_visible(True)
|
||||
return
|
||||
|
||||
row_count = 4
|
||||
for address_type, values in addresses.items():
|
||||
label = self._get_address_type_label(fields[address_type])
|
||||
self._ui.server.attach(label, 0, row_count, 1, 1)
|
||||
for index, value in enumerate(values):
|
||||
last = index == len(values) - 1
|
||||
label = self._get_address_label(value, last=last)
|
||||
self._ui.server.attach(label, 1, row_count, 1, 1)
|
||||
row_count += 1
|
||||
|
||||
@staticmethod
|
||||
def _get_addresses(fields, dataforms):
|
||||
addresses = {}
|
||||
for form in dataforms:
|
||||
field = form.vars.get('FORM_TYPE')
|
||||
if field.value != 'http://jabber.org/network/serverinfo':
|
||||
continue
|
||||
|
||||
for address_type in fields:
|
||||
field = form.vars.get(address_type)
|
||||
if field is None:
|
||||
continue
|
||||
|
||||
if field.type_ != 'list-multi':
|
||||
continue
|
||||
|
||||
if not field.values:
|
||||
continue
|
||||
addresses[address_type] = field.values
|
||||
|
||||
return addresses or None
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_address_type_label(text):
|
||||
label = Gtk.Label(label=text)
|
||||
label.set_halign(Gtk.Align.END)
|
||||
label.set_valign(Gtk.Align.START)
|
||||
label.get_style_context().add_class('dim-label')
|
||||
return label
|
||||
|
||||
def _get_address_label(self, address, last=False):
|
||||
label = Gtk.Label()
|
||||
label.set_markup('<a href="%s">%s</a>' % (address, address))
|
||||
label.set_ellipsize(Pango.EllipsizeMode.END)
|
||||
label.set_xalign(0)
|
||||
label.set_halign(Gtk.Align.START)
|
||||
label.get_style_context().add_class('link-button')
|
||||
label.connect('activate-link', self._on_activate_link)
|
||||
if last:
|
||||
label.set_margin_bottom(6)
|
||||
return label
|
||||
|
||||
def _on_activate_link(self, label, *args):
|
||||
open_uri(label.get_text(), account=self.account)
|
||||
return Gdk.EVENT_STOP
|
||||
|
||||
def _on_last_activity(self, _nbxmpp_client, stanza):
|
||||
if self._destroyed:
|
||||
# Window got closed in the meantime
|
||||
return
|
||||
if not nbxmpp.isResultNode(stanza):
|
||||
log.warning('Received malformed result: %s', stanza)
|
||||
return
|
||||
if stanza.getQueryNS() != Namespace.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
|
||||
uptime = _('%(days)s days, %(hours)s hours') % {
|
||||
'days': delta.days, 'hours': hours}
|
||||
self._ui.server_uptime.set_text(uptime)
|
||||
|
||||
def _software_version_received(self, task):
|
||||
try:
|
||||
result = task.finish()
|
||||
except StanzaError:
|
||||
self.version = _('Unknown')
|
||||
else:
|
||||
self.version = '%s %s' % (result.name, result.version)
|
||||
|
||||
self._ui.server_software.set_text(self.version)
|
||||
|
||||
@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 _server_disco_received(self, _event):
|
||||
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 or '')
|
||||
|
||||
def get_features(self):
|
||||
con = app.connections[self.account]
|
||||
Feature = namedtuple('Feature',
|
||||
['name', 'available', 'tooltip', 'enabled'])
|
||||
Feature.__new__.__defaults__ = (None, None) # type: ignore
|
||||
|
||||
# HTTP File Upload
|
||||
http_upload_info = con.get_module('HTTPUpload').httpupload_namespace
|
||||
if con.get_module('HTTPUpload').available:
|
||||
max_file_size = con.get_module('HTTPUpload').max_file_size
|
||||
if max_file_size is not None:
|
||||
max_file_size = max_file_size / (1024 * 1024)
|
||||
http_upload_info = http_upload_info + ' (max. %s MiB)' % \
|
||||
max_file_size
|
||||
|
||||
return [
|
||||
Feature('XEP-0045: Multi-User Chat',
|
||||
con.get_module('MUC').supported),
|
||||
Feature('XEP-0054: vcard-temp',
|
||||
con.get_module('VCardTemp').supported),
|
||||
Feature('XEP-0077: In-Band Registration',
|
||||
con.get_module('Register').supported),
|
||||
Feature('XEP-0163: Personal Eventing Protocol',
|
||||
con.get_module('PEP').supported),
|
||||
Feature('XEP-0163: #publish-options',
|
||||
con.get_module('PubSub').publish_options),
|
||||
Feature('XEP-0191: Blocking Command',
|
||||
con.get_module('Blocking').supported,
|
||||
Namespace.BLOCKING),
|
||||
Feature('XEP-0198: Stream Management',
|
||||
con.features.has_sm, Namespace.STREAM_MGMT),
|
||||
Feature('XEP-0258: Security Labels in XMPP',
|
||||
con.get_module('SecLabels').supported,
|
||||
Namespace.SECLABEL),
|
||||
Feature('XEP-0280: Message Carbons',
|
||||
con.get_module('Carbons').supported,
|
||||
Namespace.CARBONS),
|
||||
Feature('XEP-0313: Message Archive Management',
|
||||
con.get_module('MAM').available),
|
||||
Feature('XEP-0363: HTTP File Upload',
|
||||
con.get_module('HTTPUpload').available,
|
||||
http_upload_info),
|
||||
Feature('XEP-0398: Avatar Conversion',
|
||||
con.get_module('VCardAvatars').avatar_conversion_available),
|
||||
Feature('XEP-0411: Bookmarks Conversion',
|
||||
con.get_module('Bookmarks').conversion),
|
||||
Feature('XEP-0402: Bookmarks Compat',
|
||||
con.get_module('Bookmarks').compat),
|
||||
Feature('XEP-0402: Bookmarks Compat PEP',
|
||||
con.get_module('Bookmarks').compat_pep)
|
||||
]
|
||||
|
||||
def _on_clipboard_button_clicked(self, _widget):
|
||||
server_software = 'Server Software: %s\n' % self.version
|
||||
server_features = ''
|
||||
|
||||
for feature in self.get_features():
|
||||
if feature.available:
|
||||
available = 'Yes'
|
||||
else:
|
||||
available = 'No'
|
||||
if feature.tooltip is not None:
|
||||
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_destroy(self, *args):
|
||||
self._destroyed = True
|
||||
|
||||
|
||||
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 preferences')
|
||||
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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue