--- a/doc/reference/libpurple/libpurple-docs.xml Thu Nov 05 20:33:08 2020 -0600
+++ b/doc/reference/libpurple/libpurple-docs.xml Thu Nov 05 20:36:51 2020 -0600
@@ -68,6 +68,7 @@
<xi:include href="xml/purpleattentiontype.xml" />
<xi:include href="xml/purplebuddypresence.xml" />
<xi:include href="xml/purplechatuser.xml" />
+ <xi:include href="xml/purplecredentialmanager.xml" /> <xi:include href="xml/purplecredentialprovider.xml" />
<xi:include href="xml/purpleimconversation.xml" />
<xi:include href="xml/purplekeyvaluepair.xml" />
--- a/libpurple/core.c Thu Nov 05 20:33:08 2020 -0600
+++ b/libpurple/core.c Thu Nov 05 20:36:51 2020 -0600
@@ -155,6 +155,7 @@
+ purple_credential_manager_startup(); /* before accounts */ purple_keyring_init(); /* before accounts */
purple_theme_manager_init();
@@ -232,6 +233,7 @@
purple_statuses_uninit();
purple_accounts_uninit();
purple_keyring_uninit(); /* after accounts */
+ purple_credential_manager_shutdown(); /* after accounts */ purple_theme_manager_uninit();
--- a/libpurple/meson.build Thu Nov 05 20:33:08 2020 -0600
+++ b/libpurple/meson.build Thu Nov 05 20:36:51 2020 -0600
@@ -53,6 +53,7 @@
+ 'purplecredentialmanager.c', 'purplecredentialprovider.c',
'purpleimconversation.c',
@@ -138,6 +139,7 @@
+ 'purplecredentialmanager.h', 'purplecredentialprovider.h',
'purpleimconversation.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purplecredentialmanager.c Thu Nov 05 20:36:51 2020 -0600
@@ -0,0 +1,555 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * 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 + * 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 program; if not, see <https://www.gnu.org/licenses/>. +#include <glib/gi18n-lib.h> +#include "purplecredentialmanager.h" +#include "purpleprivate.h" + SIG_PROVIDER_REGISTERED, + SIG_PROVIDER_UNREGISTERED, +static guint signals[N_SIGNALS] = {0, }; + PurpleCredentialProvider *active_provider; +} PurpleCredentialManagerPrivate; + PurpleAccount *account; +} PurpleCredentialManagerCallbackData; +G_DEFINE_TYPE_WITH_PRIVATE(PurpleCredentialManager, purple_credential_manager, +static PurpleCredentialManager *default_manager = NULL; +static PurpleCredentialManagerCallbackData * +purple_credential_manager_callback_data_new(GTask *task, PurpleAccount *account) + PurpleCredentialManagerCallbackData *d = NULL; + d = g_new(PurpleCredentialManagerCallbackData, 1); + d->account = PURPLE_ACCOUNT(g_object_ref(G_OBJECT(account))); +purple_credential_manager_callback_data_free(PurpleCredentialManagerCallbackData * d) + g_object_unref(G_OBJECT(d->account)); +/****************************************************************************** + *****************************************************************************/ +purple_credential_manager_read_password_callback(GObject *obj, + PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj); + PurpleCredentialManagerCallbackData *d = NULL; + gchar *password = NULL; + d = (PurpleCredentialManagerCallbackData *)data; + password = purple_credential_provider_read_password_finish(provider, + g_task_return_error(d->task, error); + g_task_return_pointer(d->task, password, g_free); + /* Clean up our initial reference to the task. */ + g_object_unref(G_OBJECT(d->task)); +purple_credential_manager_write_password_callback(GObject *obj, + PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj); + PurpleCredentialManagerCallbackData *d = NULL; + d = (PurpleCredentialManagerCallbackData *)data; + ret = purple_credential_provider_write_password_finish(provider, d->account, + g_task_return_error(d->task, error); + g_task_return_boolean(d->task, ret); + /* Clean up our initial reference to the task. */ + g_object_unref(G_OBJECT(d->task)); +purple_credential_manager_clear_password_callback(GObject *obj, + PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj); + PurpleCredentialManagerCallbackData *d = NULL; + d = (PurpleCredentialManagerCallbackData *)data; + ret = purple_credential_provider_clear_password_finish(provider, d->account, + g_task_return_error(d->task, error); + g_task_return_boolean(d->task, ret); + /* Clean up our initial reference to the task. */ + g_object_unref(G_OBJECT(d->task)); +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +purple_credential_manager_finalize(GObject *obj) { + PurpleCredentialManager *manager = NULL; + PurpleCredentialManagerPrivate *priv = NULL; + manager = PURPLE_CREDENTIAL_MANAGER(obj); + priv = purple_credential_manager_get_instance_private(manager); + g_clear_pointer(&priv->providers, g_hash_table_destroy); + g_clear_object(&priv->active_provider); + G_OBJECT_CLASS(purple_credential_manager_parent_class)->finalize(obj); +purple_credential_manager_init(PurpleCredentialManager *manager) { + PurpleCredentialManagerPrivate *priv = NULL; + priv = purple_credential_manager_get_instance_private(manager); + priv->providers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, +purple_credential_manager_class_init(PurpleCredentialManagerClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + obj_class->finalize = purple_credential_manager_finalize; + * PurpleCredentialManager::provider-registered: + * @manager: The #PurpleCredentialManager instance. + * @provider: The #PurpleCredentialProvider that was registered. + * Emitted after @provider has been registered in @manager. + signals[SIG_PROVIDER_REGISTERED] = g_signal_new( + G_OBJECT_CLASS_TYPE(klass), + G_STRUCT_OFFSET(PurpleCredentialManagerClass, provider_registered), + PURPLE_TYPE_CREDENTIAL_PROVIDER); + * PurpleCredentialManager::provider-unregistered: + * @manager: The #PurpleCredentialManager instance. + * @provider: The #PurpleCredentialProvider that was unregistered. + * Emitted after @provider has been unregistered from @manager. + signals[SIG_PROVIDER_UNREGISTERED] = g_signal_new( + "provider-unregistered", + G_OBJECT_CLASS_TYPE(klass), + G_STRUCT_OFFSET(PurpleCredentialManagerClass, provider_unregistered), + PURPLE_TYPE_CREDENTIAL_PROVIDER); +/****************************************************************************** + *****************************************************************************/ +purple_credential_manager_startup(void) { + if(default_manager == NULL) { + default_manager = g_object_new(PURPLE_TYPE_CREDENTIAL_MANAGER, NULL); +purple_credential_manager_shutdown(void) { + g_clear_object(&default_manager); +/****************************************************************************** + *****************************************************************************/ +PurpleCredentialManager * +purple_credential_manager_get_default(void) { + return default_manager; +purple_credential_manager_register_provider(PurpleCredentialManager *manager, + PurpleCredentialProvider *provider, + PurpleCredentialManagerPrivate *priv = NULL; + const gchar *id = NULL; + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE); + if(!purple_credential_provider_is_valid(provider, error)) { + /* purple_credential_provider_is_valid sets the error on failure. */ + priv = purple_credential_manager_get_instance_private(manager); + id = purple_credential_provider_get_id(provider); + if(g_hash_table_lookup(priv->providers, id) != NULL) { + g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + _("provider %s is already registered"), id); + g_hash_table_insert(priv->providers, g_strdup(id), g_object_ref(provider)); + g_signal_emit(G_OBJECT(manager), signals[SIG_PROVIDER_REGISTERED], 0, +purple_credential_manager_unregister_provider(PurpleCredentialManager *manager, + PurpleCredentialProvider *provider, + PurpleCredentialManagerPrivate *priv = NULL; + const gchar *id = NULL; + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE); + priv = purple_credential_manager_get_instance_private(manager); + id = purple_credential_provider_get_id(provider); + if(provider == priv->active_provider) { + g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + _("provider %s is currently in use"), id); + if(g_hash_table_remove(priv->providers, id)) { + g_signal_emit(G_OBJECT(manager), signals[SIG_PROVIDER_UNREGISTERED], 0, + g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + _("provider %s is not registered"), id); +purple_credential_manager_set_active_provider(PurpleCredentialManager *manager, + const gchar *id, GError **error) + PurpleCredentialManagerPrivate *priv = NULL; + PurpleCredentialProvider *provider = NULL; + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); + priv = purple_credential_manager_get_instance_private(manager); + /* Unset the active provider if the id is NULL. */ + g_clear_object(&priv->active_provider); + provider = g_hash_table_lookup(priv->providers, id); + if(!PURPLE_IS_CREDENTIAL_PROVIDER(provider)) { + g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + "no credential provider found with id %s", id); + g_set_object(&priv->active_provider, provider); +purple_credential_manager_read_password_async(PurpleCredentialManager *manager, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + PurpleCredentialManagerPrivate *priv = NULL; + PurpleCredentialManagerCallbackData *d = NULL; + g_return_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager)); + g_return_if_fail(PURPLE_IS_ACCOUNT(account)); + priv = purple_credential_manager_get_instance_private(manager); + task = g_task_new(manager, cancellable, callback, data); + d = purple_credential_manager_callback_data_new(task, account); + g_task_set_task_data(task, d, + (GDestroyNotify)purple_credential_manager_callback_data_free); + if(priv->active_provider == NULL) { + error = g_error_new_literal(PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + _("can not read password, no active " + "credential provider")); + g_task_return_error(task, error); + purple_credential_provider_read_password_async(priv->active_provider, + purple_credential_manager_read_password_callback, +purple_credential_manager_read_password_finish(PurpleCredentialManager *manager, + PurpleAccount *account, + PurpleCredentialManagerCallbackData *d = NULL; + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), NULL); + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); + d = g_task_get_task_data(G_TASK(result)); + return g_task_propagate_pointer(d->task, error); +purple_credential_manager_write_password_async(PurpleCredentialManager *manager, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + PurpleCredentialManagerPrivate *priv = NULL; + PurpleCredentialManagerCallbackData *d = NULL; + g_return_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager)); + g_return_if_fail(PURPLE_IS_ACCOUNT(account)); + priv = purple_credential_manager_get_instance_private(manager); + task = g_task_new(manager, cancellable, callback, data); + d = purple_credential_manager_callback_data_new(task, account); + g_task_set_task_data(task, d, + (GDestroyNotify)purple_credential_manager_callback_data_free); + if(priv->active_provider == NULL) { + error = g_error_new_literal(PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + _("can not write password, no active " + "credential provider")); + g_task_return_error(task, error); + purple_credential_provider_write_password_async(priv->active_provider, + purple_credential_manager_write_password_callback, +purple_credential_manager_write_password_finish(PurpleCredentialManager *manager, + PurpleAccount *account, + PurpleCredentialManagerCallbackData *d = NULL; + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE); + d = g_task_get_task_data(G_TASK(result)); + return g_task_propagate_boolean(d->task, error); +purple_credential_manager_clear_password_async(PurpleCredentialManager *manager, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + PurpleCredentialManagerPrivate *priv = NULL; + PurpleCredentialManagerCallbackData *d = NULL; + g_return_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager)); + g_return_if_fail(PURPLE_IS_ACCOUNT(account)); + priv = purple_credential_manager_get_instance_private(manager); + task = g_task_new(manager, cancellable, callback, data); + d = purple_credential_manager_callback_data_new(task, account); + g_task_set_task_data(task, d, + (GDestroyNotify)purple_credential_manager_callback_data_free); + if(priv->active_provider == NULL) { + error = g_error_new_literal(PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + _("can not clear password, no active " + "credential provider")); + g_task_return_error(task, error); + purple_credential_provider_clear_password_async(priv->active_provider, + purple_credential_manager_clear_password_callback, +purple_credential_manager_clear_password_finish(PurpleCredentialManager *manager, + PurpleAccount *account, + PurpleCredentialManagerCallbackData *d = NULL; + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE); + d = g_task_get_task_data(G_TASK(result)); + return g_task_propagate_boolean(d->task, error); +purple_credential_manager_read_settings(PurpleCredentialManager *manager, + PurpleCredentialManagerPrivate *priv = NULL; + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); + priv = purple_credential_manager_get_instance_private(manager); + if(priv->active_provider == NULL) { + g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + _("can not read settings, no active credential provider")); + return purple_credential_provider_read_settings(priv->active_provider); +purple_credential_manager_write_settings(PurpleCredentialManager *manager, + PurpleRequestFields *fields, + PurpleCredentialManagerPrivate *priv = NULL; + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); + priv = purple_credential_manager_get_instance_private(manager); + if(priv->active_provider == NULL) { + g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, + _("can not write settings, no active credential provider")); + return purple_credential_provider_write_settings(priv->active_provider, --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purplecredentialmanager.h Thu Nov 05 20:36:51 2020 -0600
@@ -0,0 +1,287 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * 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 + * 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 program; if not, see <https://www.gnu.org/licenses/>. +#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION) +# error "only <pidgin.h> may be included directly" +#ifndef PURPLE_CREDENTIAL_MANAGER_H +#define PURPLE_CREDENTIAL_MANAGER_H +#include <glib-object.h> +#include <purplecredentialprovider.h> + * SECTION:purplecredentialmanager + * @section_id: libpurple-purplecredentialmanager + * @title: Purple Credential Manager + * Purple Credential Manager is the main API access to different credential + * providers. Providers register themselves with the manager and then the user + * can choose which provider to use. + * Once a provider is selected, all credential access will be directed to that + * PURPLE_CREDENTIAL_MANAGER_DOMAIN: + * A #GError domain for errors from #PurpleCredentialManager. +#define PURPLE_CREDENTIAL_MANAGER_DOMAIN (g_quark_from_static_string("purple-credential-manager")) + * PURPLE_TYPE_CREDENTIAL_MANAGER: + * The standard _get_type macro for #PurpleCredentialManager. +#define PURPLE_TYPE_CREDENTIAL_MANAGER (purple_credential_manager_get_type()) +G_DECLARE_DERIVABLE_TYPE(PurpleCredentialManager, purple_credential_manager, + PURPLE, CREDENTIAL_MANAGER, GObject) + * PurpleCredentialManager: + * An opaque data structure that manages credentials. + * PurpleCredentialManagerClass: + * @provider_registered: The default signal handler for when a provider is + * @provider_unregistered: The default signal handler for when a provider is + * The class structure for #PurpleCredentialProvider. +struct _PurpleCredentialManagerClass { + void (*provider_registered)(PurpleCredentialManager *manager, PurpleCredentialProvider *provider); + void (*provider_unregistered)(PurpleCredentialManager *manager, PurpleCredentialProvider *provider); + * purple_credential_manager_get_default: + * Gets the default #PurpleCredentialManager instance. + * Returns: (transfer none): The default #PurpleCredentialManager instance. +PurpleCredentialManager *purple_credential_manager_get_default(void); + * purple_credential_manager_register_provider: + * @manager: The #PurpleCredentialManager instance. + * @provider: The #PurpleCredentialProvider to register. + * @error: (out) (optional) (nullable): A return address for a #GError. + * Registers @provider with @manager. + * Returns: %TRUE if @provider was successfully registered with @manager, %FALSE +gboolean purple_credential_manager_register_provider(PurpleCredentialManager *manager, PurpleCredentialProvider *provider, GError **error); + * purple_credential_manager_unregister_provider: + * @manager: The #PurpleCredentialManager instance. + * @provider: The #PurpleCredentialProvider to unregister. + * @error: (out) (optional) (nullable): A return address for a #GError. + * Unregisters @provider from @manager. + * Returns: %TRUE if @provider was successfully unregistered from @provider, +gboolean purple_credential_manager_unregister_provider(PurpleCredentialManager *manager, PurpleCredentialProvider *provider, GError **error); + * purple_credential_manager_set_active_provider: + * @manager: The #PurpleCredentialManager instance. + * @id: The id of the #PurpleCredentialProvider to use or %NULL to disable the + * @error: (out) (optional) (nullable): A return address for a #GError. + * Changes the active #PurpleCredentialProvider of @manager to provider with an + * Returns: %TRUE on success or %FALSE with @error set on failure. +gboolean purple_credential_manager_set_active_provider(PurpleCredentialManager *manager, const gchar *id, GError **error); + * purple_credential_manager_read_password_async: + * @manager: The #PurpleCredentialManager instance. + * @account: The #PurpleAccount whose password to read. + * @cancellable: (nullable): optional GCancellable object, %NULL to ignore. + * @callback: (scope async): a #GAsyncReadyCallback to call when the request is + * @data: User data to pass to @callback. + * Reads the password for @account using the active #PurpleCredentialProvider of +void purple_credential_manager_read_password_async(PurpleCredentialManager *manager, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data); + * purple_credential_manager_read_password_finish: + * @manager: The #PurpleCredentialManager instance. + * @account: The #PurpleAccount whose password we're looking up. + * @result: The #GAsyncResult from the previous + * purple_credential_manager_read_password_async() call. + * @error: (out) (optional): Return address for a #GError. + * Finishes a previous call to purple_credential_manager_read_password_async(). + * Returns: (transfer full): The password or %NULL if successful, otherwise + * %NULL with @error set on failure. +gchar *purple_credential_manager_read_password_finish(PurpleCredentialManager *manager, PurpleAccount *account, GAsyncResult *result, GError **error); + * purple_credential_manager_write_password_async: + * @manager: The #PurpleCredentialManager instance. + * @account: The #PurpleAccount whose password to write. + * @password: The password to write. + * @cancellable: (nullable): optional GCancellable object, %NULL to ignore. + * @callback: (scope async): a #GAsyncReadyCallback to call when the request is + * @data: User data to pass to @callback. + * Writes @password for @account to the active #PurpleCredentialProvider of +void purple_credential_manager_write_password_async(PurpleCredentialManager *manager, PurpleAccount *account, const gchar *password, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data); + * purple_credential_manager_write_password_finish: + * @manager: The #PurpleCredentialManager instance. + * @account: The #PurpleAccount whose password we're writing. + * @result: The #GAsyncResult from the previous + * purple_credential_provider_write_password_async() call. + * @error: (out) (optional) (nullable): Return address for a #GError. + * Finishes a previous call to purple_credential_manager_write_password_async(). + * Returns: %TRUE if the password was written successfully, otherwise %FALSE +gboolean purple_credential_manager_write_password_finish(PurpleCredentialManager *manager, PurpleAccount *account, GAsyncResult *result, GError **error); + * purple_credential_manager_clear_password_async: + * @manager: The #PurpleCredentialManager instance. + * @account: The #PurpleAccount whose password to clear. + * @cancellable: (nullable): optional #GCancellable object, or %NULL to ignore. + * @callback: (scope async): a #GAsyncReadyCallback to call when the request is + * @data: User data to pass to @callback. + * Clears the password for @account from the active #PurpleCredentialProvider +void purple_credential_manager_clear_password_async(PurpleCredentialManager *manager, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data); + * purple_credential_manager_clear_password_finish: + * @manager: The #PurpleCredentialManager instance. + * @account: The #PurpleAccount whose password we're clearing. + * @result: The #GAsyncResult from the previous + * purple_credential_provider_clear_password_async() call. + * @error: (out) (optional) (nullable): Return address for a #GError. + * Finishes a previous call to + * purple_credential_provider_clear_password_async(). + * Returns: %TRUE if the password was cleared successfully, otherwise %FALSE +gboolean purple_credential_manager_clear_password_finish(PurpleCredentialManager *manager, PurpleAccount *account, GAsyncResult *result, GError **error); + * purple_credential_manager_read_settings: + * @manager: The #PurpleCredentialManagerInstance. + * @error: (out) (optional) (nullable): A return address for a #GError. + * Reads settings from the active #PurpleCredentialProvider of @manager. + * Returns: (transfer full): New copy of current settings which must be free'd + * with purple_request_fields_destroy(). +PurpleRequestFields *purple_credential_manager_read_settings(PurpleCredentialManager *manager, GError **error); + * purple_credential_manager_write_settings: + * @provider: The #PurpleCredentialManager instance. + * @fields: (transfer full): Modified settings from + * purple_credential_manager_read_settings(). + * @error: (out) (optional) (nullable): A return address for a #GError. + * Write @fields to the active #PurpleCredentialProvider of @manager. + * Returns: %TRUE if successful, %FALSE otherwise. +gboolean purple_credential_manager_write_settings(PurpleCredentialManager *manager, PurpleRequestFields *fields, GError **error); +#endif /* PURPLE_CREDENTIAL_MANAGER_H */ --- a/libpurple/purplecredentialprovider.h Thu Nov 05 20:33:08 2020 -0600
+++ b/libpurple/purplecredentialprovider.h Thu Nov 05 20:36:51 2020 -0600
@@ -128,11 +128,11 @@
* purple_credential_provider_is_valid:
* @provider: The #PurpleCredentialProvider instance.
- * @error: (out) (optional): A return address for a #GError.
+ * @error: (out) (optional) (nullable): Return address for a #GError. * Checks whether or not @provider is setup correctly. This is primarily meant
- * for #purple_keyring_register to call to avoid programming errors, but can be
+ * for #purple_credential_provider_register_provider to call to avoid + * programming errors, but can be used by anyone. * Returns: %FALSE on error, otherwise %TRUE.
@@ -144,8 +144,9 @@
* purple_credential_provider_read_password_async:
* @provider: The #PurpleCredentialProvider instance.
* @account: The #PurpleAccount whose password to read.
- * @cancellable: (nullable): optional GCancellable object, NULL to ignore.
- * @callback: (scope async): a GAsyncReadyCallback to call when the request is satisfied.
+ * @cancellable: (nullable): optional GCancellable object, %NULL to ignore. + * @callback: (scope async): a #GAsyncReadyCallback to call when the request is * @data: User data to pass to @callback.
* Reads the password for @account from @provider.
@@ -160,7 +161,7 @@
* @account: The #PurpleAccount whose password we're looking up.
* @result: The #GAsyncResult from the previous
* purple_credential_provider_read_password_async() call.
- * @error: (out) (optional): Return address for a #GError.
+ * @error: (out) (optional) (nullable): Return address for a #GError. * Finishes a previous call to purple_credential_provider_read_password_async().
@@ -176,7 +177,7 @@
* @provider: The #PurpleCredentialProvider instance.
* @account: The #PurpleAccount whose password to write.
* @password: The password to write.
- * @cancellable: (nullable): optional GCancellable object, NULL to ignore.
+ * @cancellable: (nullable): optional GCancellable object, %NULL to ignore. * @callback: (scope async): a #GAsyncReadyCallback to call when the request is
* @data: User data to pass to @callback.
@@ -193,7 +194,7 @@
* @account: The #PurpleAccount whose password we're writing.
* @result: The #GAsyncResult from the previous
* purple_credential_provider_write_password_async() call.
- * @error: (out) (optional): Return address for a #GError.
+ * @error: (out) (optional) (nullable): Return address for a #GError. * Finishes a previous call to
* purple_credential_provider_write_password_async().
@@ -226,7 +227,7 @@
* @account: The #PurpleAccount whose password we're clearing.
* @result: The #GAsyncResult from the previous
* purple_credential_provider_clear_password_async() call.
- * @error: (out) (optional): Return address for a #GError.
+ * @error: (out) (optional) (nullable): Return address for a #GError. * Finishes a previous call to
* purple_credential_provider_clear_password_async().
@@ -246,6 +247,8 @@
* or close a file to save memory.
void purple_credential_provider_close(PurpleCredentialProvider *provider);
--- a/libpurple/purpleprivate.h Thu Nov 05 20:33:08 2020 -0600
+++ b/libpurple/purpleprivate.h Thu Nov 05 20:36:51 2020 -0600
@@ -206,6 +206,20 @@
_purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *msg);
+ * purple_credential_manager_startup: + * Starts up the credential manager by creating the default instance. +void purple_credential_manager_startup(void); + * purple_credential_manager_shutdown: + * Shuts down the credential manager by destroying the default instance. +void purple_credential_manager_shutdown(void); #endif /* PURPLE_PRIVATE_H */
--- a/libpurple/tests/meson.build Thu Nov 05 20:33:08 2020 -0600
+++ b/libpurple/tests/meson.build Thu Nov 05 20:36:51 2020 -0600
@@ -2,6 +2,7 @@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_credential_manager.c Thu Nov 05 20:36:51 2020 -0600
@@ -0,0 +1,765 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * This library 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 of the License, or (at your option) any later version. + * This library 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 library; if not, see <https://www.gnu.org/licenses/>. +/****************************************************************************** + *****************************************************************************/ +/* Since we're using GTask to test asynchronous functions, we need to use a +static GMainLoop *loop = NULL; +/****************************************************************************** + *****************************************************************************/ +test_purple_credential_manager_timeout_cb(gpointer data) { + g_main_loop_quit((GMainLoop *)data); + g_warning("timed out waiting for the callback function to be called"); +/****************************************************************************** + * TestPurpleCredentialProvider Implementation + *****************************************************************************/ +#define TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER (test_purple_credential_provider_get_type()) +G_DECLARE_FINAL_TYPE(TestPurpleCredentialProvider, + test_purple_credential_provider, + TEST_PURPLE, CREDENTIAL_PROVIDER, + PurpleCredentialProvider) +struct _TestPurpleCredentialProvider { + PurpleCredentialProvider parent; + PurpleRequestFields *fields; +G_DEFINE_TYPE(TestPurpleCredentialProvider, + test_purple_credential_provider, + PURPLE_TYPE_CREDENTIAL_PROVIDER) +test_purple_credential_provider_read_password_async(PurpleCredentialProvider *p, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + task = g_task_new(p, cancellable, callback, data); + g_task_return_pointer(task, g_strdup("password"), g_free); +test_purple_credential_provider_read_password_finish(PurpleCredentialProvider *p, + PurpleAccount *account, + g_return_val_if_fail(g_task_is_valid(result, p), NULL); + return g_task_propagate_pointer(G_TASK(result), error); +test_purple_credential_provider_write_password_async(PurpleCredentialProvider *p, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + task = g_task_new(p, cancellable, callback, data); + g_task_return_boolean(task, TRUE); +test_purple_credential_provider_write_password_finish(PurpleCredentialProvider *p, + PurpleAccount *account, + g_return_val_if_fail(g_task_is_valid(result, p), FALSE); + return g_task_propagate_boolean(G_TASK(result), error); +test_purple_credential_provider_clear_password_async(PurpleCredentialProvider *p, + PurpleAccount *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + task = g_task_new(p, cancellable, callback, data); + g_task_return_boolean(task, TRUE); +test_purple_credential_provider_clear_password_finish(PurpleCredentialProvider *p, + PurpleAccount *account, + g_return_val_if_fail(g_task_is_valid(result, p), FALSE); + return g_task_propagate_boolean(G_TASK(result), error); +static PurpleRequestFields * +test_purple_credential_provider_read_settings(PurpleCredentialProvider *p) { + return purple_request_fields_new(); +test_purple_credential_provider_write_settings(PurpleCredentialProvider *p, + PurpleRequestFields *fields) + TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p); +test_purple_credential_provider_init(TestPurpleCredentialProvider *provider) { +test_purple_credential_provider_class_init(TestPurpleCredentialProviderClass *klass) + PurpleCredentialProviderClass *provider_class = PURPLE_CREDENTIAL_PROVIDER_CLASS(klass); + provider_class->read_password_async = test_purple_credential_provider_read_password_async; + provider_class->read_password_finish = test_purple_credential_provider_read_password_finish; + provider_class->write_password_async = test_purple_credential_provider_write_password_async; + provider_class->write_password_finish = test_purple_credential_provider_write_password_finish; + provider_class->clear_password_async = test_purple_credential_provider_clear_password_async; + provider_class->clear_password_finish = test_purple_credential_provider_clear_password_finish; + provider_class->read_settings = test_purple_credential_provider_read_settings; + provider_class->write_settings = test_purple_credential_provider_write_settings; +static PurpleCredentialProvider * +test_purple_credential_provider_new(void) { + TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER, + "name", "Test Provider", +/****************************************************************************** + *****************************************************************************/ +test_purple_credential_manager_get_default(void) { + PurpleCredentialManager *manager1 = NULL, *manager2 = NULL; + manager1 = purple_credential_manager_get_default(); + g_assert_true(PURPLE_IS_CREDENTIAL_MANAGER(manager1)); + manager2 = purple_credential_manager_get_default(); + g_assert_true(PURPLE_IS_CREDENTIAL_MANAGER(manager2)); + g_assert_true(manager1 == manager2); +/****************************************************************************** + *****************************************************************************/ +test_purple_credential_manager_registration(void) { + PurpleCredentialManager *manager = NULL; + PurpleCredentialProvider *provider = NULL; + manager = purple_credential_manager_get_default(); + g_assert_true(PURPLE_IS_CREDENTIAL_MANAGER(manager)); + provider = test_purple_credential_provider_new(); + /* Register the first time cleanly. */ + r = purple_credential_manager_register_provider(manager, provider, &error); + g_assert_no_error(error); + /* Register again and verify the error. */ + r = purple_credential_manager_register_provider(manager, provider, &error); + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); + /* Unregister the provider. */ + r = purple_credential_manager_unregister_provider(manager, provider, + g_assert_no_error(error); + /* Unregister the provider again and verify the error. */ + r = purple_credential_manager_unregister_provider(manager, provider, + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); + g_clear_object(&provider); +/****************************************************************************** + *****************************************************************************/ +test_purple_credential_manager_set_active_null(void) { + PurpleCredentialManager *manager = NULL; + manager = purple_credential_manager_get_default(); + ret = purple_credential_manager_set_active_provider(manager, NULL, &error); + g_assert_no_error(error); +test_purple_credential_manager_set_active_non_existent(void) { + PurpleCredentialManager *manager = NULL; + manager = purple_credential_manager_get_default(); + ret = purple_credential_manager_set_active_provider(manager, "foo", &error); + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); +test_purple_credential_manager_set_active_normal(void) { + PurpleCredentialManager *manager = NULL; + PurpleCredentialProvider *provider = NULL; + manager = purple_credential_manager_get_default(); + /* Create the provider and register it in the manager. */ + provider = test_purple_credential_provider_new(); + r = purple_credential_manager_register_provider(manager, provider, &error); + g_assert_no_error(error); + /* Set the provider as active and verify it was successful. */ + r = purple_credential_manager_set_active_provider(manager, "test-provider", + g_assert_no_error(error); + /* Verify that unregistering the provider fails. */ + r = purple_credential_manager_unregister_provider(manager, provider, + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); + /* Now unset the active provider. */ + r = purple_credential_manager_set_active_provider(manager, NULL, &error); + g_assert_no_error(error); + /* Finally unregister the provider now that it's no longer active. */ + r = purple_credential_manager_unregister_provider(manager, provider, + g_assert_no_error(error); + /* And our final cleanup. */ + g_clear_object(&provider); +/****************************************************************************** + *****************************************************************************/ +test_purple_credential_manager_no_provider_read_password_cb(GObject *obj, + PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj); + PurpleAccount *account = PURPLE_ACCOUNT(d); + gchar *password = NULL; + password = purple_credential_manager_read_password_finish(manager, account, + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); + g_assert_null(password); + g_clear_object(&account); + g_main_loop_quit(loop); +test_purple_credential_manager_no_provider_read_password_idle(gpointer data) { + PurpleCredentialManager *m = PURPLE_CREDENTIAL_MANAGER(data); + PurpleAccount *account = NULL; + account = purple_account_new("test", "test"); + purple_credential_manager_read_password_async(m, account, NULL, + test_purple_credential_manager_no_provider_read_password_cb, +test_purple_credential_manager_no_provider_read_password_async(void) { + PurpleCredentialManager *m = purple_credential_manager_get_default(); + g_idle_add(test_purple_credential_manager_no_provider_read_password_idle, m); + g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop); +test_purple_credential_manager_no_provider_write_password_cb(GObject *obj, + PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj); + PurpleAccount *account = PURPLE_ACCOUNT(d); + r = purple_credential_manager_write_password_finish(manager, account, res, + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); + g_clear_object(&account); + g_main_loop_quit(loop); +test_purple_credential_manager_no_provider_write_password_idle(gpointer data) { + PurpleCredentialManager *m = PURPLE_CREDENTIAL_MANAGER(data); + PurpleAccount *account = NULL; + account = purple_account_new("test", "test"); + purple_credential_manager_write_password_async(m, account, NULL, NULL, + test_purple_credential_manager_no_provider_write_password_cb, +test_purple_credential_manager_no_provider_write_password_async(void) { + PurpleCredentialManager *m = purple_credential_manager_get_default(); + g_idle_add(test_purple_credential_manager_no_provider_write_password_idle, + g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop); +test_purple_credential_manager_no_provider_clear_password_cb(GObject *obj, + PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj); + PurpleAccount *account = PURPLE_ACCOUNT(d); + r = purple_credential_manager_clear_password_finish(manager, account, res, + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); + g_clear_object(&account); + g_main_loop_quit(loop); +test_purple_credential_manager_no_provider_clear_password_idle(gpointer data) { + PurpleCredentialManager *m = PURPLE_CREDENTIAL_MANAGER(data); + PurpleAccount *account = NULL; + account = purple_account_new("test", "test"); + purple_credential_manager_clear_password_async(m, account, NULL, + test_purple_credential_manager_no_provider_clear_password_cb, +test_purple_credential_manager_no_provider_clear_password_async(void) { + PurpleCredentialManager *m = purple_credential_manager_get_default(); + g_idle_add(test_purple_credential_manager_no_provider_clear_password_idle, + g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop); +test_purple_credential_manager_no_provider_read_settings(void) { + PurpleCredentialManager *manager = NULL; + PurpleRequestFields *fields = NULL; + manager = purple_credential_manager_get_default(); + fields = purple_credential_manager_read_settings(manager, &error); + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); +test_purple_credential_manager_no_provider_write_settings(void) { + PurpleCredentialManager *manager = NULL; + manager = purple_credential_manager_get_default(); + purple_credential_manager_write_settings(manager, NULL, &error); + g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0); +/****************************************************************************** + *****************************************************************************/ +test_purple_credential_manager_read_password_cb(GObject *obj, GAsyncResult *res, + PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj); + PurpleAccount *account = PURPLE_ACCOUNT(d); + gchar *password = NULL; + password = purple_credential_manager_read_password_finish(manager, account, + g_assert_no_error(error); + g_assert_cmpstr(password, ==, "password"); + g_clear_object(&account); + g_main_loop_quit(loop); +test_purple_credential_manager_read_password_idle(gpointer data) { + PurpleCredentialManager *m = PURPLE_CREDENTIAL_MANAGER(data); + PurpleAccount *account = NULL; + account = purple_account_new("test", "test"); + purple_credential_manager_read_password_async(m, account, NULL, + test_purple_credential_manager_read_password_cb, +test_purple_credential_manager_read_password_async(void) { + PurpleCredentialManager *m = purple_credential_manager_get_default(); + PurpleCredentialProvider *p = test_purple_credential_provider_new(); + r = purple_credential_manager_register_provider(m, p, &e); + r = purple_credential_manager_set_active_provider(m, "test-provider", &e); + g_idle_add(test_purple_credential_manager_read_password_idle, m); + g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop); + r = purple_credential_manager_set_active_provider(m, NULL, &e); + r = purple_credential_manager_unregister_provider(m, p, &e); +test_purple_credential_manager_write_password_cb(GObject *obj, + GAsyncResult *res, gpointer d) + PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj); + PurpleAccount *account = PURPLE_ACCOUNT(d); + r = purple_credential_manager_write_password_finish(manager, account, res, + g_assert_no_error(error); + g_clear_object(&account); + g_main_loop_quit(loop); +test_purple_credential_manager_write_password_idle(gpointer data) { + PurpleCredentialManager *m = PURPLE_CREDENTIAL_MANAGER(data); + PurpleAccount *account = NULL; + account = purple_account_new("test", "test"); + purple_credential_manager_write_password_async(m, account, NULL, NULL, + test_purple_credential_manager_write_password_cb, +test_purple_credential_manager_write_password_async(void) { + PurpleCredentialManager *m = purple_credential_manager_get_default(); + PurpleCredentialProvider *p = test_purple_credential_provider_new(); + r = purple_credential_manager_register_provider(m, p, &e); + r = purple_credential_manager_set_active_provider(m, "test-provider", &e); + g_idle_add(test_purple_credential_manager_write_password_idle, m); + g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop); + r = purple_credential_manager_set_active_provider(m, NULL, &e); + r = purple_credential_manager_unregister_provider(m, p, &e); +test_purple_credential_manager_clear_password_cb(GObject *obj, + GAsyncResult *res, gpointer d) + PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj); + PurpleAccount *account = PURPLE_ACCOUNT(d); + r = purple_credential_manager_clear_password_finish(manager, account, res, + g_assert_no_error(error); + g_clear_object(&account); + g_main_loop_quit(loop); +test_purple_credential_manager_clear_password_idle(gpointer data) { + PurpleCredentialManager *m = PURPLE_CREDENTIAL_MANAGER(data); + PurpleAccount *account = NULL; + account = purple_account_new("test", "test"); + purple_credential_manager_clear_password_async(m, account, NULL, + test_purple_credential_manager_clear_password_cb, +test_purple_credential_manager_clear_password_async(void) { + PurpleCredentialManager *m = purple_credential_manager_get_default(); + PurpleCredentialProvider *p = test_purple_credential_provider_new(); + r = purple_credential_manager_register_provider(m, p, &e); + r = purple_credential_manager_set_active_provider(m, "test-provider", &e); + g_idle_add(test_purple_credential_manager_clear_password_idle, m); + g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop); + r = purple_credential_manager_set_active_provider(m, NULL, &e); + r = purple_credential_manager_unregister_provider(m, p, &e); +test_purple_credential_manager_settings(void) { + PurpleCredentialManager *m = purple_credential_manager_get_default(); + PurpleCredentialProvider *p = test_purple_credential_provider_new(); + TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p); + PurpleRequestFields *fields = NULL; + r = purple_credential_manager_register_provider(m, p, &e); + r = purple_credential_manager_set_active_provider(m, "test-provider", &e); + fields = purple_credential_manager_read_settings(m, &e); + g_assert_nonnull(fields); + purple_request_fields_destroy(fields); + fields = purple_request_fields_new(); + r = purple_credential_manager_write_settings(m, fields, &e); + g_assert_true(tp->fields == fields); + purple_request_fields_destroy(fields); +/****************************************************************************** + *****************************************************************************/ +main(gint argc, gchar *argv[]) { + g_test_init(&argc, &argv, NULL); + loop = g_main_loop_new(NULL, FALSE); + g_test_add_func("/credential-manager/get-default", + test_purple_credential_manager_get_default); + g_test_add_func("/credential-manager/registration", + test_purple_credential_manager_registration); + g_test_add_func("/credential-manager/set-active/null", + test_purple_credential_manager_set_active_null); + g_test_add_func("/credential-manager/set-active/non-existent", + test_purple_credential_manager_set_active_non_existent); + g_test_add_func("/credential-manager/set-active/normal", + test_purple_credential_manager_set_active_normal); + g_test_add_func("/credential-manager/no-provider/read-password-async", + test_purple_credential_manager_no_provider_read_password_async); + g_test_add_func("/credential-manager/no-provider/write-password-async", + test_purple_credential_manager_no_provider_write_password_async); + g_test_add_func("/credential-manager/no-provider/clear-password-async", + test_purple_credential_manager_no_provider_clear_password_async); + g_test_add_func("/credential-manager/no-provider/read-settings", + test_purple_credential_manager_no_provider_read_settings); + g_test_add_func("/credential-manager/no-provider/write-settings", + test_purple_credential_manager_no_provider_write_settings); + g_test_add_func("/credential-manager/provider/read-password-async", + test_purple_credential_manager_read_password_async); + g_test_add_func("/credential-manager/provider/write-password-async", + test_purple_credential_manager_write_password_async); + g_test_add_func("/credential-manager/provider/clear-password-async", + test_purple_credential_manager_clear_password_async); + g_test_add_func("/credential-manager/provider/settings", + test_purple_credential_manager_settings); + g_main_loop_unref(loop); --- a/libpurple/tests/test_credential_provider.c Thu Nov 05 20:33:08 2020 -0600
+++ b/libpurple/tests/test_credential_provider.c Thu Nov 05 20:36:51 2020 -0600
@@ -28,7 +28,7 @@
/* Since we're using GTask to test asynchrous functions, we need to use a main
-static GMainLoop *loop = FALSE;
+static GMainLoop *loop = NULL; /******************************************************************************
* TestCredentialProviderEmpty
--- a/po/POTFILES.in Thu Nov 05 20:33:08 2020 -0600
+++ b/po/POTFILES.in Thu Nov 05 20:36:51 2020 -0600
@@ -256,6 +256,7 @@
libpurple/purpleattentiontype.c
libpurple/purplebuddypresence.c
libpurple/purplechatuser.c
+libpurple/purplecredentialmanager.c libpurple/purplecredentialprovider.c
libpurple/purpleimconversation.c