pidgin/pidgin

Add the new PurpleCredentialManager API

2020-11-05, Gary Kramlich
28964322556c
Parents 8d41e1838429
Children 6f198a69ac48
Add the new PurpleCredentialManager API

Create a new PurpleCredentialManager which will eventually replace the PurpleKeyring API.

Testing Done:
Ran unit tests under valgrind.

Reviewed at https://reviews.imfreedom.org/r/172/
--- 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_plugins_init();
+ 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();
purple_xfers_uninit();
purple_proxy_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 @@
'purpleattentiontype.c',
'purplebuddypresence.c',
'purplechatuser.c',
+ 'purplecredentialmanager.c',
'purplecredentialprovider.c',
'purpleimconversation.c',
'purplekeyvaluepair.c',
@@ -138,6 +139,7 @@
'purpleattentiontype.h',
'purplebuddypresence.h',
'purplechatuser.h',
+ 'purplecredentialmanager.h',
'purplecredentialprovider.h',
'purpleimconversation.h',
'purpleattachment.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
+ * 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 program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "purplecredentialmanager.h"
+#include "purpleprivate.h"
+
+enum {
+ SIG_PROVIDER_REGISTERED,
+ SIG_PROVIDER_UNREGISTERED,
+ N_SIGNALS,
+};
+static guint signals[N_SIGNALS] = {0, };
+
+typedef struct {
+ GHashTable *providers;
+
+ PurpleCredentialProvider *active_provider;
+} PurpleCredentialManagerPrivate;
+
+typedef struct {
+ GTask *task;
+ PurpleAccount *account;
+} PurpleCredentialManagerCallbackData;
+
+G_DEFINE_TYPE_WITH_PRIVATE(PurpleCredentialManager, purple_credential_manager,
+ G_TYPE_OBJECT);
+
+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->task = task;
+ d->account = PURPLE_ACCOUNT(g_object_ref(G_OBJECT(account)));
+
+ return d;
+}
+
+static void
+purple_credential_manager_callback_data_free(PurpleCredentialManagerCallbackData * d)
+{
+ g_object_unref(G_OBJECT(d->account));
+ g_free(d);
+}
+
+/******************************************************************************
+ * Async Callbacks
+ *****************************************************************************/
+static void
+purple_credential_manager_read_password_callback(GObject *obj,
+ GAsyncResult *res,
+ gpointer data)
+{
+ PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+ PurpleCredentialManagerCallbackData *d = NULL;
+ GError *error = NULL;
+ gchar *password = NULL;
+
+ d = (PurpleCredentialManagerCallbackData *)data;
+
+ password = purple_credential_provider_read_password_finish(provider,
+ d->account, res,
+ &error);
+
+ if(error != NULL) {
+ g_task_return_error(d->task, error);
+ } else {
+ g_task_return_pointer(d->task, password, g_free);
+ }
+
+ /* Clean up our initial reference to the task. */
+ g_object_unref(G_OBJECT(d->task));
+}
+
+static void
+purple_credential_manager_write_password_callback(GObject *obj,
+ GAsyncResult *res,
+ gpointer data)
+{
+ PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+ PurpleCredentialManagerCallbackData *d = NULL;
+ GError *error = NULL;
+ gboolean ret = FALSE;
+
+ d = (PurpleCredentialManagerCallbackData *)data;
+
+ ret = purple_credential_provider_write_password_finish(provider, d->account,
+ res, &error);
+
+ if(error != NULL) {
+ g_task_return_error(d->task, error);
+ } else {
+ g_task_return_boolean(d->task, ret);
+ }
+
+ /* Clean up our initial reference to the task. */
+ g_object_unref(G_OBJECT(d->task));
+}
+
+static void
+purple_credential_manager_clear_password_callback(GObject *obj,
+ GAsyncResult *res,
+ gpointer data)
+{
+ PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
+ PurpleCredentialManagerCallbackData *d = NULL;
+ GError *error = NULL;
+ gboolean ret = FALSE;
+
+ d = (PurpleCredentialManagerCallbackData *)data;
+
+ ret = purple_credential_provider_clear_password_finish(provider, d->account,
+ res, &error);
+
+ if(error != NULL) {
+ g_task_return_error(d->task, error);
+ } else {
+ g_task_return_boolean(d->task, ret);
+ }
+
+ /* Clean up our initial reference to the task. */
+ g_object_unref(G_OBJECT(d->task));
+}
+
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+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);
+}
+
+static void
+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,
+ g_object_unref);
+}
+
+static void
+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(
+ "provider-registered",
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(PurpleCredentialManagerClass, provider_registered),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ 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_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(PurpleCredentialManagerClass, provider_unregistered),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ PURPLE_TYPE_CREDENTIAL_PROVIDER);
+}
+
+/******************************************************************************
+ * Private API
+ *****************************************************************************/
+void
+purple_credential_manager_startup(void) {
+ if(default_manager == NULL) {
+ default_manager = g_object_new(PURPLE_TYPE_CREDENTIAL_MANAGER, NULL);
+ }
+}
+
+void
+purple_credential_manager_shutdown(void) {
+ g_clear_object(&default_manager);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleCredentialManager *
+purple_credential_manager_get_default(void) {
+ return default_manager;
+}
+
+gboolean
+purple_credential_manager_register_provider(PurpleCredentialManager *manager,
+ PurpleCredentialProvider *provider,
+ GError **error)
+{
+ 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. */
+
+ return FALSE;
+ }
+
+ 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);
+
+ return FALSE;
+ }
+
+ g_hash_table_insert(priv->providers, g_strdup(id), g_object_ref(provider));
+
+ g_signal_emit(G_OBJECT(manager), signals[SIG_PROVIDER_REGISTERED], 0,
+ provider);
+
+ return TRUE;
+}
+
+gboolean
+purple_credential_manager_unregister_provider(PurpleCredentialManager *manager,
+ PurpleCredentialProvider *provider,
+ GError **error)
+{
+ 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);
+
+ return FALSE;
+ }
+
+ if(g_hash_table_remove(priv->providers, id)) {
+ g_signal_emit(G_OBJECT(manager), signals[SIG_PROVIDER_UNREGISTERED], 0,
+ provider);
+
+ return TRUE;
+ }
+
+ g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0,
+ _("provider %s is not registered"), id);
+
+ return FALSE;
+}
+
+gboolean
+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. */
+ if(id == NULL) {
+ g_clear_object(&priv->active_provider);
+
+ return TRUE;
+ }
+
+ 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);
+
+ return FALSE;
+ }
+
+ g_set_object(&priv->active_provider, provider);
+
+ return TRUE;
+}
+
+void
+purple_credential_manager_read_password_async(PurpleCredentialManager *manager,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ PurpleCredentialManagerPrivate *priv = NULL;
+ PurpleCredentialManagerCallbackData *d = NULL;
+ GTask *task = 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) {
+ GError *error = 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);
+
+ return;
+ }
+
+ purple_credential_provider_read_password_async(priv->active_provider,
+ account,
+ cancellable,
+ purple_credential_manager_read_password_callback,
+ d);
+}
+
+gchar *
+purple_credential_manager_read_password_finish(PurpleCredentialManager *manager,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ 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);
+}
+
+void
+purple_credential_manager_write_password_async(PurpleCredentialManager *manager,
+ PurpleAccount *account,
+ const gchar *password,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ PurpleCredentialManagerPrivate *priv = NULL;
+ PurpleCredentialManagerCallbackData *d = NULL;
+ GTask *task = 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) {
+ GError *error = 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);
+
+ return;
+ }
+
+ purple_credential_provider_write_password_async(priv->active_provider,
+ account, password,
+ cancellable,
+ purple_credential_manager_write_password_callback,
+ d);
+}
+
+gboolean
+purple_credential_manager_write_password_finish(PurpleCredentialManager *manager,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ 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);
+}
+
+void
+purple_credential_manager_clear_password_async(PurpleCredentialManager *manager,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ PurpleCredentialManagerPrivate *priv = NULL;
+ PurpleCredentialManagerCallbackData *d = NULL;
+ GTask *task = 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) {
+ GError *error = 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);
+
+ return;
+ }
+
+ purple_credential_provider_clear_password_async(priv->active_provider,
+ account,
+ cancellable,
+ purple_credential_manager_clear_password_callback,
+ d);
+}
+
+gboolean
+purple_credential_manager_clear_password_finish(PurpleCredentialManager *manager,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ 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);
+}
+
+PurpleRequestFields *
+purple_credential_manager_read_settings(PurpleCredentialManager *manager,
+ GError **error)
+{
+ 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 NULL;
+ }
+
+ return purple_credential_provider_read_settings(priv->active_provider);
+}
+
+gboolean
+purple_credential_manager_write_settings(PurpleCredentialManager *manager,
+ PurpleRequestFields *fields,
+ GError **error)
+{
+ 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 FALSE;
+ }
+
+ return purple_credential_provider_write_settings(priv->active_provider,
+ fields);
+}
--- /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
+ * 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 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"
+#endif
+
+#ifndef PURPLE_CREDENTIAL_MANAGER_H
+#define PURPLE_CREDENTIAL_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "account.h"
+#include <purplecredentialprovider.h>
+
+G_BEGIN_DECLS
+
+/**
+ * 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
+ * provider.
+ */
+
+/**
+ * PURPLE_CREDENTIAL_MANAGER_DOMAIN:
+ *
+ * A #GError domain for errors from #PurpleCredentialManager.
+ *
+ * Since: 3.0.0
+ */
+#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.
+ *
+ * Since: 3.0.0
+ */
+
+/**
+ * PurpleCredentialManagerClass:
+ * @provider_registered: The default signal handler for when a provider is
+ * registered.
+ * @provider_unregistered: The default signal handler for when a provider is
+ * unregistered.
+ *
+ * The class structure for #PurpleCredentialProvider.
+ *
+ * Since: 3.0.0
+ */
+struct _PurpleCredentialManagerClass {
+ /*< private >*/
+ GObjectClass parent;
+
+ /*< public >*/
+ void (*provider_registered)(PurpleCredentialManager *manager, PurpleCredentialProvider *provider);
+ void (*provider_unregistered)(PurpleCredentialManager *manager, PurpleCredentialProvider *provider);
+
+ /*< private >*/
+ gpointer reserved[8];
+};
+
+/**
+ * purple_credential_manager_get_default:
+ *
+ * Gets the default #PurpleCredentialManager instance.
+ *
+ * Returns: (transfer none): The default #PurpleCredentialManager instance.
+ *
+ * Since: 3.0.0
+ */
+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
+ * otherwise.
+ *
+ * Since: 3.0.0
+ */
+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,
+ * %FALSE otherwise.
+ *
+ * Since: 3.0.0
+ */
+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
+ * active provider.
+ * @error: (out) (optional) (nullable): A return address for a #GError.
+ *
+ * Changes the active #PurpleCredentialProvider of @manager to provider with an
+ * id of @id.
+ *
+ * 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
+ * satisfied.
+ * @data: User data to pass to @callback.
+ *
+ * Reads the password for @account using the active #PurpleCredentialProvider of
+ * @manager.
+ *
+ * Since: 3.0.0
+ */
+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.
+ *
+ * Since: 3.0.0
+ */
+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
+ * satisfied.
+ * @data: User data to pass to @callback.
+ *
+ * Writes @password for @account to the active #PurpleCredentialProvider of
+ * @manager.
+ *
+ * Since: 3.0.0
+ */
+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
+ * with @error set.
+ *
+ * Since: 3.0.0
+ */
+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
+ * satisfied.
+ * @data: User data to pass to @callback.
+ *
+ * Clears the password for @account from the active #PurpleCredentialProvider
+ * of @manager.
+ *
+ * Since: 3.0.0
+ */
+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
+ * with @error set.
+ *
+ * Since: 3.0.0
+ */
+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().
+ *
+ * Since: 3.0.0
+ */
+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.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_credential_manager_write_settings(PurpleCredentialManager *manager, PurpleRequestFields *fields, GError **error);
+
+G_END_DECLS
+
+#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
- * used by anyone.
+ * 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
+ * satisfied.
* @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
* satisfied.
* @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.
*
* Since: 3.0.0
+ *
+ * Deprecated: 3.0.0
*/
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 @@
void
_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);
+
G_END_DECLS
#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 @@
'account_option',
'attention_type',
'circular_buffer',
+ 'credential_manager',
'credential_provider',
'image',
'keyvaluepair',
--- /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/>.
+ */
+
+#include <glib.h>
+
+#include <purple.h>
+
+#include "test_ui.h"
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+/* Since we're using GTask to test asynchronous functions, we need to use a
+ * main loop.
+ */
+static GMainLoop *loop = NULL;
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static gboolean
+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");
+
+ return FALSE;
+}
+
+/******************************************************************************
+ * 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)
+
+static void
+test_purple_credential_provider_read_password_async(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GTask *task = NULL;
+
+ task = g_task_new(p, cancellable, callback, data);
+ g_task_return_pointer(task, g_strdup("password"), g_free);
+ g_clear_object(&task);
+}
+
+static gchar *
+test_purple_credential_provider_read_password_finish(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail(g_task_is_valid(result, p), NULL);
+
+ return g_task_propagate_pointer(G_TASK(result), error);
+}
+
+static void
+test_purple_credential_provider_write_password_async(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ const gchar *password,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GTask *task = NULL;
+
+ task = g_task_new(p, cancellable, callback, data);
+ g_task_return_boolean(task, TRUE);
+ g_clear_object(&task);
+}
+
+static gboolean
+test_purple_credential_provider_write_password_finish(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail(g_task_is_valid(result, p), FALSE);
+
+ return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+static void
+test_purple_credential_provider_clear_password_async(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GTask *task = NULL;
+
+ task = g_task_new(p, cancellable, callback, data);
+ g_task_return_boolean(task, TRUE);
+ g_clear_object(&task);
+}
+
+static gboolean
+test_purple_credential_provider_clear_password_finish(PurpleCredentialProvider *p,
+ PurpleAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ 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();
+}
+
+static gboolean
+test_purple_credential_provider_write_settings(PurpleCredentialProvider *p,
+ PurpleRequestFields *fields)
+{
+ TestPurpleCredentialProvider *tp = TEST_PURPLE_CREDENTIAL_PROVIDER(p);
+
+ tp->fields = fields;
+
+ return TRUE;
+}
+
+static void
+test_purple_credential_provider_init(TestPurpleCredentialProvider *provider) {
+}
+
+static void
+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) {
+ return g_object_new(
+ TEST_PURPLE_TYPE_CREDENTIAL_PROVIDER,
+ "id", "test-provider",
+ "name", "Test Provider",
+ NULL);
+}
+
+/******************************************************************************
+ * Get Default Tests
+ *****************************************************************************/
+static void
+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);
+}
+
+/******************************************************************************
+ * Registration Tests
+ *****************************************************************************/
+static void
+test_purple_credential_manager_registration(void) {
+ PurpleCredentialManager *manager = NULL;
+ PurpleCredentialProvider *provider = NULL;
+ GError *error = NULL;
+ gboolean r = FALSE;
+
+ 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_true(r);
+ g_assert_no_error(error);
+
+ /* Register again and verify the error. */
+ r = purple_credential_manager_register_provider(manager, provider, &error);
+ g_assert_false(r);
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+ g_clear_error(&error);
+
+ /* Unregister the provider. */
+ r = purple_credential_manager_unregister_provider(manager, provider,
+ &error);
+ g_assert_true(r);
+ g_assert_no_error(error);
+
+ /* Unregister the provider again and verify the error. */
+ r = purple_credential_manager_unregister_provider(manager, provider,
+ &error);
+ g_assert_false(r);
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+ g_clear_error(&error);
+
+ /* Final clean ups. */
+ g_clear_object(&provider);
+}
+
+/******************************************************************************
+ * Set Active Tests
+ *****************************************************************************/
+static void
+test_purple_credential_manager_set_active_null(void) {
+ PurpleCredentialManager *manager = NULL;
+ GError *error = NULL;
+ gboolean ret = FALSE;
+
+ manager = purple_credential_manager_get_default();
+
+ ret = purple_credential_manager_set_active_provider(manager, NULL, &error);
+
+ g_assert_true(ret);
+ g_assert_no_error(error);
+
+}
+
+static void
+test_purple_credential_manager_set_active_non_existent(void) {
+ PurpleCredentialManager *manager = NULL;
+ GError *error = NULL;
+ gboolean ret = FALSE;
+
+ manager = purple_credential_manager_get_default();
+
+ ret = purple_credential_manager_set_active_provider(manager, "foo", &error);
+
+ g_assert_false(ret);
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+ g_clear_error(&error);
+}
+
+static void
+test_purple_credential_manager_set_active_normal(void) {
+ PurpleCredentialManager *manager = NULL;
+ PurpleCredentialProvider *provider = NULL;
+ GError *error = NULL;
+ gboolean r = FALSE;
+
+ 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_true(r);
+ 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",
+ &error);
+ g_assert_true(r);
+ g_assert_no_error(error);
+
+ /* Verify that unregistering the provider fails. */
+ r = purple_credential_manager_unregister_provider(manager, provider,
+ &error);
+ g_assert_false(r);
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+ g_clear_error(&error);
+
+ /* Now unset the active provider. */
+ r = purple_credential_manager_set_active_provider(manager, NULL, &error);
+ g_assert_true(r);
+ g_assert_no_error(error);
+
+ /* Finally unregister the provider now that it's no longer active. */
+ r = purple_credential_manager_unregister_provider(manager, provider,
+ &error);
+ g_assert_true(r);
+ g_assert_no_error(error);
+
+ /* And our final cleanup. */
+ g_clear_object(&provider);
+}
+
+/******************************************************************************
+ * No Provider Tests
+ *****************************************************************************/
+static void
+test_purple_credential_manager_no_provider_read_password_cb(GObject *obj,
+ GAsyncResult *res,
+ gpointer d)
+{
+ PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+ GError *error = NULL;
+ gchar *password = NULL;
+
+ password = purple_credential_manager_read_password_finish(manager, account,
+ res, &error);
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+ g_assert_null(password);
+
+ g_clear_object(&account);
+
+ g_main_loop_quit(loop);
+}
+
+static gboolean
+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,
+ account);
+
+ return FALSE;
+}
+
+static void
+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);
+
+ g_main_loop_run(loop);
+}
+
+static void
+test_purple_credential_manager_no_provider_write_password_cb(GObject *obj,
+ GAsyncResult *res,
+ gpointer d)
+{
+ PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+ GError *error = NULL;
+ gboolean r = FALSE;
+
+ r = purple_credential_manager_write_password_finish(manager, account, res,
+ &error);
+ g_assert_false(r);
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+
+ g_clear_object(&account);
+
+ g_main_loop_quit(loop);
+}
+
+static gboolean
+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,
+ account);
+
+ return FALSE;
+}
+
+static void
+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,
+ m);
+ g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+}
+
+static void
+test_purple_credential_manager_no_provider_clear_password_cb(GObject *obj,
+ GAsyncResult *res,
+ gpointer d)
+{
+ PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+ GError *error = NULL;
+ gboolean r = FALSE;
+
+ r = purple_credential_manager_clear_password_finish(manager, account, res,
+ &error);
+ g_assert_false(r);
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+
+ g_clear_object(&account);
+
+ g_main_loop_quit(loop);
+}
+
+static gboolean
+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,
+ account);
+
+ return FALSE;
+}
+
+static void
+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,
+ m);
+ g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+}
+
+static void
+test_purple_credential_manager_no_provider_read_settings(void) {
+ PurpleCredentialManager *manager = NULL;
+ PurpleRequestFields *fields = NULL;
+ GError *error = NULL;
+
+ manager = purple_credential_manager_get_default();
+
+ fields = purple_credential_manager_read_settings(manager, &error);
+
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+ g_clear_error(&error);
+
+ g_assert_null(fields);
+}
+
+static void
+test_purple_credential_manager_no_provider_write_settings(void) {
+ PurpleCredentialManager *manager = NULL;
+ GError *error = NULL;
+
+ manager = purple_credential_manager_get_default();
+
+ purple_credential_manager_write_settings(manager, NULL, &error);
+
+ g_assert_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0);
+ g_clear_error(&error);
+
+}
+
+/******************************************************************************
+ * Provider Tests
+ *****************************************************************************/
+static void
+test_purple_credential_manager_read_password_cb(GObject *obj, GAsyncResult *res,
+ gpointer d)
+{
+ PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+ GError *error = NULL;
+ gchar *password = NULL;
+
+ password = purple_credential_manager_read_password_finish(manager, account,
+ res, &error);
+ g_assert_no_error(error);
+ g_assert_cmpstr(password, ==, "password");
+ g_free(password);
+
+ g_clear_object(&account);
+
+ g_main_loop_quit(loop);
+}
+
+static gboolean
+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,
+ account);
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_manager_read_password_async(void) {
+ PurpleCredentialManager *m = purple_credential_manager_get_default();
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ GError *e = NULL;
+ gboolean r = FALSE;
+
+ r = purple_credential_manager_register_provider(m, p, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ r = purple_credential_manager_set_active_provider(m, "test-provider", &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ g_idle_add(test_purple_credential_manager_read_password_idle, m);
+ g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+
+ r = purple_credential_manager_set_active_provider(m, NULL, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ r = purple_credential_manager_unregister_provider(m, p, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ g_clear_object(&p);
+}
+
+static void
+test_purple_credential_manager_write_password_cb(GObject *obj,
+ GAsyncResult *res, gpointer d)
+{
+ PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+ GError *error = NULL;
+ gboolean r = FALSE;
+
+ r = purple_credential_manager_write_password_finish(manager, account, res,
+ &error);
+ g_assert_true(r);
+ g_assert_no_error(error);
+
+ g_clear_object(&account);
+
+ g_main_loop_quit(loop);
+}
+
+static gboolean
+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,
+ account);
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_manager_write_password_async(void) {
+ PurpleCredentialManager *m = purple_credential_manager_get_default();
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ GError *e = NULL;
+ gboolean r = FALSE;
+
+ r = purple_credential_manager_register_provider(m, p, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ r = purple_credential_manager_set_active_provider(m, "test-provider", &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ g_idle_add(test_purple_credential_manager_write_password_idle, m);
+ g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+
+ r = purple_credential_manager_set_active_provider(m, NULL, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ r = purple_credential_manager_unregister_provider(m, p, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ g_clear_object(&p);
+}
+
+static void
+test_purple_credential_manager_clear_password_cb(GObject *obj,
+ GAsyncResult *res, gpointer d)
+{
+ PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
+ PurpleAccount *account = PURPLE_ACCOUNT(d);
+ GError *error = NULL;
+ gboolean r = FALSE;
+
+ r = purple_credential_manager_clear_password_finish(manager, account, res,
+ &error);
+ g_assert_true(r);
+ g_assert_no_error(error);
+
+ g_clear_object(&account);
+
+ g_main_loop_quit(loop);
+}
+
+static gboolean
+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,
+ account);
+
+ return FALSE;
+}
+
+static void
+test_purple_credential_manager_clear_password_async(void) {
+ PurpleCredentialManager *m = purple_credential_manager_get_default();
+ PurpleCredentialProvider *p = test_purple_credential_provider_new();
+ GError *e = NULL;
+ gboolean r = FALSE;
+
+ r = purple_credential_manager_register_provider(m, p, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ r = purple_credential_manager_set_active_provider(m, "test-provider", &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ g_idle_add(test_purple_credential_manager_clear_password_idle, m);
+ g_timeout_add_seconds(10, test_purple_credential_manager_timeout_cb, loop);
+
+ g_main_loop_run(loop);
+
+ r = purple_credential_manager_set_active_provider(m, NULL, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ r = purple_credential_manager_unregister_provider(m, p, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ g_clear_object(&p);
+}
+
+static void
+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;
+ GError *e = NULL;
+ gboolean r = FALSE;
+
+ r = purple_credential_manager_register_provider(m, p, &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ r = purple_credential_manager_set_active_provider(m, "test-provider", &e);
+ g_assert_true(r);
+ g_assert_no_error(e);
+
+ fields = purple_credential_manager_read_settings(m, &e);
+ g_assert_nonnull(fields);
+ g_assert_no_error(e);
+ purple_request_fields_destroy(fields);
+
+ fields = purple_request_fields_new();
+ r = purple_credential_manager_write_settings(m, fields, &e);
+ g_assert_true(r);
+ g_assert_true(tp->fields == fields);
+ g_assert_no_error(e);
+
+ purple_request_fields_destroy(fields);
+
+ g_clear_object(&p);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar *argv[]) {
+ gint ret = 0;
+
+ g_test_init(&argc, &argv, NULL);
+
+ test_ui_purple_init();
+
+ 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);
+
+ ret = g_test_run();
+
+ g_main_loop_unref(loop);
+
+ return ret;
+}
--- 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
* loop.
*/
-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
libpurple/purplemarkup.c