gajim-plural/gajim/gtk/accounts.py

1116 lines
40 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 sys
import locale
import logging
from collections import defaultdict
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Pango
from gi.repository import GObject
from gajim.common import app
from gajim.common import passwords
from gajim.common import helpers
from gajim.common.i18n import _
from gajim.common.connection import Connection
from gajim.common.zeroconf.connection_zeroconf import ConnectionZeroconf
from gajim import gui_menu_builder
from gajim.gtk.settings import SettingsDialog
from gajim.gtk.settings import SettingsBox
from gajim.gtk.dialogs import ConfirmationDialog
from gajim.gtk.dialogs import ConfirmationDialogDoubleRadio
from gajim.gtk.dialogs import ErrorDialog
from gajim.gtk.dialogs import PassphraseDialog
from gajim.gtk.dialogs import YesNoDialog
from gajim.gtk.dialogs import DialogButton
from gajim.gtk.dialogs import NewConfirmationDialog
from gajim.gtk.util import get_builder
from gajim.gtk.const import Setting
from gajim.gtk.const import SettingKind
from gajim.gtk.const import SettingType
log = logging.getLogger('gajim.gtk.accounts')
class AccountsWindow(Gtk.ApplicationWindow):
def __init__(self):
Gtk.ApplicationWindow.__init__(self)
self.set_application(app.app)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_show_menubar(False)
self.set_name('AccountsWindow')
self.set_default_size(700, 550)
self.set_resizable(True)
self.set_title(_('Accounts'))
self._need_relogin = {}
self._accounts = {}
self._menu = AccountMenu()
self._settings = Settings()
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
box.add(self._menu)
box.add(self._settings)
self.add(box)
for account in app.get_accounts_sorted():
self.add_account(account, inital=True)
self._menu.connect('menu-activated', self._on_menu_activated)
self.connect('destroy', self._on_destroy)
self.connect('key-press-event', self._on_key_press)
self.show_all()
def _on_menu_activated(self, _listbox, account, name):
if name == 'back':
self._settings.set_page('add-account')
self._check_relogin()
elif name == 'remove':
self.on_remove_account(account)
else:
self._settings.set_page(name)
def _on_key_press(self, _widget, event):
if event.keyval == Gdk.KEY_Escape:
self.destroy()
def _on_destroy(self, *args):
self._check_relogin()
def update_account_label(self, account):
self._accounts[account].update_account_label()
def update_proxy_list(self):
for account in self._accounts:
self._settings.update_proxy_list(account)
def _check_relogin(self):
for account in self._need_relogin:
settings = self._get_relogin_settings(account)
active = app.config.get_per('accounts', account, 'active')
if settings != self._need_relogin[account]:
self._need_relogin[account] = settings
if active:
self._relog(account)
break
def _relog(self, account):
if app.connections[account].connected == 0:
return
if account == app.ZEROCONF_ACC_NAME:
app.connections[app.ZEROCONF_ACC_NAME].update_details()
return
def login(account, show_before, status_before):
"""
Login with previous status
"""
# first make sure connection is really closed,
# 0.5 may not be enough
app.connections[account].disconnect(True)
app.interface.roster.send_status(
account, show_before, status_before)
def relog(account):
show_before = app.SHOW_LIST[app.connections[account].connected]
status_before = app.connections[account].status
app.interface.roster.send_status(
account, 'offline', _('Be right back.'))
GLib.timeout_add(500, login, account, show_before, status_before)
YesNoDialog(
_('Relogin now?'),
_('If you want all the changes to apply instantly, '
'you must relogin.'),
transient_for=self,
on_response_yes=lambda *args: relog(account))
@staticmethod
def _get_relogin_settings(account):
if account == app.ZEROCONF_ACC_NAME:
settings = ['zeroconf_first_name', 'zeroconf_last_name',
'zeroconf_jabber_id', 'zeroconf_email']
else:
settings = ['client_cert', 'proxy', 'resource',
'use_custom_host', 'custom_host', 'custom_port']
values = []
for setting in settings:
values.append(app.config.get_per('accounts', account, setting))
return values
def on_remove_account(self, account):
if app.events.get_events(account):
app.interface.raise_dialog('unread-events-on-remove-account')
return
if app.config.get_per('accounts', account, 'is_zeroconf'):
# Should never happen as button is insensitive
return
win_opened = False
if app.interface.msg_win_mgr.get_controls(acct=account):
win_opened = True
elif account in app.interface.instances:
for key in app.interface.instances[account]:
if (app.interface.instances[account][key] and
key != 'remove_account'):
win_opened = True
break
# Detect if we have opened windows for this account
def remove(account):
if (account in app.interface.instances and
'remove_account' in app.interface.instances[account]):
dialog = app.interface.instances[account]['remove_account']
dialog.window.present()
else:
if account not in app.interface.instances:
app.interface.instances[account] = {}
app.interface.instances[account]['remove_account'] = \
RemoveAccountWindow(account)
if win_opened:
ConfirmationDialog(
_('You have opened chat in account %s') % account,
_('All chat and groupchat windows will be closed. '
'Do you want to continue?'),
on_response_ok=(remove, account),
transient_for=self)
else:
remove(account)
def remove_account(self, account):
del self._need_relogin[account]
self._accounts[account].remove()
def add_account(self, account, inital=False):
self._need_relogin[account] = self._get_relogin_settings(account)
self._accounts[account] = Account(account, self._menu, self._settings)
if not inital:
self._accounts[account].show()
def select_account(self, account):
try:
self._accounts[account].select()
except KeyError:
log.warning('select_account() failed, account %s not found',
account)
@staticmethod
def enable_account(account):
if account == app.ZEROCONF_ACC_NAME:
app.connections[account] = ConnectionZeroconf(account)
else:
app.connections[account] = Connection(account)
app.plugin_manager.register_modules_for_account(
app.connections[account])
# update variables
app.interface.instances[account] = {
'infos': {}, 'disco': {}, 'gc_config': {}, 'search': {},
'online_dialog': {}, 'sub_request': {}}
app.interface.minimized_controls[account] = {}
app.connections[account].connected = 0
app.groups[account] = {}
app.contacts.add_account(account)
app.gc_connected[account] = {}
app.automatic_rooms[account] = {}
app.newly_added[account] = []
app.to_be_removed[account] = []
if account == app.ZEROCONF_ACC_NAME:
app.nicks[account] = app.ZEROCONF_ACC_NAME
else:
app.nicks[account] = app.config.get_per(
'accounts', account, 'name')
app.block_signed_in_notifications[account] = True
app.sleeper_state[account] = 'off'
app.last_message_time[account] = {}
app.status_before_autoaway[account] = ''
app.gajim_optional_features[account] = []
app.caps_hash[account] = ''
helpers.update_optional_features(account)
# refresh roster
if len(app.connections) >= 2:
# Do not merge accounts if only one exists
app.interface.roster.regroup = app.config.get('mergeaccounts')
else:
app.interface.roster.regroup = False
app.interface.roster.setup_and_draw_roster()
gui_menu_builder.build_accounts_menu()
@staticmethod
def disable_account(account):
app.interface.roster.close_all(account)
if account == app.ZEROCONF_ACC_NAME:
app.connections[account].disable_account()
app.connections[account].cleanup()
del app.connections[account]
del app.interface.instances[account]
del app.interface.minimized_controls[account]
del app.nicks[account]
del app.block_signed_in_notifications[account]
del app.groups[account]
app.contacts.remove_account(account)
del app.gc_connected[account]
del app.automatic_rooms[account]
del app.to_be_removed[account]
del app.newly_added[account]
del app.sleeper_state[account]
del app.last_message_time[account]
del app.status_before_autoaway[account]
del app.gajim_optional_features[account]
del app.caps_hash[account]
if len(app.connections) >= 2:
# Do not merge accounts if only one exists
app.interface.roster.regroup = app.config.get('mergeaccounts')
else:
app.interface.roster.regroup = False
app.config.set_per(
'accounts', account, 'roster_version', '')
app.interface.roster.setup_and_draw_roster()
gui_menu_builder.build_accounts_menu()
class Settings(Gtk.ScrolledWindow):
def __init__(self):
Gtk.ScrolledWindow.__init__(self)
self.set_hexpand(True)
self.set_vexpand(True)
self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self._stack = Gtk.Stack()
self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
self._stack.add_named(AddNewAccountPage(), 'add-account')
self.get_style_context().add_class('accounts-settings')
self.add(self._stack)
self._page_signal_ids = {}
self._pages = defaultdict(list)
def add_page(self, page):
self._pages[page.account].append(page)
self._stack.add_named(page, '%s-%s' % (page.account, page.name))
self._page_signal_ids[page] = page.connect_signal(self._stack)
def set_page(self, name):
self._stack.set_visible_child_name(name)
def remove_account(self, account):
for page in self._pages[account]:
signal_id = self._page_signal_ids[page]
del self._page_signal_ids[page]
self._stack.disconnect(signal_id)
self._stack.remove(page)
page.destroy()
del self._pages[account]
def update_proxy_list(self, account):
for page in self._pages[account]:
if page.name != 'connection':
continue
page.listbox.get_setting('proxy').update_values()
class AccountMenu(Gtk.Box):
__gsignals__ = {
'menu-activated': (GObject.SignalFlags.RUN_FIRST, None, (str, str)),
}
def __init__(self):
Gtk.Box.__init__(self)
self.set_hexpand(False)
self.set_size_request(160, -1)
self.get_style_context().add_class('accounts-menu')
self._stack = Gtk.Stack()
self._stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT)
self._accounts_listbox = Gtk.ListBox()
self._accounts_listbox.set_sort_func(self._sort_func)
self._accounts_listbox.get_style_context().add_class('settings-box')
self._accounts_listbox.connect('row-activated',
self._on_account_row_activated)
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scrolled.add(self._accounts_listbox)
self._stack.add_named(scrolled, 'accounts')
self.add(self._stack)
@staticmethod
def _sort_func(row1, row2):
if row1.label == 'Local':
return -1
return locale.strcoll(row1.label.lower(), row2.label.lower())
def add_account(self, row):
self._accounts_listbox.add(row)
sub_menu = AccountSubMenu(row.account)
self._stack.add_named(sub_menu, '%s-menu' % row.account)
sub_menu.connect('row-activated', self._on_sub_menu_row_activated)
def remove_account(self, row):
if self._stack.get_visible_child_name() != 'accounts':
# activate 'back' button
self._stack.get_visible_child().get_row_at_index(1).emit('activate')
self._accounts_listbox.remove(row)
sub_menu = self._stack.get_child_by_name('%s-menu' % row.account)
self._stack.remove(sub_menu)
row.destroy()
sub_menu.destroy()
def _on_account_row_activated(self, _listbox, row):
self._stack.set_visible_child_name('%s-menu' % row.account)
self._stack.get_visible_child().get_row_at_index(2).emit('activate')
def _on_sub_menu_row_activated(self, listbox, row):
if row.name == 'back':
self._stack.set_visible_child_full(
'accounts', Gtk.StackTransitionType.OVER_RIGHT)
if row.name in ('back', 'remove'):
self.emit('menu-activated', listbox.account, row.name)
else:
self.emit('menu-activated',
listbox.account,
'%s-%s' % (listbox.account, row.name))
def update_account_label(self, account):
self._accounts_listbox.invalidate_sort()
sub_menu = self._stack.get_child_by_name('%s-menu' % account)
sub_menu.update()
class AccountSubMenu(Gtk.ListBox):
__gsignals__ = {
'update': (GObject.SignalFlags.RUN_FIRST, None, (str,))
}
def __init__(self, account):
Gtk.ListBox.__init__(self)
self.set_vexpand(True)
self.set_hexpand(True)
self.get_style_context().add_class('settings-box')
self._account = account
self.add(AccountLabelMenuItem(self, self._account))
self.add(BackMenuItem())
self.add(PageMenuItem('general', _('General')))
if account != 'Local':
self.add(PageMenuItem('privacy', _('Privacy')))
self.add(PageMenuItem('connection', _('Connection')))
self.add(RemoveMenuItem())
@property
def account(self):
return self._account
def update(self):
self.emit('update', self._account)
class MenuItem(Gtk.ListBoxRow):
def __init__(self, name):
Gtk.ListBoxRow.__init__(self)
self._name = name
self._box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
spacing=12)
self._label = Gtk.Label()
self.add(self._box)
@property
def name(self):
return self._name
class RemoveMenuItem(MenuItem):
def __init__(self):
super().__init__('remove')
self._label.set_text(_('Remove'))
image = Gtk.Image.new_from_icon_name('user-trash-symbolic',
Gtk.IconSize.MENU)
self.set_selectable(False)
image.get_style_context().add_class('error-color')
self._box.add(image)
self._box.add(self._label)
class AccountLabelMenuItem(MenuItem):
def __init__(self, parent, account):
super().__init__('account-label')
self._update_account_label(parent, account)
self.set_selectable(False)
self.set_sensitive(False)
self.set_activatable(False)
image = Gtk.Image.new_from_icon_name('avatar-default-symbolic',
Gtk.IconSize.MENU)
image.get_style_context().add_class('insensitive-fg-color')
self._label.get_style_context().add_class('accounts-label-row')
self._label.set_ellipsize(Pango.EllipsizeMode.END)
self._label.set_xalign(0)
self._box.add(image)
self._box.add(self._label)
parent.connect('update', self._update_account_label)
def _update_account_label(self, _listbox, account):
account_label = app.get_account_label(account)
self._label.set_text(account_label)
class BackMenuItem(MenuItem):
def __init__(self):
super().__init__('back')
self.set_selectable(False)
self._label.set_text(_('Back'))
image = Gtk.Image.new_from_icon_name('go-previous-symbolic',
Gtk.IconSize.MENU)
image.get_style_context().add_class('insensitive-fg-color')
self._box.add(image)
self._box.add(self._label)
class PageMenuItem(MenuItem):
def __init__(self, name, label):
super().__init__(name)
if name == 'general':
icon = 'preferences-system-symbolic'
elif name == 'privacy':
icon = 'preferences-system-privacy-symbolic'
elif name == 'connection':
icon = 'preferences-system-network-symbolic'
else:
icon = 'dialog-error-symbolic'
image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.MENU)
self._label.set_text(label)
self._box.add(image)
self._box.add(self._label)
class Account:
def __init__(self, account, menu, settings):
self._account = account
self._menu = menu
self._settings = settings
if account == app.ZEROCONF_ACC_NAME:
self._settings.add_page(ZeroConfPage(account))
else:
self._settings.add_page(GeneralPage(account))
self._settings.add_page(ConnectionPage(account))
self._settings.add_page(PrivacyPage(account))
self._account_row = AccountRow(account)
self._menu.add_account(self._account_row)
def select(self):
self._account_row.emit('activate')
def show(self):
self._menu.show_all()
self._settings.show_all()
self.select()
def remove(self):
self._menu.remove_account(self._account_row)
self._settings.remove_account(self._account)
def update_account_label(self):
self._account_row.update_account_label()
self._menu.update_account_label(self._account)
@property
def menu(self):
return self._menu
@property
def account(self):
return self._account
@property
def settings(self):
return self._account
class AccountRow(Gtk.ListBoxRow):
def __init__(self, account):
Gtk.ListBoxRow.__init__(self)
self.set_selectable(False)
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
self._account = account
self._label = Gtk.Label(label=app.get_account_label(account))
self._label.set_halign(Gtk.Align.START)
self._label.set_hexpand(True)
self._label.set_ellipsize(Pango.EllipsizeMode.END)
self._label.set_xalign(0)
self._label.set_width_chars(18)
next_icon = Gtk.Image.new_from_icon_name('go-next-symbolic',
Gtk.IconSize.MENU)
next_icon.get_style_context().add_class('insensitive-fg-color')
switch = Gtk.Switch()
switch.set_active(
app.config.get_per('accounts', self._account, 'active'))
switch.set_vexpand(False)
if (self._account == app.ZEROCONF_ACC_NAME and
not app.is_installed('ZEROCONF')):
switch.set_active(False)
self.set_activatable(False)
self.set_sensitive(False)
if sys.platform in ('win32', 'darwin'):
tooltip = _('Please check if Bonjour is installed.')
else:
tooltip = _('Please check if Avahi is installed.')
self.set_tooltip_text(tooltip)
switch.connect('state-set', self._on_enable_switch, self._account)
box.add(switch)
box.add(self._label)
box.add(next_icon)
self.add(box)
@property
def account(self):
return self._account
@property
def label(self):
return self._label.get_text()
def update_account_label(self):
self._label.set_text(app.get_account_label(self._account))
def _on_enable_switch(self, switch, state, account):
def _disable():
app.connections[account].change_status('offline', 'offline')
app.connections[account].disconnect(reconnect=False)
self.get_toplevel().disable_account(account)
app.config.set_per('accounts', account, 'active', False)
switch.set_state(state)
old_state = app.config.get_per('accounts', account, 'active')
if old_state == state:
return Gdk.EVENT_PROPAGATE
if (account in app.connections and
app.connections[account].connected > 0):
# Connecting or connected
NewConfirmationDialog(
_('Disable Account'),
_('Account %s is still connected') % account,
_('All chat and group chat windows will be closed. '
'Do you want to continue?'),
[DialogButton.make('Cancel',
callback=lambda: switch.set_active(True)),
DialogButton.make('Remove',
text=_('Disable Account'),
callback=_disable)],
transient_for=self.get_toplevel()).show()
return Gdk.EVENT_STOP
if state:
self.get_toplevel().enable_account(account)
else:
self.get_toplevel().disable_account(account)
app.config.set_per('accounts', account, 'active', state)
return Gdk.EVENT_PROPAGATE
class AddNewAccountPage(Gtk.Box):
def __init__(self):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
self.set_vexpand(True)
self.set_hexpand(True)
button = Gtk.Button(label='Add Account')
button.get_style_context().add_class('suggested-action')
button.set_action_name('app.add-account')
button.set_halign(Gtk.Align.CENTER)
button.set_valign(Gtk.Align.CENTER)
self.add(button)
class GenericSettingPage(Gtk.Box):
def __init__(self, account, settings):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12)
self.set_valign(Gtk.Align.START)
self.set_vexpand(True)
self.account = account
self.listbox = SettingsBox(account)
self.listbox.get_style_context().add_class('accounts-settings-border')
self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.listbox.set_vexpand(False)
self.listbox.set_valign(Gtk.Align.END)
for setting in settings:
self.listbox.add_setting(setting)
self.listbox.update_states()
self.pack_end(self.listbox, True, True, 0)
self.listbox.connect('row-activated', self.on_row_activated)
def connect_signal(self, stack):
return stack.connect('notify::visible-child',
self._on_visible_child_changed)
def _on_visible_child_changed(self, stack, param):
if self == stack.get_visible_child():
self.listbox.update_states()
@staticmethod
def on_row_activated(_listbox, row):
row.on_row_activated()
def set_entry_text(self, toggle, update=False):
account_label = app.get_account_label(self.account)
if update:
self.entry.set_text(account_label)
return
if toggle.get_active():
self.entry.set_sensitive(True)
self.entry.grab_focus()
else:
self.entry.set_sensitive(False)
value = self.entry.get_text()
if not value:
value = account_label
app.config.set_per('accounts', self.account,
'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()
class GeneralPage(GenericSettingPage):
name = 'general'
def __init__(self, account):
settings = [
Setting(SettingKind.ENTRY, _('Label'),
SettingType.ACCOUNT_CONFIG, 'account_label',
callback=self._on_account_name_change),
Setting(SettingKind.LOGIN, _('Login'), SettingType.DIALOG,
props={'dialog': LoginDialog}),
Setting(SettingKind.ACTION, _('Import Contacts'),
SettingType.ACTION, '-import-contacts',
props={'account': account}),
Setting(SettingKind.DIALOG, _('Client Certificate'),
SettingType.DIALOG, props={'dialog': CertificateDialog}),
Setting(SettingKind.SWITCH, _('Connect on startup'),
SettingType.ACCOUNT_CONFIG, 'autoconnect'),
Setting(SettingKind.SWITCH,
_('Save conversations for all contacts'),
SettingType.ACCOUNT_CONFIG, 'no_log_for',
desc=_('Store conversations on the harddrive')),
Setting(SettingKind.SWITCH, _('Global Status'),
SettingType.ACCOUNT_CONFIG, 'sync_with_global_status',
desc=_('Synchronise the status of all accounts')),
Setting(SettingKind.SWITCH, _('Use file transfer proxies'),
SettingType.ACCOUNT_CONFIG, 'use_ft_proxies'),
]
GenericSettingPage.__init__(self, account, settings)
def _on_account_name_change(self, *args):
self.get_toplevel().update_account_label(self.account)
class PrivacyPage(GenericSettingPage):
name = 'privacy'
def __init__(self, account):
settings = [
Setting(SettingKind.SWITCH, _('Idle Time'),
SettingType.ACCOUNT_CONFIG, 'send_idle_time',
desc=_('Disclose the time of your last activity')),
Setting(SettingKind.SWITCH, _('Local System Time'),
SettingType.ACCOUNT_CONFIG, 'send_time_info',
desc=_('Disclose the local system time of the '
'device Gajim runs on')),
Setting(SettingKind.SWITCH, _('Client / Operating System'),
SettingType.ACCOUNT_CONFIG, 'send_os_info',
desc=_('Disclose informations about the client '
'and operating system you currently use')),
Setting(SettingKind.SWITCH, _('Ignore Unknown Contacts'),
SettingType.ACCOUNT_CONFIG, 'ignore_unknown_contacts',
desc=_('Ignore everything from contacts not in your '
'Roster')),
]
GenericSettingPage.__init__(self, account, settings)
class ConnectionPage(GenericSettingPage):
name = 'connection'
def __init__(self, account):
settings = [
Setting(SettingKind.SWITCH, 'HTTP_PROXY',
SettingType.ACCOUNT_CONFIG, 'use_env_http_proxy',
desc=_('Use environment variable')),
Setting(SettingKind.PROXY, _('Proxy'),
SettingType.ACCOUNT_CONFIG, 'proxy', name='proxy'),
Setting(SettingKind.SWITCH, _('Warn on insecure connection'),
SettingType.ACCOUNT_CONFIG,
'warn_when_insecure_ssl_connection'),
Setting(SettingKind.SWITCH, _('Send keep-alive packets'),
SettingType.ACCOUNT_CONFIG, 'keep_alives_enabled'),
Setting(SettingKind.HOSTNAME, _('Hostname'), SettingType.DIALOG,
desc=_('Manually set the hostname for the server'),
props={'dialog': CutstomHostnameDialog}),
Setting(SettingKind.ENTRY, _('Resource'),
SettingType.ACCOUNT_CONFIG, 'resource'),
Setting(SettingKind.PRIORITY, _('Priority'),
SettingType.DIALOG, props={'dialog': PriorityDialog}),
]
GenericSettingPage.__init__(self, account, settings)
class ZeroConfPage(GenericSettingPage):
name = 'general'
def __init__(self, account):
settings = [
Setting(SettingKind.DIALOG, _('Profile'),
SettingType.DIALOG,
props={'dialog': ZeroconfProfileDialog}),
Setting(SettingKind.SWITCH, _('Connect on startup'),
SettingType.ACCOUNT_CONFIG, 'autoconnect',
desc=_('Use environment variable')),
Setting(SettingKind.SWITCH,
_('Save conversations for all contacts'),
SettingType.ACCOUNT_CONFIG, 'no_log_for',
desc=_('Store conversations on the harddrive')),
Setting(SettingKind.SWITCH, _('Global Status'),
SettingType.ACCOUNT_CONFIG, 'sync_with_global_status',
desc=_('Synchronize the status of all accounts')),
]
GenericSettingPage.__init__(self, account, settings)
class ZeroconfProfileDialog(SettingsDialog):
def __init__(self, account, parent):
settings = [
Setting(SettingKind.ENTRY, _('First Name'),
SettingType.ACCOUNT_CONFIG, 'zeroconf_first_name'),
Setting(SettingKind.ENTRY, _('Last Name'),
SettingType.ACCOUNT_CONFIG, 'zeroconf_last_name'),
Setting(SettingKind.ENTRY, _('Jabber ID'),
SettingType.ACCOUNT_CONFIG, 'zeroconf_jabber_id'),
Setting(SettingKind.ENTRY, _('Email'),
SettingType.ACCOUNT_CONFIG, 'zeroconf_email'),
]
SettingsDialog.__init__(self, parent, _('Profile'),
Gtk.DialogFlags.MODAL, settings, account)
class PriorityDialog(SettingsDialog):
def __init__(self, account, parent):
neg_priority = app.config.get('enable_negative_priority')
if neg_priority:
range_ = (-128, 127)
else:
range_ = (0, 127)
settings = [
Setting(SettingKind.SWITCH, _('Adjust to status'),
SettingType.ACCOUNT_CONFIG, 'adjust_priority_with_status',
'adjust'),
Setting(SettingKind.SPIN, _('Priority'),
SettingType.ACCOUNT_CONFIG, 'priority',
enabledif=('adjust', False), props={'range_': range_}),
]
SettingsDialog.__init__(self, parent, _('Priority'),
Gtk.DialogFlags.MODAL, settings, account)
self.connect('destroy', self.on_destroy)
def on_destroy(self, *args):
# Update priority
if self.account not in app.connections:
return
show = app.SHOW_LIST[app.connections[self.account].connected]
status = app.connections[self.account].status
app.connections[self.account].change_status(show, status)
class CutstomHostnameDialog(SettingsDialog):
def __init__(self, account, parent):
settings = [
Setting(SettingKind.SWITCH, _('Enable'),
SettingType.ACCOUNT_CONFIG,
'use_custom_host', name='custom'),
Setting(SettingKind.ENTRY, _('Hostname'),
SettingType.ACCOUNT_CONFIG, 'custom_host',
enabledif=('custom', True)),
Setting(SettingKind.ENTRY, _('Port'),
SettingType.ACCOUNT_CONFIG, 'custom_port',
enabledif=('custom', True)),
]
SettingsDialog.__init__(self, parent, _('Connection Settings'),
Gtk.DialogFlags.MODAL, settings, account)
class CertificateDialog(SettingsDialog):
def __init__(self, account, parent):
settings = [
Setting(SettingKind.FILECHOOSER, _('Client Certificate'),
SettingType.ACCOUNT_CONFIG, 'client_cert',
props={'filefilter': (_('PKCS12 Files'), '*.p12')}),
Setting(SettingKind.SWITCH, _('Encrypted Certificate'),
SettingType.ACCOUNT_CONFIG, 'client_cert_encrypted'),
]
SettingsDialog.__init__(self, parent, _('Certificate Settings'),
Gtk.DialogFlags.MODAL, settings, account)
class LoginDialog(SettingsDialog):
def __init__(self, account, parent):
settings = [
Setting(SettingKind.ENTRY, _('Password'),
SettingType.ACCOUNT_CONFIG, 'password', name='password',
enabledif=('savepass', True)),
Setting(SettingKind.SWITCH, _('Save Password'),
SettingType.ACCOUNT_CONFIG, 'savepass', name='savepass'),
Setting(SettingKind.CHANGEPASSWORD, _('Change Password'),
SettingType.DIALOG, callback=self.on_password_change,
props={'dialog': None}),
]
SettingsDialog.__init__(self, parent, _('Login Settings'),
Gtk.DialogFlags.MODAL, settings, account)
self.connect('destroy', self.on_destroy)
def on_password_change(self, new_password, _data):
passwords.save_password(self.account, new_password)
def on_destroy(self, *args):
savepass = app.config.get_per('accounts', self.account, 'savepass')
if not savepass:
passwords.delete_password(self.account)
class RemoveAccountWindow:
"""
Ask whether to remove from gajim only or both from gajim and the server,
then remove the account given
"""
def on_remove_account_window_destroy(self, _widget):
if self.account in app.interface.instances:
del app.interface.instances[self.account]['remove_account']
def on_cancel_button_clicked(self, _widget):
self._ui.remove_account_window.destroy()
def __init__(self, account):
self.account = account
self._ui = get_builder('remove_account_window.ui')
active_window = app.app.get_active_window()
self._ui.remove_account_window.set_transient_for(active_window)
self._ui.remove_account_window.set_title(
_('Removing account %s') % self.account)
self._ui.connect_signals(self)
self._ui.remove_account_window.show_all()
def on_remove_button_clicked(self, _widget):
def remove():
if self.account in app.connections and \
app.connections[self.account].connected and \
not self._ui.remove_and_unregister_radiobutton.get_active():
# change status to offline only if we will not
# remove this JID from server
app.connections[self.account].change_status('offline', 'offline')
if self._ui.remove_and_unregister_radiobutton.get_active():
if not self.account in app.connections:
ErrorDialog(
_('Account is disabled'),
_('To unregister from a server, the account must be '
'enabled.'),
transient_for=self._ui.remove_account_window)
return
if not app.connections[self.account].password:
def on_ok(passphrase, _checked):
if passphrase == -1:
# We don't remove account cause we
# canceled pw window
return
app.connections[self.account].password = passphrase
app.connections[self.account].unregister_account(
self._on_remove_success)
PassphraseDialog(
_('Password required'),
_('Enter your password for account %s') % self.account,
_('Save password'), ok_handler=on_ok,
transient_for=self._ui.remove_account_window)
return
app.connections[self.account].unregister_account(
self._on_remove_success)
else:
self._on_remove_success(True)
if self.account in app.connections and \
app.connections[self.account].connected:
ConfirmationDialog(
_('Account "%s" is connected to the server') % self.account,
_('If you remove it, the connection will be lost.'),
on_response_ok=remove,
transient_for=self._ui.remove_account_window)
else:
remove()
def on_remove_response_ok(self, is_checked):
if is_checked[0]:
self._on_remove_success(True)
def _on_remove_success(self, res):
# action of unregistration has failed, we don't remove the account
# Error message is send by connect_and_auth()
if not res:
ConfirmationDialogDoubleRadio(
_('Connection to server %s failed') % self.account,
_('What would you like to do?'),
_('Remove only from Gajim'),
_('Don\'t remove anything. I\'ll try again later'),
on_response_ok=self.on_remove_response_ok, is_modal=False,
transient_for=self._ui.remove_account_window)
return
# Close all opened windows
app.interface.roster.close_all(self.account, force=True)
if self.account in app.connections:
app.connections[self.account].disconnect(reconnect=False)
app.connections[self.account].cleanup()
del app.connections[self.account]
app.logger.remove_roster(app.get_jid_from_account(self.account))
# Delete password must be before del_per() because it calls set_per()
# which would recreate the account with defaults values if not found
passwords.delete_password(self.account)
app.config.del_per('accounts', self.account)
del app.interface.instances[self.account]
if self.account in app.nicks:
del app.interface.minimized_controls[self.account]
del app.nicks[self.account]
del app.block_signed_in_notifications[self.account]
del app.groups[self.account]
app.contacts.remove_account(self.account)
del app.gc_connected[self.account]
del app.automatic_rooms[self.account]
del app.to_be_removed[self.account]
del app.newly_added[self.account]
del app.sleeper_state[self.account]
del app.last_message_time[self.account]
del app.status_before_autoaway[self.account]
del app.gajim_optional_features[self.account]
del app.caps_hash[self.account]
if len(app.connections) >= 2: # Do not merge accounts if only one exists
app.interface.roster.regroup = app.config.get('mergeaccounts')
else:
app.interface.roster.regroup = False
app.interface.roster.setup_and_draw_roster()
app.app.remove_account_actions(self.account)
gui_menu_builder.build_accounts_menu()
window = app.get_app_window('AccountsWindow')
if window is not None:
window.remove_account(self.account)
self._ui.remove_account_window.destroy()
def destroy(self):
self._ui.remove_account_window.destroy()