Mon, 15 Nov 2021 02:23:25 -0600
Move the kwallet plugin to the plugins directory and make it autoload
Testing Done:
Ran from the devenv and verified that it autoloaded and was in the new location.
Reviewed at https://reviews.imfreedom.org/r/1125/
| libpurple/plugins/keyrings/kwallet/meson.build | file | annotate | diff | comparison | revisions | |
| libpurple/plugins/keyrings/kwallet/purplekwallet.cpp | file | annotate | diff | comparison | revisions | |
| libpurple/plugins/keyrings/kwallet/purplekwallet.h | file | annotate | diff | comparison | revisions | |
| libpurple/plugins/keyrings/meson.build | file | annotate | diff | comparison | revisions | |
| libpurple/plugins/kwallet/meson.build | file | annotate | diff | comparison | revisions | |
| libpurple/plugins/kwallet/purplekwallet.cpp | file | annotate | diff | comparison | revisions | |
| libpurple/plugins/kwallet/purplekwallet.h | file | annotate | diff | comparison | revisions | |
| libpurple/plugins/meson.build | file | annotate | diff | comparison | revisions | |
| po/POTFILES.in | file | annotate | diff | comparison | revisions |
--- a/libpurple/plugins/keyrings/kwallet/meson.build Mon Nov 15 02:21:18 2021 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -if kwallet.found() - kwallet_moc = qt5.preprocess( - moc_headers: 'purplekwallet.h', - dependencies: qt5_dep, - ) - - kwallet_sources = [ - 'purplekwallet.cpp', - 'purplekwallet.h', - kwallet_moc, - ] - - kwallet_plugin = library('purplekwallet', kwallet_sources, - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-KWalletKeyring"'], - dependencies : [kwallet, qt5_dep, libpurple_dep], - name_prefix : '', - install : true, install_dir : PURPLE_PLUGINDIR) - - devenv.append('PURPLE_PLUGIN_PATH', meson.current_build_dir()) -endif
--- a/libpurple/plugins/keyrings/kwallet/purplekwallet.cpp Mon Nov 15 02:21:18 2021 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,617 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program 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; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this library; if not, see <https://www.gnu.org/licenses/>. - */ - -#include <glib.h> -#include <glib/gi18n-lib.h> - -#include <purple.h> - -#include <QCoreApplication> - -#include <kwallet.h> - -#include "purplekwallet.h" - -/****************************************************************************** - * Globals - *****************************************************************************/ -static QCoreApplication *qCoreApp = NULL; -static PurpleCredentialProvider *instance = NULL; - -#define PURPLE_KWALLET_DOMAIN (g_quark_from_static_string("purple-kwallet")) -#define PURPLE_KWALLET_WALLET_NAME (KWallet::Wallet::NetworkWallet()) - -struct _PurpleKWalletProvider { - PurpleCredentialProvider parent; - - PurpleKWalletPlugin::Engine *engine; -}; - -G_DEFINE_DYNAMIC_TYPE(PurpleKWalletProvider, purple_kwallet_provider, - PURPLE_TYPE_CREDENTIAL_PROVIDER) - -/****************************************************************************** - * Helpers - *****************************************************************************/ -static QString -purple_kwallet_get_ui_name(void) { - PurpleUiInfo *ui_info = NULL; - QString ui_name = NULL; - - ui_info = purple_core_get_ui_info(); - if(PURPLE_IS_UI_INFO(ui_info)) { - ui_name = purple_ui_info_get_name(ui_info); - g_object_unref(G_OBJECT(ui_info)); - } - - if(ui_name.isEmpty()) { - ui_name = "libpurple"; - } - - return ui_name; -} - -static QString -purple_kwallet_provider_account_key(PurpleAccount *account) { - return QString(purple_account_get_protocol_id(account)) + ":" + - purple_account_get_username(account); -} - -/****************************************************************************** - * Request Implementation - *****************************************************************************/ -PurpleKWalletPlugin::Request::Request(QString key, GTask *task) { - this->key = key; - this->task = G_TASK(g_object_ref(G_OBJECT(task))); -} - -PurpleKWalletPlugin::Request::~Request(void) { - g_clear_object(&this->task); -} - -/****************************************************************************** - * ReadRequest Implementation - *****************************************************************************/ -PurpleKWalletPlugin::ReadRequest::ReadRequest(QString key, GTask *task) : PurpleKWalletPlugin::Request(key, task) { -} - -void -PurpleKWalletPlugin::ReadRequest::execute(KWallet::Wallet *wallet) { - QString password; - int result = 0; - bool missing; - - missing = KWallet::Wallet::keyDoesNotExist(PURPLE_KWALLET_WALLET_NAME, - purple_kwallet_get_ui_name(), - key); - - if(missing) { - g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, 0, - "no password stored"); - - g_clear_object(&this->task); - - return; - } - - result = wallet->readPassword(this->key, password); - - if(result != 0) { - g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, result, - _("failed to read password, kwallet responded " - "with error code %d"), result); - } else { - gchar *c_password = g_strdup(password.toUtf8().constData()); - g_task_return_pointer(this->task, c_password, g_free); - } - - g_clear_object(&this->task); -} - -void -PurpleKWalletPlugin::ReadRequest::cancel(QString reason) { - g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, 0, - _("failed to read password: %s"), - reason.toUtf8().constData()); - - g_clear_object(&this->task); -} - -/****************************************************************************** - * WriteRequest Implementation - *****************************************************************************/ -PurpleKWalletPlugin::WriteRequest::WriteRequest(QString key, GTask *task, QString password) : PurpleKWalletPlugin::Request(key, task) { - this->password = password; -} - -void -PurpleKWalletPlugin::WriteRequest::execute(KWallet::Wallet *wallet) { - int result; - - result = wallet->writePassword(this->key, this->password); - - if(result != 0) { - g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, result, - _("failed to write password, kwallet " - "responded with error code %d"), result); - } else { - g_task_return_boolean(this->task, TRUE); - } - - g_clear_object(&this->task); -} - -void -PurpleKWalletPlugin::WriteRequest::cancel(QString reason) { - g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, 0, - _("failed to write password: %s"), - reason.toUtf8().constData()); - - g_clear_object(&this->task); -} - -/****************************************************************************** - * ClearRequest Implementation - *****************************************************************************/ -PurpleKWalletPlugin::ClearRequest::ClearRequest(QString key, GTask *task) : PurpleKWalletPlugin::Request(key, task) { -} - -void -PurpleKWalletPlugin::ClearRequest::execute(KWallet::Wallet *wallet) { - int result; - - result = wallet->removeEntry(this->key); - - if(result != 0) { - g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, result, - _("failed to clear password, kwallet " - "responded with error code %d"), result); - } else { - g_task_return_boolean(this->task, TRUE); - } - - g_clear_object(&this->task); -} - -void -PurpleKWalletPlugin::ClearRequest::cancel(QString reason) { - g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, 0, - _("failed to clear password: %s"), - reason.toUtf8().constData()); - - g_clear_object(&this->task); -} - -/****************************************************************************** - * Engine Implementation - *****************************************************************************/ -PurpleKWalletPlugin::Engine::Engine(void) { - this->queue = QQueue<PurpleKWalletPlugin::Request *>(); - - this->wallet = NULL; - - this->connected = false; - this->failed = false; - this->externallyClosed = false; -} - -PurpleKWalletPlugin::Engine::~Engine(void) { - this->close(); -} - -void -PurpleKWalletPlugin::Engine::open(void) { - purple_debug_misc("kwallet-provider", "attempting to open wallet"); - - if(this->connected) { - purple_debug_misc("kwallet-provider", "wallet already opened"); - - return; - } - - // Reset our externallyClosed and failed states. - this->externallyClosed = false; - this->failed = false; - - // No need to check this pointer as an async open always returns non-null. - this->wallet = KWallet::Wallet::openWallet(PURPLE_KWALLET_WALLET_NAME, - 0, - KWallet::Wallet::Asynchronous); - - this->failed |= !QObject::connect(this->wallet, SIGNAL(walletOpened(bool)), - SLOT(opened(bool))); - this->failed |= !QObject::connect(this->wallet, SIGNAL(walletClosed(void)), - SLOT(closed())); - - if(this->failed) { - purple_debug_error("kwallet-provider", - "Failed to connect KWallet signals"); - } -} - -void -PurpleKWalletPlugin::Engine::close(void) { - while(!this->queue.isEmpty()) { - PurpleKWalletPlugin::Request *request = this->queue.dequeue(); - - request->cancel("wallet is closing"); - - delete request; - } - - if(this->wallet != NULL) { - delete this->wallet; - this->wallet = NULL; - } - - this->connected = false; - this->failed = false; -} - -void -PurpleKWalletPlugin::Engine::enqueue(PurpleKWalletPlugin::Request *request) { - this->queue.enqueue(request); - - processQueue(); -} - -void -PurpleKWalletPlugin::Engine::opened(bool opened) { - QString folder_name; - - if(!opened) { - purple_debug_error("kwallet-provider", "failed to open wallet"); - - delete this->wallet; - this->wallet = NULL; - - this->connected = false; - this->failed = true; - - return; - } - - // Handle the case where the wallet opened signal connected, but the wallet - // closed signal failed to connect. - if(this->failed) { - purple_debug_error("kwallet-provider", - "wallet opened, but failed to connect the wallet " - "closed signal"); - return; - } - - this->connected = true; - - // setup our folder - folder_name = purple_kwallet_get_ui_name(); - if(!this->wallet->hasFolder(folder_name)) { - if(!this->wallet->createFolder(folder_name)) { - purple_debug_error("kwallet-provider", - "failed to create folder %s in wallet.", - folder_name.toUtf8().constData()); - this->failed = true; - } - } - - if(!this->failed && !this->wallet->setFolder(folder_name)) { - purple_debug_error("kwallet-provider", "failed to set folder to %s", - folder_name.toUtf8().constData()); - this->failed = true; - } - - purple_debug_misc("kwallet-provider", "successfully opened the wallet"); - - processQueue(); -} - -void -PurpleKWalletPlugin::Engine::closed(void) { - purple_debug_misc("kwallet-provider", "the wallet was closed externally"); - - this->externallyClosed = true; - this->close(); -} - -void -PurpleKWalletPlugin::Engine::processQueue() { - if(this->externallyClosed && this->queue.isEmpty() == false) { - this->open(); - } else if(this->connected || this->failed) { - while(!this->queue.isEmpty()) { - PurpleKWalletPlugin::Request *request = this->queue.dequeue(); - - if(this->failed) { - request->cancel(_("failed to open kwallet")); - } else { - request->execute(this->wallet); - } - - delete request; - } - } -} - -/****************************************************************************** - * PurpleCredentialProvider Implementation - *****************************************************************************/ -static void -purple_kwallet_provider_activate(PurpleCredentialProvider *provider) { - PurpleKWalletProvider *kwallet_provider = NULL; - - kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); - - kwallet_provider->engine->open(); -} - -static void -purple_kwallet_provider_deactivate(PurpleCredentialProvider *provider) { - PurpleKWalletProvider *kwallet_provider = NULL; - - kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); - - kwallet_provider->engine->close(); -} - -static void -purple_kwallet_read_password_async(PurpleCredentialProvider *provider, - PurpleAccount *account, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data) -{ - PurpleKWalletProvider *kwallet_provider = NULL; - PurpleKWalletPlugin::ReadRequest *request = NULL; - GTask *task = NULL; - QString key; - - key = purple_kwallet_provider_account_key(account); - - task = g_task_new(G_OBJECT(provider), cancellable, callback, data); - - request = new PurpleKWalletPlugin::ReadRequest(key, task); - - kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); - kwallet_provider->engine->enqueue(request); - - g_clear_object(&task); -} - -static gchar * -purple_kwallet_read_password_finish(PurpleCredentialProvider *provider, - GAsyncResult *result, GError **error) -{ - return (gchar *)g_task_propagate_pointer(G_TASK(result), error); -} - -static void -purple_kwallet_write_password_async(PurpleCredentialProvider *provider, - PurpleAccount *account, - const gchar *password, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data) -{ - PurpleKWalletProvider *kwallet_provider = NULL; - PurpleKWalletPlugin::WriteRequest *request = NULL; - GTask *task = NULL; - QString key; - - task = g_task_new(G_OBJECT(provider), cancellable, callback, data); - - key = purple_kwallet_provider_account_key(account); - - request = new PurpleKWalletPlugin::WriteRequest(key, task, password); - - kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); - kwallet_provider->engine->enqueue(request); - - g_clear_object(&task); -} - -static gboolean -purple_kwallet_write_password_finish(PurpleCredentialProvider *provider, - GAsyncResult *result, GError **error) -{ - return g_task_propagate_boolean(G_TASK(result), error); -} - -static void -purple_kwallet_clear_password_async(PurpleCredentialProvider *provider, - PurpleAccount *account, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data) -{ - PurpleKWalletProvider *kwallet_provider = NULL; - PurpleKWalletPlugin::ClearRequest *request = NULL; - GTask *task = NULL; - QString key; - - task = g_task_new(G_OBJECT(provider), cancellable, callback, data); - - key = purple_kwallet_provider_account_key(account); - - request = new PurpleKWalletPlugin::ClearRequest(key, task); - - kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); - kwallet_provider->engine->enqueue(request); - - g_clear_object(&task); -} - -static gboolean -purple_kwallet_clear_password_finish(PurpleCredentialProvider *provider, - GAsyncResult *result, GError **error) -{ - return g_task_propagate_boolean(G_TASK(result), error); -} - -/****************************************************************************** - * GObject Implementation - *****************************************************************************/ -static void -purple_kwallet_provider_dispose(GObject *obj) { - PurpleKWalletProvider *provider = PURPLE_KWALLET_PROVIDER(obj); - - if(provider->engine != NULL) { - provider->engine->close(); - } - - G_OBJECT_CLASS(purple_kwallet_provider_parent_class)->dispose(obj); -} - -static void -purple_kwallet_provider_finalize(GObject *obj) { - PurpleKWalletProvider *provider = PURPLE_KWALLET_PROVIDER(obj); - - if(provider->engine != NULL) { - delete provider->engine; - provider->engine = NULL; - } - - G_OBJECT_CLASS(purple_kwallet_provider_parent_class)->finalize(obj); -} - -static void -purple_kwallet_provider_init(PurpleKWalletProvider *provider) { - provider->engine = new PurpleKWalletPlugin::Engine(); -} - -static void -purple_kwallet_provider_class_init(PurpleKWalletProviderClass *klass) { - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - PurpleCredentialProviderClass *provider_class = NULL; - - provider_class = PURPLE_CREDENTIAL_PROVIDER_CLASS(klass); - - obj_class->dispose = purple_kwallet_provider_dispose; - obj_class->finalize = purple_kwallet_provider_finalize; - - provider_class->activate = purple_kwallet_provider_activate; - provider_class->deactivate = purple_kwallet_provider_deactivate; - provider_class->read_password_async = purple_kwallet_read_password_async; - provider_class->read_password_finish = purple_kwallet_read_password_finish; - provider_class->write_password_async = purple_kwallet_write_password_async; - provider_class->write_password_finish = - purple_kwallet_write_password_finish; - provider_class->clear_password_async = purple_kwallet_clear_password_async; - provider_class->clear_password_finish = - purple_kwallet_clear_password_finish; -} - -static void -purple_kwallet_provider_class_finalize(PurpleKWalletProviderClass *klass) { -} - -/****************************************************************************** - * API - *****************************************************************************/ -static PurpleCredentialProvider * -purple_kwallet_provider_new(void) { - return PURPLE_CREDENTIAL_PROVIDER(g_object_new( - PURPLE_KWALLET_TYPE_PROVIDER, - "id", "kwallet", - "name", _("KWallet"), - "description", _("A credentials management application for the KDE " - "Software Compilation desktop environment"), - NULL - )); -} - -/****************************************************************************** - * Plugin Exports - *****************************************************************************/ -static GPluginPluginInfo * -kwallet_query(G_GNUC_UNUSED GError **error) { - const gchar * const authors[] = { - "Pidgin Developers <devel@pidgin.im>", - NULL - }; - - return GPLUGIN_PLUGIN_INFO(purple_plugin_info_new( - "id", "keyring-kwallet", - "name", N_("KWallet"), - "version", DISPLAY_VERSION, - "category", N_("Keyring"), - "summary", "KWallet Keyring Plugin", - "description", N_("This plugin will store passwords in KWallet."), - "authors", authors, - "website", PURPLE_WEBSITE, - "abi-version", PURPLE_ABI_VERSION, - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL, - NULL - )); -} - -static gboolean -kwallet_load(GPluginPlugin *plugin, GError **error) { - PurpleCredentialManager *manager = NULL; - - purple_kwallet_provider_register_type(G_TYPE_MODULE(plugin)); - - if(qCoreApp == NULL) { - int argc = 0; - qCoreApp = new QCoreApplication(argc, NULL); - qCoreApp->setApplicationName(purple_kwallet_get_ui_name()); - } - - if(!KWallet::Wallet::isEnabled()) { - g_set_error(error, PURPLE_KWALLET_DOMAIN, 0, - "KWallet service is disabled."); - - return FALSE; - } - - manager = purple_credential_manager_get_default(); - - instance = purple_kwallet_provider_new(); - - return purple_credential_manager_register(manager, instance, error); -} - -static gboolean -kwallet_unload(G_GNUC_UNUSED GPluginPlugin *plugin, - G_GNUC_UNUSED gboolean shutdown, - GError **error) -{ - PurpleCredentialManager *manager = NULL; - gboolean ret = FALSE; - - manager = purple_credential_manager_get_default(); - ret = purple_credential_manager_unregister(manager, instance, error); - - if(!ret) { - return ret; - } - - if(qCoreApp != NULL) { - delete qCoreApp; - qCoreApp = NULL; - } - - g_clear_object(&instance); - - return TRUE; -} - -G_BEGIN_DECLS -GPLUGIN_NATIVE_PLUGIN_DECLARE(kwallet) -G_END_DECLS
--- a/libpurple/plugins/keyrings/kwallet/purplekwallet.h Mon Nov 15 02:21:18 2021 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program 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; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this library; if not, see <https://www.gnu.org/licenses/>. - */ - -#include <glib.h> - -#include <purple.h> - -#include <kwallet.h> -#include <QQueue> - -#define PURPLE_KWALLET_TYPE_PROVIDER (purple_kwallet_provider_get_type()) -G_DECLARE_FINAL_TYPE(PurpleKWalletProvider, purple_kwallet_provider, - PURPLE_KWALLET, PROVIDER, PurpleCredentialProvider) - -namespace PurpleKWalletPlugin { - -class Request { -public: - Request(QString key, GTask *task); - virtual ~Request(void); - virtual void execute(KWallet::Wallet *wallet) = 0; - virtual void cancel(QString reason) = 0; -protected: - QString key; - GTask *task; -}; - -class ReadRequest : public Request { -public: - ReadRequest(QString key, GTask *task); - void execute(KWallet::Wallet *wallet); - void cancel(QString reason); -}; - -class WriteRequest : public Request { -public: - WriteRequest(QString key, GTask *task, QString password); - void execute(KWallet::Wallet *wallet); - void cancel(QString reason); -private: - QString password; -}; - -class ClearRequest : public Request { -public: - ClearRequest(QString key, GTask *task); - void execute(KWallet::Wallet *wallet); - void cancel(QString reason); -}; - -class Engine : public QObject { - Q_OBJECT - -public: - Engine(void); - ~Engine(void); - - void open(void); - void close(void); - void enqueue(Request *request); -private slots: - void opened(bool opened); - void closed(void); -private: - void processQueue(void); - - bool connected; - bool externallyClosed; - bool failed; - - KWallet::Wallet *wallet; - - QQueue<Request *> queue; -}; - -}
--- a/libpurple/plugins/keyrings/meson.build Mon Nov 15 02:21:18 2021 -0600 +++ b/libpurple/plugins/keyrings/meson.build Mon Nov 15 02:23:25 2021 -0600 @@ -1,5 +1,3 @@ -subdir('kwallet') - if nettle.found() internalkeyring_plugin = library('internalkeyring', 'internalkeyring.c', c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-InternalKeyring"'],
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/kwallet/meson.build Mon Nov 15 02:23:25 2021 -0600 @@ -0,0 +1,20 @@ +if kwallet.found() + kwallet_moc = qt5.preprocess( + moc_headers: 'purplekwallet.h', + dependencies: qt5_dep, + ) + + kwallet_sources = [ + 'purplekwallet.cpp', + 'purplekwallet.h', + kwallet_moc, + ] + + kwallet_plugin = library('purplekwallet', kwallet_sources, + c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-KWalletKeyring"'], + dependencies : [kwallet, qt5_dep, libpurple_dep], + name_prefix : '', + install : true, install_dir : PURPLE_PLUGINDIR) + + devenv.append('PURPLE_PLUGIN_PATH', meson.current_build_dir()) +endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/kwallet/purplekwallet.cpp Mon Nov 15 02:23:25 2021 -0600 @@ -0,0 +1,618 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program 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; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this library; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include <purple.h> + +#include <QCoreApplication> + +#include <kwallet.h> + +#include "purplekwallet.h" + +/****************************************************************************** + * Globals + *****************************************************************************/ +static QCoreApplication *qCoreApp = NULL; +static PurpleCredentialProvider *instance = NULL; + +#define PURPLE_KWALLET_DOMAIN (g_quark_from_static_string("purple-kwallet")) +#define PURPLE_KWALLET_WALLET_NAME (KWallet::Wallet::NetworkWallet()) + +struct _PurpleKWalletProvider { + PurpleCredentialProvider parent; + + PurpleKWalletPlugin::Engine *engine; +}; + +G_DEFINE_DYNAMIC_TYPE(PurpleKWalletProvider, purple_kwallet_provider, + PURPLE_TYPE_CREDENTIAL_PROVIDER) + +/****************************************************************************** + * Helpers + *****************************************************************************/ +static QString +purple_kwallet_get_ui_name(void) { + PurpleUiInfo *ui_info = NULL; + QString ui_name = NULL; + + ui_info = purple_core_get_ui_info(); + if(PURPLE_IS_UI_INFO(ui_info)) { + ui_name = purple_ui_info_get_name(ui_info); + g_object_unref(G_OBJECT(ui_info)); + } + + if(ui_name.isEmpty()) { + ui_name = "libpurple"; + } + + return ui_name; +} + +static QString +purple_kwallet_provider_account_key(PurpleAccount *account) { + return QString(purple_account_get_protocol_id(account)) + ":" + + purple_account_get_username(account); +} + +/****************************************************************************** + * Request Implementation + *****************************************************************************/ +PurpleKWalletPlugin::Request::Request(QString key, GTask *task) { + this->key = key; + this->task = G_TASK(g_object_ref(G_OBJECT(task))); +} + +PurpleKWalletPlugin::Request::~Request(void) { + g_clear_object(&this->task); +} + +/****************************************************************************** + * ReadRequest Implementation + *****************************************************************************/ +PurpleKWalletPlugin::ReadRequest::ReadRequest(QString key, GTask *task) : PurpleKWalletPlugin::Request(key, task) { +} + +void +PurpleKWalletPlugin::ReadRequest::execute(KWallet::Wallet *wallet) { + QString password; + int result = 0; + bool missing; + + missing = KWallet::Wallet::keyDoesNotExist(PURPLE_KWALLET_WALLET_NAME, + purple_kwallet_get_ui_name(), + key); + + if(missing) { + g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, 0, + "no password stored"); + + g_clear_object(&this->task); + + return; + } + + result = wallet->readPassword(this->key, password); + + if(result != 0) { + g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, result, + _("failed to read password, kwallet responded " + "with error code %d"), result); + } else { + gchar *c_password = g_strdup(password.toUtf8().constData()); + g_task_return_pointer(this->task, c_password, g_free); + } + + g_clear_object(&this->task); +} + +void +PurpleKWalletPlugin::ReadRequest::cancel(QString reason) { + g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, 0, + _("failed to read password: %s"), + reason.toUtf8().constData()); + + g_clear_object(&this->task); +} + +/****************************************************************************** + * WriteRequest Implementation + *****************************************************************************/ +PurpleKWalletPlugin::WriteRequest::WriteRequest(QString key, GTask *task, QString password) : PurpleKWalletPlugin::Request(key, task) { + this->password = password; +} + +void +PurpleKWalletPlugin::WriteRequest::execute(KWallet::Wallet *wallet) { + int result; + + result = wallet->writePassword(this->key, this->password); + + if(result != 0) { + g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, result, + _("failed to write password, kwallet " + "responded with error code %d"), result); + } else { + g_task_return_boolean(this->task, TRUE); + } + + g_clear_object(&this->task); +} + +void +PurpleKWalletPlugin::WriteRequest::cancel(QString reason) { + g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, 0, + _("failed to write password: %s"), + reason.toUtf8().constData()); + + g_clear_object(&this->task); +} + +/****************************************************************************** + * ClearRequest Implementation + *****************************************************************************/ +PurpleKWalletPlugin::ClearRequest::ClearRequest(QString key, GTask *task) : PurpleKWalletPlugin::Request(key, task) { +} + +void +PurpleKWalletPlugin::ClearRequest::execute(KWallet::Wallet *wallet) { + int result; + + result = wallet->removeEntry(this->key); + + if(result != 0) { + g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, result, + _("failed to clear password, kwallet " + "responded with error code %d"), result); + } else { + g_task_return_boolean(this->task, TRUE); + } + + g_clear_object(&this->task); +} + +void +PurpleKWalletPlugin::ClearRequest::cancel(QString reason) { + g_task_return_new_error(this->task, PURPLE_KWALLET_DOMAIN, 0, + _("failed to clear password: %s"), + reason.toUtf8().constData()); + + g_clear_object(&this->task); +} + +/****************************************************************************** + * Engine Implementation + *****************************************************************************/ +PurpleKWalletPlugin::Engine::Engine(void) { + this->queue = QQueue<PurpleKWalletPlugin::Request *>(); + + this->wallet = NULL; + + this->connected = false; + this->failed = false; + this->externallyClosed = false; +} + +PurpleKWalletPlugin::Engine::~Engine(void) { + this->close(); +} + +void +PurpleKWalletPlugin::Engine::open(void) { + purple_debug_misc("kwallet-provider", "attempting to open wallet"); + + if(this->connected) { + purple_debug_misc("kwallet-provider", "wallet already opened"); + + return; + } + + // Reset our externallyClosed and failed states. + this->externallyClosed = false; + this->failed = false; + + // No need to check this pointer as an async open always returns non-null. + this->wallet = KWallet::Wallet::openWallet(PURPLE_KWALLET_WALLET_NAME, + 0, + KWallet::Wallet::Asynchronous); + + this->failed |= !QObject::connect(this->wallet, SIGNAL(walletOpened(bool)), + SLOT(opened(bool))); + this->failed |= !QObject::connect(this->wallet, SIGNAL(walletClosed(void)), + SLOT(closed())); + + if(this->failed) { + purple_debug_error("kwallet-provider", + "Failed to connect KWallet signals"); + } +} + +void +PurpleKWalletPlugin::Engine::close(void) { + while(!this->queue.isEmpty()) { + PurpleKWalletPlugin::Request *request = this->queue.dequeue(); + + request->cancel("wallet is closing"); + + delete request; + } + + if(this->wallet != NULL) { + delete this->wallet; + this->wallet = NULL; + } + + this->connected = false; + this->failed = false; +} + +void +PurpleKWalletPlugin::Engine::enqueue(PurpleKWalletPlugin::Request *request) { + this->queue.enqueue(request); + + processQueue(); +} + +void +PurpleKWalletPlugin::Engine::opened(bool opened) { + QString folder_name; + + if(!opened) { + purple_debug_error("kwallet-provider", "failed to open wallet"); + + delete this->wallet; + this->wallet = NULL; + + this->connected = false; + this->failed = true; + + return; + } + + // Handle the case where the wallet opened signal connected, but the wallet + // closed signal failed to connect. + if(this->failed) { + purple_debug_error("kwallet-provider", + "wallet opened, but failed to connect the wallet " + "closed signal"); + return; + } + + this->connected = true; + + // setup our folder + folder_name = purple_kwallet_get_ui_name(); + if(!this->wallet->hasFolder(folder_name)) { + if(!this->wallet->createFolder(folder_name)) { + purple_debug_error("kwallet-provider", + "failed to create folder %s in wallet.", + folder_name.toUtf8().constData()); + this->failed = true; + } + } + + if(!this->failed && !this->wallet->setFolder(folder_name)) { + purple_debug_error("kwallet-provider", "failed to set folder to %s", + folder_name.toUtf8().constData()); + this->failed = true; + } + + purple_debug_misc("kwallet-provider", "successfully opened the wallet"); + + processQueue(); +} + +void +PurpleKWalletPlugin::Engine::closed(void) { + purple_debug_misc("kwallet-provider", "the wallet was closed externally"); + + this->externallyClosed = true; + this->close(); +} + +void +PurpleKWalletPlugin::Engine::processQueue() { + if(this->externallyClosed && this->queue.isEmpty() == false) { + this->open(); + } else if(this->connected || this->failed) { + while(!this->queue.isEmpty()) { + PurpleKWalletPlugin::Request *request = this->queue.dequeue(); + + if(this->failed) { + request->cancel(_("failed to open kwallet")); + } else { + request->execute(this->wallet); + } + + delete request; + } + } +} + +/****************************************************************************** + * PurpleCredentialProvider Implementation + *****************************************************************************/ +static void +purple_kwallet_provider_activate(PurpleCredentialProvider *provider) { + PurpleKWalletProvider *kwallet_provider = NULL; + + kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); + + kwallet_provider->engine->open(); +} + +static void +purple_kwallet_provider_deactivate(PurpleCredentialProvider *provider) { + PurpleKWalletProvider *kwallet_provider = NULL; + + kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); + + kwallet_provider->engine->close(); +} + +static void +purple_kwallet_read_password_async(PurpleCredentialProvider *provider, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data) +{ + PurpleKWalletProvider *kwallet_provider = NULL; + PurpleKWalletPlugin::ReadRequest *request = NULL; + GTask *task = NULL; + QString key; + + key = purple_kwallet_provider_account_key(account); + + task = g_task_new(G_OBJECT(provider), cancellable, callback, data); + + request = new PurpleKWalletPlugin::ReadRequest(key, task); + + kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); + kwallet_provider->engine->enqueue(request); + + g_clear_object(&task); +} + +static gchar * +purple_kwallet_read_password_finish(PurpleCredentialProvider *provider, + GAsyncResult *result, GError **error) +{ + return (gchar *)g_task_propagate_pointer(G_TASK(result), error); +} + +static void +purple_kwallet_write_password_async(PurpleCredentialProvider *provider, + PurpleAccount *account, + const gchar *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data) +{ + PurpleKWalletProvider *kwallet_provider = NULL; + PurpleKWalletPlugin::WriteRequest *request = NULL; + GTask *task = NULL; + QString key; + + task = g_task_new(G_OBJECT(provider), cancellable, callback, data); + + key = purple_kwallet_provider_account_key(account); + + request = new PurpleKWalletPlugin::WriteRequest(key, task, password); + + kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); + kwallet_provider->engine->enqueue(request); + + g_clear_object(&task); +} + +static gboolean +purple_kwallet_write_password_finish(PurpleCredentialProvider *provider, + GAsyncResult *result, GError **error) +{ + return g_task_propagate_boolean(G_TASK(result), error); +} + +static void +purple_kwallet_clear_password_async(PurpleCredentialProvider *provider, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data) +{ + PurpleKWalletProvider *kwallet_provider = NULL; + PurpleKWalletPlugin::ClearRequest *request = NULL; + GTask *task = NULL; + QString key; + + task = g_task_new(G_OBJECT(provider), cancellable, callback, data); + + key = purple_kwallet_provider_account_key(account); + + request = new PurpleKWalletPlugin::ClearRequest(key, task); + + kwallet_provider = PURPLE_KWALLET_PROVIDER(provider); + kwallet_provider->engine->enqueue(request); + + g_clear_object(&task); +} + +static gboolean +purple_kwallet_clear_password_finish(PurpleCredentialProvider *provider, + GAsyncResult *result, GError **error) +{ + return g_task_propagate_boolean(G_TASK(result), error); +} + +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +static void +purple_kwallet_provider_dispose(GObject *obj) { + PurpleKWalletProvider *provider = PURPLE_KWALLET_PROVIDER(obj); + + if(provider->engine != NULL) { + provider->engine->close(); + } + + G_OBJECT_CLASS(purple_kwallet_provider_parent_class)->dispose(obj); +} + +static void +purple_kwallet_provider_finalize(GObject *obj) { + PurpleKWalletProvider *provider = PURPLE_KWALLET_PROVIDER(obj); + + if(provider->engine != NULL) { + delete provider->engine; + provider->engine = NULL; + } + + G_OBJECT_CLASS(purple_kwallet_provider_parent_class)->finalize(obj); +} + +static void +purple_kwallet_provider_init(PurpleKWalletProvider *provider) { + provider->engine = new PurpleKWalletPlugin::Engine(); +} + +static void +purple_kwallet_provider_class_init(PurpleKWalletProviderClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + PurpleCredentialProviderClass *provider_class = NULL; + + provider_class = PURPLE_CREDENTIAL_PROVIDER_CLASS(klass); + + obj_class->dispose = purple_kwallet_provider_dispose; + obj_class->finalize = purple_kwallet_provider_finalize; + + provider_class->activate = purple_kwallet_provider_activate; + provider_class->deactivate = purple_kwallet_provider_deactivate; + provider_class->read_password_async = purple_kwallet_read_password_async; + provider_class->read_password_finish = purple_kwallet_read_password_finish; + provider_class->write_password_async = purple_kwallet_write_password_async; + provider_class->write_password_finish = + purple_kwallet_write_password_finish; + provider_class->clear_password_async = purple_kwallet_clear_password_async; + provider_class->clear_password_finish = + purple_kwallet_clear_password_finish; +} + +static void +purple_kwallet_provider_class_finalize(PurpleKWalletProviderClass *klass) { +} + +/****************************************************************************** + * API + *****************************************************************************/ +static PurpleCredentialProvider * +purple_kwallet_provider_new(void) { + return PURPLE_CREDENTIAL_PROVIDER(g_object_new( + PURPLE_KWALLET_TYPE_PROVIDER, + "id", "kwallet", + "name", _("KWallet"), + "description", _("A credentials management application for the KDE " + "Software Compilation desktop environment"), + NULL + )); +} + +/****************************************************************************** + * Plugin Exports + *****************************************************************************/ +static GPluginPluginInfo * +kwallet_query(G_GNUC_UNUSED GError **error) { + const gchar * const authors[] = { + "Pidgin Developers <devel@pidgin.im>", + NULL + }; + + return GPLUGIN_PLUGIN_INFO(purple_plugin_info_new( + "id", "keyring-kwallet", + "name", N_("KWallet"), + "version", DISPLAY_VERSION, + "category", N_("Keyring"), + "summary", "KWallet Keyring Plugin", + "description", N_("This plugin will store passwords in KWallet."), + "authors", authors, + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | + PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, + NULL + )); +} + +static gboolean +kwallet_load(GPluginPlugin *plugin, GError **error) { + PurpleCredentialManager *manager = NULL; + + purple_kwallet_provider_register_type(G_TYPE_MODULE(plugin)); + + if(qCoreApp == NULL) { + int argc = 0; + qCoreApp = new QCoreApplication(argc, NULL); + qCoreApp->setApplicationName(purple_kwallet_get_ui_name()); + } + + if(!KWallet::Wallet::isEnabled()) { + g_set_error(error, PURPLE_KWALLET_DOMAIN, 0, + "KWallet service is disabled."); + + return FALSE; + } + + manager = purple_credential_manager_get_default(); + + instance = purple_kwallet_provider_new(); + + return purple_credential_manager_register(manager, instance, error); +} + +static gboolean +kwallet_unload(G_GNUC_UNUSED GPluginPlugin *plugin, + G_GNUC_UNUSED gboolean shutdown, + GError **error) +{ + PurpleCredentialManager *manager = NULL; + gboolean ret = FALSE; + + manager = purple_credential_manager_get_default(); + ret = purple_credential_manager_unregister(manager, instance, error); + + if(!ret) { + return ret; + } + + if(qCoreApp != NULL) { + delete qCoreApp; + qCoreApp = NULL; + } + + g_clear_object(&instance); + + return TRUE; +} + +G_BEGIN_DECLS +GPLUGIN_NATIVE_PLUGIN_DECLARE(kwallet) +G_END_DECLS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/kwallet/purplekwallet.h Mon Nov 15 02:23:25 2021 -0600 @@ -0,0 +1,94 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program 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; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this library; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <glib.h> + +#include <purple.h> + +#include <kwallet.h> +#include <QQueue> + +#define PURPLE_KWALLET_TYPE_PROVIDER (purple_kwallet_provider_get_type()) +G_DECLARE_FINAL_TYPE(PurpleKWalletProvider, purple_kwallet_provider, + PURPLE_KWALLET, PROVIDER, PurpleCredentialProvider) + +namespace PurpleKWalletPlugin { + +class Request { +public: + Request(QString key, GTask *task); + virtual ~Request(void); + virtual void execute(KWallet::Wallet *wallet) = 0; + virtual void cancel(QString reason) = 0; +protected: + QString key; + GTask *task; +}; + +class ReadRequest : public Request { +public: + ReadRequest(QString key, GTask *task); + void execute(KWallet::Wallet *wallet); + void cancel(QString reason); +}; + +class WriteRequest : public Request { +public: + WriteRequest(QString key, GTask *task, QString password); + void execute(KWallet::Wallet *wallet); + void cancel(QString reason); +private: + QString password; +}; + +class ClearRequest : public Request { +public: + ClearRequest(QString key, GTask *task); + void execute(KWallet::Wallet *wallet); + void cancel(QString reason); +}; + +class Engine : public QObject { + Q_OBJECT + +public: + Engine(void); + ~Engine(void); + + void open(void); + void close(void); + void enqueue(Request *request); +private slots: + void opened(bool opened); + void closed(void); +private: + void processQueue(void); + + bool connected; + bool externallyClosed; + bool failed; + + KWallet::Wallet *wallet; + + QQueue<Request *> queue; +}; + +}
--- a/libpurple/plugins/meson.build Mon Nov 15 02:21:18 2021 -0600 +++ b/libpurple/plugins/meson.build Mon Nov 15 02:23:25 2021 -0600 @@ -1,4 +1,5 @@ subdir('keyrings') +subdir('kwallet') subdir('notification-sound') autoaccept = library('autoaccept', 'autoaccept.c',
--- a/po/POTFILES.in Mon Nov 15 02:21:18 2021 -0600 +++ b/po/POTFILES.in Mon Nov 15 02:23:25 2021 -0600 @@ -61,9 +61,9 @@ libpurple/plugins/idle.c libpurple/plugins/joinpart.c libpurple/plugins/keyrings/internalkeyring.c -libpurple/plugins/keyrings/kwallet/purplekwallet.cpp libpurple/plugins/keyrings/secretservice.c libpurple/plugins/keyrings/wincred.c +libpurple/plugins/kwallet/purplekwallet.cpp libpurple/plugins/notification-sound/notification-sound.c libpurple/plugins/psychic.c libpurple/plugins/purple-toast.c