/*
 * kwallet.cpp: KWallet provider for SVN_AUTH_CRED_SIMPLE
 *
 * ====================================================================
 * Copyright (c) 2008 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 */

/* ==================================================================== */



/*** Includes. ***/

#include <apr_pools.h>
#include "svn_auth.h"
#include "svn_auth_dso.h"
#include "svn_error.h"
#include "svn_version.h"

#include "private/svn_auth_private.h"

#include "svn_private_config.h"

#ifdef SVN_HAVE_QT3
#include <qstring.h>
#include <qwidget.h>
#include <klocale.h>
#else
#include <QtCore/QString>
#include <QtGui/QWidget>
#endif

#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kwallet.h>


/*-----------------------------------------------------------------------*/
/* KWallet simple provider, puts passwords in KWallet                    */
/*-----------------------------------------------------------------------*/

class SvnKWallet
{
public:
  static svn_boolean_t init(svn_boolean_t non_interactive);
  SvnKWallet(const char *realmstring, const char *username);
  ~SvnKWallet();
  svn_boolean_t get(const char **password, apr_pool_t *pool);
  svn_boolean_t set(const char *password);
private:
  bool open(bool check_key, bool create_wallet);

  KApplication application;
  QWidget widget;
  WId wid;
  KWallet::Wallet *wallet;
  QString wallet_name;
  QString folder;
  QString key;
};

svn_boolean_t SvnKWallet::init( svn_boolean_t non_interactive )
{
  if (non_interactive)
    {
      return FALSE;
    }

  if (! KWallet::Wallet::isEnabled())
    {
      return FALSE;
    }

  char kdearg0[] = "svn";
  char *kdeargs[1] = { kdearg0 };
#ifdef SVN_HAVE_QT3
  KCmdLineArgs::init(1,
                     kdeargs,
                     "Subversion",
                     "subversion",
                     i18n("Version control system"),
                     SVN_VER_NUMBER,
                     true);
#else
  KCmdLineArgs::init(1,
                     kdeargs,
                     "Subversion",
                     "subversion",
                     ki18n("Subversion"),
                     SVN_VER_NUMBER,
                     ki18n("Version control system"),
                     KCmdLineArgs::CmdLineArgKDE);
#endif
  return TRUE;
}

SvnKWallet::SvnKWallet(const char *realmstring, const char *username)
  : wallet(0)
{
  wallet_name = KWallet::Wallet::NetworkWallet();
  folder = QString::fromUtf8("Subversion");
  key = QString::fromUtf8(username) + "@" + QString::fromUtf8(realmstring);
}

SvnKWallet::~SvnKWallet()
{
//  KWallet::Wallet::disconnectApplication(wallet_name,
//                                         QString::fromUtf8("Subversion"));
  delete wallet;
}

svn_boolean_t SvnKWallet::get(const char **password, apr_pool_t *pool)
{
  if (!open(true, false))
    return FALSE;
  QString q_password;
  if (wallet->readPassword(key, q_password) != 0)
    return FALSE;
#ifdef SVN_HAVE_QT3
  *password = apr_pstrmemdup(pool,
                             q_password.utf8().data(),
                             q_password.length());
#else
  *password = apr_pstrmemdup(pool,
                             q_password.toUtf8().data(),
                             q_password.size());
#endif
  return TRUE;
}

svn_boolean_t SvnKWallet::set(const char *password)
{
  if (!open(false, true))
    return FALSE;
  QString q_password = QString::fromUtf8(password);
  if (wallet->writePassword(key, q_password) != 0)
    return FALSE;
  return TRUE;
}

bool SvnKWallet::open(bool check_key, bool create_wallet)
{
  if (check_key && KWallet::Wallet::keyDoesNotExist(wallet_name, folder, key))
    return false;
  wallet = KWallet::Wallet::openWallet(wallet_name,
                                       wid,
                                       KWallet::Wallet::Synchronous);
  if (!wallet)
    return false;
  if (!wallet->hasFolder(folder))
    {
      if (!create_wallet)
        return false;
      if (!wallet->createFolder(folder))
        return false;
    }
  return wallet->setFolder(folder);
}

/* Implementation of svn_auth__password_get_t that retrieves
   the password from KWallet. */
static svn_boolean_t
kwallet_password_get(const char **password,
                     apr_hash_t *creds,
                     const char *realmstring,
                     const char *username,
                     svn_boolean_t non_interactive,
                     apr_pool_t *pool)
{
  if (!SvnKWallet::init(non_interactive))
    return FALSE;
  return SvnKWallet(realmstring, username).get(password, pool);
}

/* Implementation of svn_auth__password_set_t that stores
   the password in KWallet. */
static svn_boolean_t
kwallet_password_set(apr_hash_t *creds,
                     const char *realmstring,
                     const char *username,
                     const char *password,
                     svn_boolean_t non_interactive,
                     apr_pool_t *pool)
{
  if (!SvnKWallet::init(non_interactive))
    return FALSE;
  return SvnKWallet(realmstring, username).set(password);
}

/* Get cached encrypted credentials from the simple provider's cache. */
static svn_error_t *
kwallet_simple_first_creds(void **credentials,
                           void **iter_baton,
                           void *provider_baton,
                           apr_hash_t *parameters,
                           const char *realmstring,
                           apr_pool_t *pool)
{
  return svn_auth__simple_first_creds_helper(credentials,
                                             iter_baton,
                                             provider_baton,
                                             parameters,
                                             realmstring,
                                             kwallet_password_get,
                                             SVN_AUTH__KWALLET_PASSWORD_TYPE,
                                             pool);
}

/* Save encrypted credentials to the simple provider's cache. */
static svn_error_t *
kwallet_simple_save_creds(svn_boolean_t *saved,
                          void *credentials,
                          void *provider_baton,
                          apr_hash_t *parameters,
                          const char *realmstring,
                          apr_pool_t *pool)
{
  return svn_auth__simple_save_creds_helper(saved, credentials,
                                            provider_baton,
                                            parameters,
                                            realmstring,
                                            kwallet_password_set,
                                            SVN_AUTH__KWALLET_PASSWORD_TYPE,
                                            pool);
}

static const svn_auth_provider_t kwallet_simple_provider = {
  SVN_AUTH_CRED_SIMPLE,
  kwallet_simple_first_creds,
  NULL,
  kwallet_simple_save_creds
};

/* Public API */
extern "C" {
void
svn_auth_get_kwallet_simple_provider(svn_auth_provider_object_t **provider,
                                     apr_pool_t *pool)
{
  svn_auth_provider_object_t *po =
    static_cast<svn_auth_provider_object_t *> (apr_pcalloc(pool, sizeof(*po)));

  po->vtable = &kwallet_simple_provider;
  *provider = po;
}
}
