diff --git a/README.html b/README.html index b02df982b..aebf73589 100644 --- a/README.html +++ b/README.html @@ -28,8 +28,7 @@
  • For zeroconf (bonjour), the "enable link-local messaging" checkbox, you need dbus-glib, python-avahi
  • dnsutils (or whatever package provides the nslookup binary) for SRV support
  • gir1.2-gtkspell3-3.0 and aspell-LANG where lang is your locale eg. en, fr etc
  • -
  • gir1.2-secret-1 or gir1.2-gnomekeyring-1.0 for GnomeKeyring support as password storage
  • -
  • kwalletcli (for support of KDE Wallet as password storage)
  • +
  • gir1.2-secret-1 for GNOME Keyring or KDE support as password storage
  • gir1.2-notify-0.7 to get cooler popups
  • D-Bus running to have gajim-remote working. Some distributions split dbus-x11, which is needed for dbus to work with Gajim. Version >= 0.80 is required.
  • python3-dbus bindings (>=1.2.0)
  • diff --git a/src/common/config.py b/src/common/config.py index 4d756b37d..5649f8118 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -288,8 +288,6 @@ class Config: 'confirm_block': [ opt_str, '', _('Should we show the confirm block contact dialog or not? Empty string means we never show the dialog.')], 'confirm_custom_status': [ opt_str, '', _('Should we show the confirm custom status dialog or not? Empty string means we never show the dialog.')], 'enable_negative_priority': [ opt_bool, False, _('If True, you will be able to set a negative priority to your account in account modification window. BE CAREFUL, when you are logged in with a negative priority, you will NOT receive any message from your server.')], - 'use_gnomekeyring': [opt_bool, True, _('If True, Gajim will use Gnome Keyring (if available) to store account passwords.')], - 'use_kwalletcli': [opt_bool, True, _('If True, Gajim will use KDE Wallet (if kwalletcli is available) to store account passwords.')], 'show_contacts_number': [opt_bool, True, _('If True, Gajim will show number of online and total contacts in account and group rows.')], 'treat_incoming_messages': [ opt_str, '', _('Can be empty, \'chat\' or \'normal\'. If not empty, treat all incoming messages as if they were of this type')], 'scroll_roster_to_last_message': [opt_bool, True, _('If True, Gajim will scroll and select the contact who sent you the last message, if chat window is not already opened.')], diff --git a/src/common/exceptions.py b/src/common/exceptions.py index 7f5edef4d..958d996dc 100644 --- a/src/common/exceptions.py +++ b/src/common/exceptions.py @@ -143,15 +143,3 @@ class PluginsystemError(Exception): def __str__(self): return self.text - -class GnomeKeyringError(Exception): - """ - Error with Gnome Keyring - """ - - def __init__(self, error): - Exception.__init__(self) - self.error = error - - def __str__(self): - return str(self.error) diff --git a/src/common/kwalletbinding.py b/src/common/kwalletbinding.py deleted file mode 100644 index 750f3c8c1..000000000 --- a/src/common/kwalletbinding.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding:utf-8 -*- -## src/common/kwalletbinding.py -## -## Copyright (c) 2009 Thorsten Glaser -## -## 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. This file is -## also available under the terms of The MirOS Licence. -## -## 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 . -## - -__all__ = ['kwallet_available', 'kwallet_get', 'kwallet_put'] - -import subprocess - - -def kwallet_available(): - """ - Return True if kwalletcli can be run, False otherwise - """ - try: - p = subprocess.Popen(["kwalletcli", "-qV"]) - except Exception: - return False - p.communicate() - if p.returncode == 0: - return True - return False - - -def kwallet_get(folder, entry): - """ - Retrieve a passphrase from the KDE Wallet via kwalletcli - - Arguments: - • folder: The top-level category to use (normally the programme name) - • entry: The key of the entry to retrieve - - Returns the passphrase, False if it cannot be found, - or None if an error occured. - """ - p = subprocess.Popen(["kwalletcli", "-q", "-f", folder.encode('utf-8'), - "-e", entry.encode('utf-8')], stdout=subprocess.PIPE) - pw = p.communicate()[0] - if p.returncode == 0: - return pw - if p.returncode == 1 or p.returncode == 4: - # ENOENT - return False - # error - return None - - -def kwallet_put(folder, entry, passphrase): - """ - Store a passphrase into the KDE Wallet via kwalletcli - - Arguments: - • folder: The top-level category to use (normally the programme name) - • entry: The key of the entry to store - • passphrase: The value to store - - Returns True on success, False otherwise. - """ - p = subprocess.Popen(["kwalletcli", "-q", "-f", folder.encode('utf-8'), - "-e", entry.encode('utf-8'), "-P"], stdin=subprocess.PIPE) - p.communicate(passphrase.encode('utf-8')) - if p.returncode == 0: - return True - return False diff --git a/src/common/passwords.py b/src/common/passwords.py index d7a926aa2..4aebcc7e8 100644 --- a/src/common/passwords.py +++ b/src/common/passwords.py @@ -28,15 +28,8 @@ __all__ = ['get_password', 'save_password'] import warnings from common import gajim -from common import kwalletbinding -from common.exceptions import GnomeKeyringError import gi -USER_HAS_LIBSECRET = False -USER_HAS_GNOMEKEYRING = False -USER_USES_GNOMEKEYRING = False -USER_HAS_KWALLETCLI = False -GnomeKeyring = None Secret = None class PasswordStorage(object): @@ -49,10 +42,8 @@ class PasswordStorage(object): class SimplePasswordStorage(PasswordStorage): def get_password(self, account_name): passwd = gajim.config.get_per('accounts', account_name, 'password') - if passwd and (passwd.startswith('gnomekeyring:') or passwd.startswith('libsecret:') or \ - passwd == ''): - # this is not a real password, it's either a gnome - # keyring token or stored in the KDE wallet + if passwd and passwd.startswith('libsecret:'): + # this is not a real password, it’s stored through libsecret. return None else: return passwd @@ -63,98 +54,6 @@ class SimplePasswordStorage(PasswordStorage): gajim.connections[account_name].password = password -class GnomePasswordStorage(PasswordStorage): - def __init__(self): - (err, self.keyring) = GnomeKeyring.get_default_keyring_sync() - if err != GnomeKeyring.Result.OK: - raise GnomeKeyringError(err) - if self.keyring is None: - self.keyring = 'login' - err = GnomeKeyring.create_sync(self.keyring, None) - if err not in (GnomeKeyring.Result.OK, - GnomeKeyring.Result.KEYRING_ALREADY_EXISTS): - raise GnomeKeyringError(err) - - def get_password(self, account_name): - conf = gajim.config.get_per('accounts', account_name, 'password') - if conf is None or conf == '': - return None - if not (conf.startswith('gnomekeyring:') or conf.startswith('libsecret')): - password = conf - ## migrate the password over to keyring - try: - self.save_password(account_name, password) - except GnomeKeyringError as e: - if e.error == GnomeKeyring.Result.NO_KEYRING_DAEMON: - ## no keyring daemon: in the future, stop using it - set_storage(SimplePasswordStorage()) - return password - server = gajim.config.get_per('accounts', account_name, 'hostname') - user = gajim.config.get_per('accounts', account_name, 'name') - attributes1 = GnomeKeyring.attribute_list_new() - GnomeKeyring.attribute_list_append_string(attributes1, 'server', - str(server)) - GnomeKeyring.attribute_list_append_string(attributes1, 'user', - str(user)) - GnomeKeyring.attribute_list_append_string(attributes1, 'protocol', - 'xmpp') - attributes2 = GnomeKeyring.attribute_list_new() - GnomeKeyring.attribute_list_append_string(attributes2, 'account_name', - str(account_name)) - GnomeKeyring.attribute_list_append_string(attributes2, 'gajim', - '1') - (err, items) = GnomeKeyring.find_items_sync( - GnomeKeyring.ItemType.NETWORK_PASSWORD, attributes1) - if err != GnomeKeyring.Result.OK: - (err, items) = GnomeKeyring.find_items_sync( - GnomeKeyring.ItemType.GENERIC_SECRET, attributes2) - if err == GnomeKeyring.Result.OK and len(items) > 0: - password = items[0].secret - self.save_password(account_name, password) - for item in items: - GnomeKeyring.item_delete_sync(item.keyring, - int(item.item_id)) - else: - items = [] - if len(items) > 1: - warnings.warn("multiple gnome keyring items found for account %s;" - " trying to use the first one..." % account_name) - if err == GnomeKeyring.Result.NO_KEYRING_DAEMON: - ## no keyring daemon: in the future, stop using it - set_storage(SimplePasswordStorage()) - if items: - return items[0].secret - return None - - def save_password(self, account_name, password, update=True): - server = gajim.config.get_per('accounts', account_name, 'hostname') - user = gajim.config.get_per('accounts', account_name, 'name') - display_name = _('XMPP account %s@%s') % (user, server) - attributes1 = GnomeKeyring.attribute_list_new() - GnomeKeyring.attribute_list_append_string(attributes1, 'server', - str(server)) - GnomeKeyring.attribute_list_append_string(attributes1, 'user', - str(user)) - GnomeKeyring.attribute_list_append_string(attributes1, 'protocol', - 'xmpp') - if password is None: - password = str() - (err, auth_token) = GnomeKeyring.item_create_sync(self.keyring, - GnomeKeyring.ItemType.NETWORK_PASSWORD, display_name, attributes1, - password, update) - if err != GnomeKeyring.Result.OK: - if err in (GnomeKeyring.Result.DENIED, - GnomeKeyring.Result.CANCELLED): - set_storage(SimplePasswordStorage()) - storage.save_password(account_name, password) - return - else: - raise GnomeKeyringError(err) - gajim.config.set_per('accounts', account_name, 'password', - 'gnomekeyring:') - if account_name in gajim.connections: - gajim.connections[account_name].password = password - class SecretPasswordStorage(PasswordStorage): def __init__(self): self.GAJIM_SCHEMA = Secret.Schema.new("org.gnome.keyring.NetworkPassword", @@ -170,14 +69,7 @@ class SecretPasswordStorage(PasswordStorage): conf = gajim.config.get_per('accounts', account_name, 'password') if conf is None: return None - if conf == '': - # Migrate from kwallet - if kwalletbinding.kwallet_available(): - kw_storage = KWalletPasswordStorage() - password = kw_storage.get_password(account_name) - self.save_password(account_name, password) - return password - if not (conf.startswith('libsecret:') or conf.startswith('gnomekeyring:')): + if not conf.startswith('libsecret:'): password = conf ## migrate the password over to keyring try: @@ -206,94 +98,22 @@ class SecretPasswordStorage(PasswordStorage): if account_name in gajim.connections: gajim.connections[account_name].password = password -class KWalletPasswordStorage(PasswordStorage): - def get_password(self, account_name): - pw = gajim.config.get_per('accounts', account_name, 'password') - if not pw or pw.startswith('gnomekeyring:'): - # unset, empty or not ours - return None - if pw != '': - # migrate the password - if kwalletbinding.kwallet_put('gajim', account_name, pw): - gajim.config.set_per('accounts', account_name, 'password', - '') - else: - # stop using the KDE Wallet - set_storage(SimplePasswordStorage()) - return pw - pw = kwalletbinding.kwallet_get('gajim', account_name) - if pw is None: - # stop using the KDE Wallet - set_storage(SimplePasswordStorage()) - if not pw: - # False, None, or the empty string - return None - return pw - - def save_password(self, account_name, password): - if not kwalletbinding.kwallet_put('gajim', account_name, password): - # stop using the KDE Wallet - set_storage(SimplePasswordStorage()) - storage.save_password(account_name, password) - return - pwtoken = '' - if not password: - # no sense in looking up the empty string in the KWallet - pwtoken = '' - gajim.config.set_per('accounts', account_name, 'password', pwtoken) - if account_name in gajim.connections: - gajim.connections[account_name].password = password - storage = None def get_storage(): global storage if storage is None: # None is only in first time get_storage is called - if gajim.config.get('use_gnomekeyring'): - global Secret - try: - gi.require_version('Secret', '1') - gir = __import__('gi.repository', globals(), locals(), - ['Secret'], 0) - Secret = gir.Secret - except (ValueError, AttributeError): - global GnomeKeyring - try: - gir = __import__('gi.repository', globals(), locals(), - ['GnomeKeyring'], 0) - GnomeKeyring = gir.GnomeKeyring - except (ImportError, AttributeError): - pass - else: - global USER_HAS_GNOMEKEYRING - global USER_USES_GNOMEKEYRING - USER_HAS_GNOMEKEYRING = True - if GnomeKeyring.is_available(): - USER_USES_GNOMEKEYRING = True - else: - USER_USES_GNOMEKEYRING = False - else: - global USER_HAS_LIBSECRET - USER_HAS_LIBSECRET = True - if USER_HAS_LIBSECRET: - try: - storage = SecretPasswordStorage() - return storage - except Exception: - storage = None - if USER_USES_GNOMEKEYRING: - try: - storage = GnomePasswordStorage() - except GnomeKeyringError: - storage = None - if storage is None: - if gajim.config.get('use_kwalletcli'): - global USER_HAS_KWALLETCLI - if kwalletbinding.kwallet_available(): - USER_HAS_KWALLETCLI = True - if USER_HAS_KWALLETCLI: - storage = KWalletPasswordStorage() - if storage is None: + global Secret + try: + gi.require_version('Secret', '1') + gir = __import__('gi.repository', globals(), locals(), + ['Secret'], 0) + Secret = gir.Secret + except (ValueError, AttributeError): + pass + try: + storage = SecretPasswordStorage() + except Exception: storage = SimplePasswordStorage() return storage diff --git a/src/features_window.py b/src/features_window.py index 96b836f31..23df54467 100644 --- a/src/features_window.py +++ b/src/features_window.py @@ -31,7 +31,6 @@ import gtkgui_helpers from common import gajim from common import helpers -from common import kwalletbinding from common.i18n import Q_ class FeaturesWindow: @@ -70,7 +69,7 @@ class FeaturesWindow: _('Feature not available under Windows.')), _('Password encryption'): (self.some_keyring_available, _('Passwords can be stored securely and not just in plaintext.'), - _('Requires gnome-keyring and python-gnome2-desktop, or kwalletcli.'), + _('Requires libsecret and a provider (such as GNOME Keyring and KSecretService).'), _('Feature not available under Windows.')), _('Spell Checker'): (self.speller_available, _('Spellchecking of composed messages.'), @@ -186,12 +185,10 @@ class FeaturesWindow: def some_keyring_available(self): if os.name == 'nt': return False - if kwalletbinding.kwallet_available(): - return True try: - gi.require_version('GnomeKeyring', '1.0') - from gi.repository import GnomeKeyring - except Exception: + gi.require_version('Secret', '1') + from gi.repository import Secret + except (ValueError, ImportError): return False return True diff --git a/src/gui_interface.py b/src/gui_interface.py index ac36a64dc..d06a379d0 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -731,11 +731,6 @@ class Interface: if account in self.pass_dialog: return text = _('Enter your password for account %s') % account - if passwords.USER_HAS_GNOMEKEYRING and \ - not passwords.USER_USES_GNOMEKEYRING: - text += '\n' + _('Gnome Keyring is installed but not ' - 'correctly started (environment variable probably not ' - 'correctly set)') def on_ok(passphrase, save): if save: