changeset 6c332dd9e795 in /home/hg/repos/gajim
details:http://hg.gajim.org/gajim?cmd=changeset;node=6c332dd9e795
description: [mirabilos] save password in kwallet is available. Fixes #5153
diffstat:
src/common/config.py | 1 +
src/common/kwalletbinding.py | 75 +++++++++++++++++++++++++++++++++++++
src/common/passwords.py | 66 +++++++++++++++++++++++++++++----
src/features_window.py | 9 +++-
4 files changed, 140 insertions(+), 11 deletions(-)
diffs (240 lines):
diff -r 130d308b573a -r 6c332dd9e795 src/common/config.py
--- a/src/common/config.py Tue Jul 14 18:48:24 2009 +0200
+++ b/src/common/config.py Wed Jul 15 21:02:47 2009 +0200
@@ -247,6 +247,7 @@
'confirm_metacontacts': [ opt_str, '', _('Should we show the confirm metacontacts creation 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 -r 130d308b573a -r 6c332dd9e795 src/common/kwalletbinding.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/kwalletbinding.py Wed Jul 15 21:02:47 2009 +0200
@@ -0,0 +1,75 @@
+# -*- coding:utf-8 -*-
+## src/common/kwalletbinding.py
+##
+## Copyright (c) 2009 Thorsten Glaser <t.glaser AT tarent.de>
+##
+## 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 <http://www.gnu.org/licenses/>.
+##
+
+__all__ = ['kwallet_available', 'kwallet_get', 'kwallet_put']
+
+import subprocess
+
+
+def kwallet_available():
+ """Return True if kwalletcli can be run, False otherwise."""
+ p = subprocess.Popen(["kwalletcli", "-qV"])
+ 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 as unicode, 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 unicode(pw.decode('utf-8'))
+ 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 -r 130d308b573a -r 6c332dd9e795 src/common/passwords.py
--- a/src/common/passwords.py Tue Jul 14 18:48:24 2009 +0200
+++ b/src/common/passwords.py Wed Jul 15 21:02:47 2009 +0200
@@ -7,6 +7,7 @@
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
## Julien Pivotto <roidelapluie AT gmail.com>
## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
+## Copyright (c) 2009 Thorsten Glaser <t.glaser AT tarent.de>
##
## This file is part of Gajim.
##
@@ -27,9 +28,11 @@
import warnings
from common import gajim
+from common import kwalletbinding
USER_HAS_GNOMEKEYRING = False
USER_USES_GNOMEKEYRING = False
+USER_HAS_KWALLETCLI = False
gnomekeyring = None
class PasswordStorage(object):
@@ -42,8 +45,11 @@
class SimplePasswordStorage(PasswordStorage):
def get_password(self, account_name):
passwd = gajim.config.get_per('accounts', account_name, 'password')
- if passwd and passwd.startswith('gnomekeyring:'):
- return None # this is not a real password, it's a gnome keyring token
+ if passwd and (passwd.startswith('gnomekeyring:') or \
+ passwd == '<kwallet>'):
+ # this is not a real password, it's either a gnome
+ # keyring token or stored in the KDE wallet
+ return None
else:
return passwd
@@ -65,7 +71,7 @@
def get_password(self, account_name):
conf = gajim.config.get_per('accounts', account_name, 'password')
- if conf is None:
+ if conf is None or conf == '<kwallet>':
return None
if not conf.startswith('gnomekeyring:'):
password = conf
@@ -129,6 +135,45 @@
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 != '<kwallet>':
+ # migrate the password
+ if kwalletbinding.kwallet_put('gajim', account_name, pw):
+ gajim.config.set_per('accounts', account_name, 'password',
+ '<kwallet>')
+ 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 = '<kwallet>'
+ 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
@@ -150,11 +195,16 @@
if USER_USES_GNOMEKEYRING:
try:
storage = GnomePasswordStorage()
- except gnomekeyring.NoKeyringDaemonError:
- storage = SimplePasswordStorage()
- except gnomekeyring.DeniedError:
- storage = SimplePasswordStorage()
- else:
+ except (gnomekeyring.NoKeyringDaemonError, gnomekeyring.DeniedError):
+ 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:
storage = SimplePasswordStorage()
return storage
diff -r 130d308b573a -r 6c332dd9e795 src/features_window.py
--- a/src/features_window.py Tue Jul 14 18:48:24 2009 +0200
+++ b/src/features_window.py Wed Jul 15 21:02:47 2009 +0200
@@ -30,6 +30,7 @@
from common import gajim
from common import helpers
+from common import kwalletbinding
class FeaturesWindow:
'''Class for features window'''
@@ -66,9 +67,9 @@
_('Gajim session is stored on logout and restored on login.'),
_('Requires python-gnome2.'),
_('Feature not available under Windows.')),
- _('Password encryption'): (self.gnome_keyring_available,
+ _('Password encryption'): (self.some_keyring_available,
_('Passwords can be stored securely and not just in plaintext.'),
- _('Requires gnome-keyring and python-gnome2-desktop.'),
+ _('Requires gnome-keyring and python-gnome2-desktop, or kwalletcli.'),
_('Feature not available under Windows.')),
_('SRV'): (self.srv_available,
_('Ability to connect to servers which are using SRV records.'),
@@ -200,9 +201,11 @@
return False
return True
- def gnome_keyring_available(self):
+ def some_keyring_available(self):
if os.name == 'nt':
return False
+ if kwalletbinding.kwallet_available():
+ return True
try:
import gnomekeyring
except Exception: