# -*- coding:utf-8 -*-
## src/common/passwords.py
##
## Copyright (C) 2006 Gustavo J. A. M. Carneiro <gjcarneiro AT gmail.com>
##                    Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
## 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.
##
## 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/>.
##

__all__ = ['get_password', 'save_password']

import warnings
from common import gajim
from common import kwalletbinding
from common.exceptions import GnomeKeyringError

USER_HAS_GNOMEKEYRING = False
USER_USES_GNOMEKEYRING = False
USER_HAS_KWALLETCLI = False
GnomeKeyring = None

class PasswordStorage(object):
    def get_password(self, account_name):
        raise NotImplementedError
    def save_password(self, account_name, password):
        raise NotImplementedError


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 == '<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

    def save_password(self, account_name, password):
        gajim.config.set_per('accounts', account_name, 'password', password)
        if account_name in gajim.connections:
            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 == '<kwallet>':
            return None
        if not conf.startswith('gnomekeyring:'):
            password = conf
            ## migrate the password over to keyring
            try:
                self.save_password(account_name, password, update=False)
            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 items:
            return items[0].secret
        else:
            return None
        if err == GnomeKeyring.Result.NO_KEYRING_DAEMON:
            ## no keyring daemon: in the future, stop using it
            set_storage(SimplePasswordStorage())
        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 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
    if storage is None: # None is only in first time get_storage is called
        if gajim.config.get('use_gnomekeyring'):
            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
        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:
            storage = SimplePasswordStorage()
    return storage

def set_storage(storage_):
    global storage
    storage = storage_


def get_password(account_name):
    return get_storage().get_password(account_name)

def save_password(account_name, password):
    return get_storage().save_password(account_name, password)