[vlc-devel] [PATCH 06/11] keystore: add kwallet module

Thomas Guillem thomas at gllm.fr
Wed Jan 6 16:56:26 CET 2016


---
 configure.ac                 |  33 ++
 modules/keystore/Makefile.am |  20 ++
 modules/keystore/kwallet.cpp | 706 +++++++++++++++++++++++++++++++++++++++++++
 modules/keystore/kwallet.hpp |  86 ++++++
 4 files changed, 845 insertions(+)
 create mode 100644 modules/keystore/kwallet.cpp
 create mode 100644 modules/keystore/kwallet.hpp

diff --git a/configure.ac b/configure.ac
index f2b9688..e3962ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4078,6 +4078,39 @@ dnl
 PKG_ENABLE_MODULES_VLC([SECRET], [], [libsecret-1], [use libsecret for keystore], [auto])
 
 dnl
+dnl  kwallet
+dnl
+AC_ARG_ENABLE(kwallet,
+  [AS_HELP_STRING([--enable-kwallet],
+    [use KWallet for keystore (default auto)])])
+have_kwallet="no"
+KWALLET_CXXFLAGS=""
+KWALLET_LIBS=""
+AS_IF([test "${enable_qt}" != "no" -a "${KDE4_CONFIG}" != "no" -a "${enable_kwallet}" != "no" ], [
+  VLC_SAVE_FLAGS
+  AC_LANG_PUSH([C++])
+  KWALLET_CXXFLAGS="$QT_CFLAGS $CXXFLAGS_qt4 -I`$KDE4_CONFIG --path include`"
+  CPPFLAGS="$CPPFLAGS $KWALLET_CXXFLAGS"
+  AC_CHECK_HEADER([kwallet.h], [
+    KWALLET_LIBS="$QT_LIBS -L`$KDE4_CONFIG --install lib` -lkdeui"
+    LDFLAGS="$LDFLAGS $KWALLET_LIBS"
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <kwallet.h>], [
+KWallet::Wallet::LocalWallet();
+    ])], [
+      have_kwallet="yes"
+    ])
+  AC_LANG_POP([C++])
+  VLC_RESTORE_FLAGS
+  ])
+#])
+AS_IF([test "$enable_kwallet" = "yes" -a "$have_kwallet" = "no" ], [
+  AC_MSG_ERROR([KWallet headers or so not found])
+])
+AC_SUBST(KWALLET_CXXFLAGS)
+AC_SUBST(KWALLET_LIBS)
+AM_CONDITIONAL([HAVE_KWALLET], [test "${have_kwallet}" = "yes"])
+
+dnl
 dnl  Developers helper modules (should be hidden from configure help)
 dnl
 AC_ARG_ENABLE(devtools, [], [], [enable_devtools="no"])
diff --git a/modules/keystore/Makefile.am b/modules/keystore/Makefile.am
index dc8c526..64787de 100644
--- a/modules/keystore/Makefile.am
+++ b/modules/keystore/Makefile.am
@@ -8,6 +8,26 @@ libsecret_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(SECRET_CFLAGS)
 libsecret_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(keystoredir)'
 libsecret_plugin_la_LIBADD = $(SECRET_LIBS)
 
+
+# Meta-object compilation
+
+SUFFIXES += .hpp .moc.cpp
+moc_verbose = $(moc_verbose_$(V))
+moc_verbose_ = $(moc_verbose__$(AM_DEFAULT_VERBOSITY))
+moc_verbose_0 = @echo "  MOC   " $@;
+moc_verbose__0 = $(moc_verbose_0)
+
+.hpp.moc.cpp:
+	$(moc_verbose)$(MOC) $(MOC_CPPFLAGS) -o $@ $<
+
+libkwallet_plugin_la_SOURCES = keystore/kwallet.cpp keystore/kwallet.moc.cpp
+libkwallet_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
+libkwallet_plugin_la_CXXFLAGS = $(KWALLET_CXXFLAGS)
+libkwallet_plugin_la_LIBADD = $(KWALLET_LIBS)
+if HAVE_KWALLET
+keystore_LTLIBRARIES += libkwallet_plugin.la
+endif
+
 keystore_LTLIBRARIES += \
 	$(LTLIBsecret)
 
diff --git a/modules/keystore/kwallet.cpp b/modules/keystore/kwallet.cpp
new file mode 100644
index 0000000..902fb09
--- /dev/null
+++ b/modules/keystore/kwallet.cpp
@@ -0,0 +1,706 @@
+/*****************************************************************************
+ * kwallet.cpp: KWallet keystore module
+ *****************************************************************************
+ * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "kwallet.hpp"
+
+#include <vlc_plugin.h>
+#include <vlc_interrupt.h>
+#include <vlc_strings.h>
+
+#include <QMap>
+#include <QUrl>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusConnectionInterface>
+#include <QtDBus/qdbusservicewatcher.h>
+
+#define MAP_KEY_SECRET QString("secret")
+
+#define qfu(i) QString::fromUtf8(i)
+#define qtu(i) ((i).toUtf8().constData())
+
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_shortname(N_("KWallet keystore"))
+    set_description(N_("secrets are stored via KWallet"))
+    set_category(CAT_ADVANCED)
+    set_subcategory(SUBCAT_ADVANCED_MISC)
+    set_capability("keystore", 100)
+    set_callbacks(Open, Close)
+    cannot_unload_broken_library()
+vlc_module_end ()
+
+/* List of kwallet services names */
+static const QStringList kwalletServices = QStringList()
+    << QString::fromLatin1("org.kde.kwalletd5")
+    << QString::fromLatin1("org.kde.kwalletd");
+
+static const char *const ppsz_keys[] = {
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    "realm",
+    "authtype",
+};
+static_assert(sizeof(ppsz_keys)/sizeof(*ppsz_keys) == KEY_MAX, "key mismatch");
+
+static int
+str2key(const char *psz_key)
+{
+    for (unsigned int i = 0; i < KEY_MAX; ++i)
+    {
+        if (ppsz_keys[i] && strcmp(ppsz_keys[i], psz_key) == 0)
+            return i;
+    }
+    return -1;
+}
+
+static int
+str2int(const char *psz_port)
+{
+    if (psz_port)
+    {
+        bool ok;
+        int i_port = QString::fromLocal8Bit(psz_port).toInt(&ok);
+        if (!ok)
+            return -1;
+        return i_port;
+    }
+    else
+        return -1;
+}
+
+/**
+ * Create a key and a map from values
+ */
+static QString
+values2Key(const char * const ppsz_values[KEY_MAX])
+{
+    const char *psz_protocol = ppsz_values[KEY_PROTOCOL];
+    const char *psz_user = ppsz_values[KEY_USER];
+    const char *psz_server = ppsz_values[KEY_SERVER];
+    const char *psz_path = ppsz_values[KEY_PATH];
+    const char *psz_port = ppsz_values[KEY_PORT];
+    int i_port = str2int(psz_port);
+
+    if (!psz_protocol || !psz_server)
+        return QString();
+
+    QUrl url;
+    url.setScheme(qfu(psz_protocol));
+    url.setHost(qfu(psz_server));
+    if (psz_user != NULL)
+        url.setUserName(qfu(psz_user));
+    if (psz_path != NULL)
+        url.setPath(qfu(psz_path));
+
+    if (i_port != -1)
+        url.setPort(i_port);
+
+    if (!url.isValid())
+        return QString();
+    return url.toString();
+}
+
+/**
+ * In case of error, caller must free ppsz_values[*]
+ */
+static int
+key2Values(const QString &key, char * ppsz_values[KEY_MAX])
+{
+    QUrl url(key);
+    if (!url.isValid())
+        return VLC_EGENERIC;
+
+    if (url.scheme().isEmpty() || url.host().isEmpty())
+        return VLC_EGENERIC;
+
+    ppsz_values[KEY_PROTOCOL] = strdup(qtu(url.scheme()));
+    ppsz_values[KEY_SERVER] = strdup(qtu(url.host()));
+    if (!ppsz_values[KEY_PROTOCOL] || !ppsz_values[KEY_SERVER])
+        return VLC_EGENERIC;
+
+    if (!url.userName().isEmpty())
+    {
+        ppsz_values[KEY_USER] = strdup(qtu(url.userName()));
+        if (!ppsz_values[KEY_USER])
+            return VLC_EGENERIC;
+    }
+    if (!url.path().isEmpty())
+    {
+        ppsz_values[KEY_PATH] = strdup(qtu(url.path()));
+        if (!ppsz_values[KEY_PATH])
+            return VLC_EGENERIC;
+    }
+
+    int i_port = url.port();
+    if (i_port != -1)
+    {
+        ppsz_values[KEY_PORT] = strdup(QString::number(i_port).toLocal8Bit().constData());
+        if (!ppsz_values[KEY_PORT])
+            return VLC_EGENERIC;
+    }
+
+    return VLC_SUCCESS;
+}
+
+static bool
+matchQString(const char *psz_str, const QString &str)
+{
+    return psz_str == NULL || (!str.isEmpty() && strcmp(psz_str, qtu(str)) == 0);
+}
+
+static bool
+matchKey(const QString &key, const char * const ppsz_values[KEY_MAX])
+{
+    QUrl url(key);
+    if (!url.isValid())
+        return false;
+
+    if (url.scheme().isEmpty() || url.host().isEmpty())
+        return false;
+
+    const char *psz_protocol = ppsz_values[KEY_PROTOCOL];
+    const char *psz_user = ppsz_values[KEY_USER];
+    const char *psz_server = ppsz_values[KEY_SERVER];
+    const char *psz_path = ppsz_values[KEY_PATH];
+    const char *psz_port = ppsz_values[KEY_PORT];
+    int i_port = str2int(psz_port);
+
+    return (matchQString(psz_protocol, url.scheme())
+        && matchQString(psz_user, url.userName())
+        && matchQString(psz_server, url.host())
+        && matchQString(psz_path, url.path())
+        && (i_port == -1 || i_port == url.port()));
+}
+
+static QMap<QString, QString>
+values2Map(const char * const ppsz_values[KEY_MAX])
+{
+    QMap<QString, QString> map;
+    for (unsigned int i = 0; i < KEY_MAX; ++i)
+    {
+        const char *psz_key = ppsz_keys[i];
+        if (psz_key && ppsz_values[i])
+            map.insert(qfu(psz_key), qfu(ppsz_values[i]));
+    }
+    return map;
+}
+
+/**
+ * Return true if all pairs of matchMap are in map (and not the contrary).
+ */
+static bool
+matchMaps(const QMap<QString, QString> &matchMap,
+          const QMap<QString, QString> &map)
+{
+    if (matchMap.isEmpty())
+        return true;
+
+    QMapIterator<QString, QString> it(matchMap);
+    while (it.hasNext())
+    {
+        it.next();
+        if (map.value(it.key()) != it.value())
+            return false;
+    }
+    return true;
+}
+
+/**
+ * Fill a keystore entry from a map and a key.
+ * In case of error, caller must free p_entry
+ */
+static int
+mapAndKey2Entry(const QMap<QString, QString> &map, const QString &key,
+                vlc_keystore_entry *p_entry)
+{
+    QMapIterator<QString, QString> it(map);
+    while (it.hasNext())
+    {
+        it.next();
+        if (it.key() != MAP_KEY_SECRET)
+        {
+            /* Copy map pair to ppsz_values */
+            const char *psz_key = qtu(it.key());
+            int i_key = str2key(psz_key);
+            if (i_key == -1 || i_key >= KEY_MAX)
+                return VLC_EGENERIC;
+
+            char *psz_value = strdup(qtu(it.value()));
+            if (!psz_value)
+                return VLC_EGENERIC;
+            p_entry->ppsz_values[i_key] = psz_value;
+        }
+        else
+        {
+            /* Copy secret from the map */
+            p_entry->i_secret_len = 
+                vlc_b64_decode_binary(&p_entry->p_secret,
+                                      it.value().toLocal8Bit().constData());
+        }
+    }
+    if (!p_entry->i_secret_len)
+        return VLC_EGENERIC;
+    return key2Values(key, p_entry->ppsz_values);
+}
+
+VLCKWallet::VLCKWallet(QCoreApplication *p_qApp, vlc_object_t *p_obj)
+    : mState(STATE_INIT)
+    , mObj(p_obj)
+    , mWallet(NULL)
+{
+    /* KWallet and Dbus methods need to be run from the mainloop */
+    moveToThread(p_qApp->thread());
+}
+
+void
+VLCKWallet::signalLocked(enum State state)
+{
+    mState = state;
+    mCond.wakeOne();
+}
+
+void
+VLCKWallet::signal(enum State state)
+{
+    QMutexLocker locker(&mMutex);
+    signalLocked(state);
+}
+
+void
+VLCKWallet::interrupted(void *data)
+{
+    VLCKWallet *self = (VLCKWallet *) data;
+
+    self->signal(STATE_KWALLET_CLOSED);
+}
+
+/**
+ * Interruptible wait for KWallet opening (or for an error)
+ */
+bool
+VLCKWallet::waitOpened()
+{
+    QMutexLocker locker(&mMutex);
+
+    vlc_interrupt_register(interrupted, this);
+
+    while (mState == STATE_INIT)
+        mCond.wait(&mMutex);
+
+    vlc_interrupt_unregister();
+
+    return mState == STATE_KWALLET_OPENED;
+}
+
+/**
+ * Slot called when KWallet is opened
+ */
+void
+VLCKWallet::kwalletOpen(bool opened)
+{
+    QMutexLocker locker(&mMutex);
+
+    if (mState != STATE_INIT)
+        return;
+
+    if (opened)
+    {
+        /* Create VLC folder if it doesn't exist */
+        if (!mWallet->hasFolder(VLC_KEYSTORE_NAME))
+        {
+            if (!mWallet->createFolder(VLC_KEYSTORE_NAME))
+            {
+                msg_Err(mObj, "could not create '%s' folder'", VLC_KEYSTORE_NAME);
+                signalLocked(STATE_KWALLET_CLOSED);
+                return;
+            }
+        }
+        /* set VLC folder */
+        if (!mWallet->setFolder(VLC_KEYSTORE_NAME))
+        {
+            signalLocked(STATE_KWALLET_CLOSED);
+            return;
+        }
+        signalLocked(STATE_KWALLET_OPENED);
+    }
+    else
+        signalLocked(STATE_KWALLET_CLOSED);
+}
+
+/**
+ * Slot called when KWallet is closed
+ */
+void
+VLCKWallet::kwalletClose()
+{
+    msg_Err(mObj, "VLCKWallet::kwalletClose\n");
+    signal(STATE_KWALLET_CLOSED);
+}
+
+/**
+ * Open from a VLC thread
+ * Returns true if case of success.
+ */
+bool
+VLCKWallet::open(bool b_force)
+{
+    /* Open from the mainloop, KWallet and Dbus methods need to be run from the
+     * mainloop */
+    if (!connect(this, SIGNAL(opened(bool)), this, SLOT(mainloopOpen(bool)),
+                 Qt::QueuedConnection))
+        return false;
+    emit opened(b_force);
+
+    bool b_opened = waitOpened();
+    disconnect(this, SIGNAL(opened(bool)), this, SLOT(mainloopOpen(bool)));
+
+    if (!b_opened)
+        return false;
+
+    if (!connect(this, SIGNAL(closed(void)), this, SLOT(mainloopClose(void)),
+                 Qt::QueuedConnection))
+        return false;
+
+    /* Theses slots will be executed on the mainloop and will be blocking */
+
+    if (!connect(this, SIGNAL(stored(const char * const *,
+                 const uint8_t *, size_t, const char *, int &)),
+                 this, SLOT(mainloopStore(const char * const *,
+                 const uint8_t *, size_t, const char *, int &)),
+                 Qt::BlockingQueuedConnection))
+        return false;
+
+    if (!connect(this, SIGNAL(found(const char * const *,
+                 vlc_keystore_entry **, unsigned int &)),
+                 this, SLOT(mainloopFind(const char * const *,
+                 vlc_keystore_entry **, unsigned int &)),
+                 Qt::BlockingQueuedConnection))
+        return false;
+
+    if (!connect(this, SIGNAL(removed(const char * const *, unsigned int &)),
+                 this, SLOT(mainloopRemove(const char * const *, unsigned int &)),
+                 Qt::BlockingQueuedConnection))
+        return false;
+
+    return true;
+}
+
+/**
+ * Open from the main loop
+ */
+void
+VLCKWallet::mainloopOpen(bool b_force)
+{
+    QMutexLocker locker(&mMutex);
+
+    if (mState != STATE_INIT)
+        return;
+
+    if (!b_force)
+    {
+        /* First, check if kwallet service is running. Indeed,
+         * KWallet::Wallet::openWallet() will spawn a service if it's not
+         * running, even on non KDE environments */
+        bool b_registered = false;
+        QDBusConnectionInterface *intf = QDBusConnection::sessionBus().interface();
+
+        for (int i = 0; i < kwalletServices.size(); ++i)
+        {
+            if (intf->isServiceRegistered(kwalletServices.at(i)))
+            {
+                b_registered = true;
+                break;
+            }
+        }
+        if (!b_registered)
+        {
+            signalLocked(STATE_KWALLET_CLOSED);
+            return;
+        }
+    }
+
+    mWallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(),
+                                          0, KWallet::Wallet::Asynchronous);
+    if (!mWallet)
+    {
+        msg_Err(mObj, "openWallet failed");
+        signalLocked(STATE_KWALLET_CLOSED);
+        return;
+    }
+    mWallet->setParent(this);
+
+    /* Connect KWallet signals */
+    if (!connect(mWallet, SIGNAL(walletOpened(bool)),
+                 this, SLOT(kwalletOpen(bool))))
+    {
+        msg_Err(mObj, "could not connect to walletOpened");
+        signalLocked(STATE_KWALLET_CLOSED);
+        return;
+    }
+    if (!connect(mWallet, SIGNAL(walletClosed()), this, SLOT(kwalletClose())))
+    {
+        msg_Err(mObj, "could not connect to walletClosed");
+        signalLocked(STATE_KWALLET_CLOSED);
+        return;
+    }
+}
+
+/**
+ * Close from a VLC thread
+ */
+void
+VLCKWallet::close()
+{
+    signal(STATE_KWALLET_CLOSED);
+    emit closed();
+}
+
+/**
+ * Close from the mainloop
+ */
+void
+VLCKWallet::mainloopClose(void)
+{
+    /* delete VLCKWallet, and its child (mWallet) */
+    delete this;
+}
+
+/**
+ * Store from a VLC thread
+ */
+int
+VLCKWallet::store(const char * const ppsz_values[KEY_MAX],
+                  const uint8_t *p_secret, size_t i_secret_len,
+                  const char *psz_label)
+{
+    int i_ret = VLC_EGENERIC;
+    emit stored(ppsz_values, p_secret, i_secret_len, psz_label, i_ret);
+    return i_ret;
+}
+
+/**
+ * Store from the mainloop
+ */
+void
+VLCKWallet::mainloopStore(const char * const *ppsz_values,
+                          const uint8_t *p_secret, size_t i_secret_len,
+                          const char *psz_label, int &i_ret)
+{
+    (void) psz_label;
+
+    QMutexLocker locker(&mMutex);
+    if (mState != STATE_KWALLET_OPENED)
+        return;
+
+    /* Get key and map from values */
+    QString key = values2Key(ppsz_values);
+    if (key.isEmpty())
+        return;
+
+    QMap<QString, QString> map = values2Map(ppsz_values);
+
+    /* Encode secret, since KWallet can't store binary */
+    char *psz_b64_secret = vlc_b64_encode_binary(p_secret, i_secret_len);
+    if (!psz_b64_secret)
+        return;
+    /* Write the secret into the map */
+    map.insert(MAP_KEY_SECRET, QString(psz_b64_secret));
+    free(psz_b64_secret);
+
+    /* Write the map at the specified key */
+    if (mWallet->writeMap(key, map) != 0)
+        return;
+
+    i_ret = VLC_SUCCESS;
+}
+
+/**
+ * Find from a VLC thread
+ */
+unsigned int
+VLCKWallet::find(const char * const ppsz_values[KEY_MAX],
+                 vlc_keystore_entry **pp_entries)
+{
+    unsigned int i_entry_count = 0;
+    emit found(ppsz_values, pp_entries, i_entry_count);
+    return i_entry_count;
+}
+
+/**
+ * Find from the mainloop
+ */
+void
+VLCKWallet::mainloopFind(const char * const *ppsz_values,
+                         vlc_keystore_entry **pp_entries,
+                         unsigned int &i_entry_count)
+{
+    QMutexLocker locker(&mMutex);
+    if (mState != STATE_KWALLET_OPENED)
+        return;
+
+    /* Get map from values */
+    QMap<QString, QString> matchMap = values2Map(ppsz_values);
+
+    /* Fetch all maps */
+    QMap<QString, QMap<QString, QString>> mapMap;
+    if (mWallet->readMapList(QString("*"), mapMap) != 0)
+        return;
+
+    vlc_keystore_entry *p_entries = (vlc_keystore_entry *)
+        calloc(mapMap.size(), sizeof(vlc_keystore_entry));
+
+    QMapIterator<QString, QMap<QString, QString>> it(mapMap);
+    while (it.hasNext())
+    {
+        it.next();
+
+        if (!matchKey(it.key(), ppsz_values) || !matchMaps(matchMap, it.value()))
+            continue;
+
+        /* Matching key/value */
+        vlc_keystore_entry *p_entry = &p_entries[i_entry_count++];
+
+        /* Fill the entry from the map and the key */
+        if (mapAndKey2Entry(it.value(), it.key(), p_entry))
+        {
+            vlc_keystore_release_entries(p_entries, i_entry_count);
+            i_entry_count = 0;
+            return;
+        }
+    }
+    *pp_entries = p_entries;
+}
+
+/**
+ * Remove from a VLC thread
+ */
+unsigned int
+VLCKWallet::remove(const char * const ppsz_values[KEY_MAX])
+{
+    unsigned int i_entry_count = 0;
+    emit removed(ppsz_values, i_entry_count);
+    return i_entry_count;
+}
+
+/**
+ * Remove from the mainloop
+ */
+void
+VLCKWallet::mainloopRemove(const char * const *ppsz_values,
+                           unsigned int &i_entry_count)
+{
+    QMutexLocker locker(&mMutex);
+    if (mState != STATE_KWALLET_OPENED)
+        return;
+
+    QMap<QString, QString> matchMap = values2Map(ppsz_values);
+
+    /* Fetch all maps */
+    QMap<QString, QMap<QString, QString>> mapMap;
+    if (mWallet->readMapList("*", mapMap) != 0)
+        return;
+
+    QMapIterator<QString, QMap<QString, QString>> it(mapMap);
+    while (it.hasNext())
+    {
+        it.next();
+
+        if (!matchKey(it.key(), ppsz_values) || !matchMaps(matchMap, it.value()))
+            continue;
+
+        /* Matching key/value */
+        if (mWallet->removeEntry(it.key()) == 0)
+            i_entry_count++;
+    }
+}
+
+static int
+Store(vlc_keystore *p_keystore, const char * const ppsz_values[KEY_MAX],
+      const uint8_t *p_secret, size_t i_secret_len, const char *psz_label)
+{
+    VLCKWallet *p_wallet = (VLCKWallet *) p_keystore->p_sys;
+
+    return p_wallet->store(ppsz_values, p_secret, i_secret_len, psz_label);
+}
+
+static unsigned int
+Find(vlc_keystore *p_keystore, const char * const ppsz_values[KEY_MAX],
+     vlc_keystore_entry **pp_entries)
+{
+    VLCKWallet *p_wallet = (VLCKWallet *) p_keystore->p_sys;
+
+    return p_wallet->find(ppsz_values, pp_entries);
+}
+
+static unsigned int
+Remove(vlc_keystore *p_keystore, const char * const ppsz_values[KEY_MAX])
+{
+    VLCKWallet *p_wallet = (VLCKWallet *) p_keystore->p_sys;
+
+    return p_wallet->remove(ppsz_values);
+}
+
+static int
+Open(vlc_object_t *p_this)
+{
+    /* KWallet and DBus methods need to be run from the QApplication main loop.
+     * There can be only one QApplication and it's currently used by the qt
+     * interface.
+     * TODO: Spawn the Qt thread singleton from here or from the qt interface
+     * module */
+    QCoreApplication *p_qApp = QCoreApplication::instance();
+    if (p_qApp == NULL)
+        return VLC_EGENERIC;
+
+    VLCKWallet *p_wallet = new VLCKWallet(p_qApp, p_this);
+    if (!p_wallet)
+        return VLC_EGENERIC;
+
+    if (!p_wallet->open(p_this->b_force))
+    {
+        p_wallet->close();
+        return VLC_EGENERIC;
+    }
+
+    vlc_keystore *p_keystore = (vlc_keystore *)p_this;
+    p_keystore->p_sys = (vlc_keystore_sys *) p_wallet;
+    p_keystore->pf_store = Store;
+    p_keystore->pf_find = Find;
+    p_keystore->pf_remove = Remove;
+
+    return VLC_SUCCESS;
+}
+
+static void
+Close(vlc_object_t *p_this)
+{
+    vlc_keystore *p_keystore = (vlc_keystore *)p_this;
+    VLCKWallet *p_wallet = (VLCKWallet *) p_keystore->p_sys;
+    p_wallet->close();
+}
diff --git a/modules/keystore/kwallet.hpp b/modules/keystore/kwallet.hpp
new file mode 100644
index 0000000..f0f0694
--- /dev/null
+++ b/modules/keystore/kwallet.hpp
@@ -0,0 +1,86 @@
+/*****************************************************************************
+ * kwallet.hpp: KWallet keystore module
+ *****************************************************************************
+ * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_keystore.h>
+
+#include <kwallet.h>
+#include <QCoreApplication>
+#include <QObject>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QWaitCondition>
+
+class VLCKWallet : public QObject
+{
+    Q_OBJECT
+
+public:
+    VLCKWallet(QCoreApplication *, vlc_object_t *);
+    bool open(bool b_force);
+    void close(void);
+    int store(const char * const [KEY_MAX], const uint8_t *, size_t, const char *);
+    unsigned int find(const char * const [KEY_MAX], vlc_keystore_entry **);
+    unsigned int remove(const char * const [KEY_MAX]);
+
+signals:
+    void opened(bool b_force);
+    void closed();
+    void stored(const char * const *, const uint8_t *, size_t,
+                const char *, int &);
+    void found(const char * const *, vlc_keystore_entry **,
+               unsigned int &);
+    void removed(const char * const *, unsigned int &);
+
+
+private slots:
+    void mainloopOpen(bool);
+    void mainloopClose();
+    void mainloopStore(const char * const *, const uint8_t *, size_t,
+                       const char *, int &);
+    void mainloopFind(const char * const *, vlc_keystore_entry **,
+                      unsigned int &);
+    void mainloopRemove(const char * const *, unsigned int &);
+
+    void kwalletOpen(bool);
+    void kwalletClose();
+
+private:
+    enum State {
+        STATE_INIT,
+        STATE_KWALLET_OPENED,
+        STATE_KWALLET_CLOSED,
+    };
+
+    void signalLocked(enum State);
+    void signal(enum State);
+    static void interrupted(void *);
+    bool waitOpened(void);
+
+    enum State              mState;
+    vlc_object_t *          mObj;
+    KWallet::Wallet *       mWallet;
+    QMutex                  mMutex;
+    QWaitCondition          mCond;
+};
-- 
2.1.4



More information about the vlc-devel mailing list