libpurple/purplecredentialmanager.c

Fri, 02 Jul 2021 04:32:18 -0500

author
Elliott Sales de Andrade <quantum.analyst@gmail.com>
date
Fri, 02 Jul 2021 04:32:18 -0500
changeset 40977
dd0f04c5d212
parent 40823
338bba38df77
child 40987
671f2442e50e
permissions
-rw-r--r--

Cleanup RoomList UI ops

* Mark unused parameters, and remove unnecessary widgets passed to callbacks.
* Remove unused `purple_roomlist_[gs]et_protocol_data`.
* Remove unused list of `PurpleRoomList`s.
* Inline `update_roomlist` using GObject macros.
* Remove use of `PurpleRoomListUiOps->destroy`.
This can be achieved through object destruction notifiers.
I'm not sure how the room list would ever get destroyed in Finch though, as the
widget seems to hold a ref to it, which is cleared when the list is destroyed.
But the list can't be destroyed until said ref is dropped.
* Replace `PurpleRoomlistUiOps->in_progress` with notify signals.

Testing Done:
Compile only. Might have done some tests, but I wrote this a while back and don't remember.

Reviewed at https://reviews.imfreedom.org/r/785/

/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * 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 "core.h"
#include "debug.h"
#include "prefs.h"
#include "purplenoopcredentialprovider.h"
#include "purpleprivate.h"
#include "util.h"

enum {
	SIG_PROVIDER_REGISTERED,
	SIG_PROVIDER_UNREGISTERED,
	SIG_ACTIVE_PROVIDER_CHANGED,
	N_SIGNALS,
};
static guint signals[N_SIGNALS] = {0, };

typedef struct {
	GHashTable *providers;

	PurpleCredentialProvider *active_provider;
} PurpleCredentialManagerPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(PurpleCredentialManager, purple_credential_manager,
                           G_TYPE_OBJECT);

static PurpleCredentialManager *default_manager = NULL;

/******************************************************************************
 * Async Callbacks
 *****************************************************************************/
static void
purple_credential_manager_read_password_callback(GObject *obj,
                                                 GAsyncResult *res,
                                                 gpointer data)
{
	PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
	GError *error = NULL;
	GTask *task = G_TASK(data);
	gchar *password = NULL;

	password = purple_credential_provider_read_password_finish(provider, res,
	                                                           &error);

	if(error != NULL) {
		g_task_return_error(task, error);
	} else {
		g_task_return_pointer(task, password, g_free);
	}

	/* Clean up our initial reference to the task. */
	g_object_unref(G_OBJECT(task));
}

static void
purple_credential_manager_write_password_callback(GObject *obj,
                                                  GAsyncResult *res,
                                                  gpointer data)
{
	PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
	GError *error = NULL;
	GTask *task = G_TASK(data);
	gboolean ret = FALSE;

	ret = purple_credential_provider_write_password_finish(provider, res,
	                                                       &error);

	if(error != NULL) {
		g_task_return_error(task, error);
	} else {
		g_task_return_boolean(task, ret);
	}

	/* Clean up our initial reference to the task. */
	g_object_unref(G_OBJECT(task));
}

static void
purple_credential_manager_clear_password_callback(GObject *obj,
                                                  GAsyncResult *res,
                                                  gpointer data)
{
	PurpleCredentialProvider *provider = PURPLE_CREDENTIAL_PROVIDER(obj);
	GError *error = NULL;
	GTask *task = G_TASK(data);
	gboolean ret = FALSE;

	ret = purple_credential_provider_clear_password_finish(provider, res,
	                                                       &error);

	if(error != NULL) {
		g_task_return_error(task, error);
	} else {
		g_task_return_boolean(task, ret);
	}

	/* Clean up our initial reference to the task. */
	g_object_unref(G_OBJECT(task));
}

/******************************************************************************
 * Purple Callbacks
 *****************************************************************************/
static void
purple_credential_manager_core_init_cb(gpointer data) {
	PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(data);
	PurpleCredentialManagerPrivate *priv = NULL;

	priv = purple_credential_manager_get_instance_private(manager);
	if(!PURPLE_IS_CREDENTIAL_PROVIDER(priv->active_provider)) {
		purple_notify_error(NULL, _("Credential Manager"),
		                    _("Failed to load the selected credential "
		                      "provider."),
		                    _("Check your system configuration or select "
		                      "another one in the preferences dialog."), NULL);
	}
}

/******************************************************************************
 * 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;
	PurpleCredentialProvider *noop = 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);

	noop = purple_noop_credential_provider_new();
	purple_credential_manager_register_provider(manager, noop, NULL);
	g_object_unref(G_OBJECT(noop));

	/* Connect to the core-initialized signal so we can alert the user if we
	 * were unable to find their credential provider.
	 */
	purple_signal_connect(purple_get_core(), "core-initialized", manager,
	                      PURPLE_CALLBACK(purple_credential_manager_core_init_cb),
	                      manager);
}

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.
	 *
	 * Since: 3.0.0
	 */
	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.
	 *
	 * Since: 3.0.0
	 */
	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);

	/**
	 * PurpleCredentialManager::active-provider-changed:
	 * @manager: The #PurpleCredentialManager instance.
	 * @old: The #PurpleCredentialProvider that was previously active.
	 * @current: The #PurpleCredentialProvider that is now currently active.
	 *
	 * Emitted after @provider has become the active provider for @manager.
	 *
	 * Since: 3.0.0
	 */
	signals[SIG_ACTIVE_PROVIDER_CHANGED] = g_signal_new(
		"active-provider-changed",
		G_OBJECT_CLASS_TYPE(klass),
		G_SIGNAL_RUN_LAST,
		G_STRUCT_OFFSET(PurpleCredentialManagerClass, active_provider_changed),
		NULL,
		NULL,
		NULL,
		G_TYPE_NONE,
		2,
		PURPLE_TYPE_CREDENTIAL_PROVIDER,
		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) {
	if(PURPLE_IS_CREDENTIAL_MANAGER(default_manager)) {
		PurpleCredentialManagerPrivate *priv = NULL;
		guint size = 0;

		priv = purple_credential_manager_get_instance_private(default_manager);

		size = g_hash_table_size(priv->providers);
		if(size > 0) {
			g_warning("purple_credential_manager_shutdown called while %d "
				      "providers were still registered. Skipping shutdown",
				      size);
		} else {
			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);

	/* If we don't currently have an active provider, check if the newly
	 * registered provider has the id of the stored provider in preferences.
	 * If it is, go ahead and make it the active provider.
	 */
	if(!PURPLE_IS_CREDENTIAL_PROVIDER(priv->active_provider)) {
		const gchar *wanted = NULL;

		wanted = purple_prefs_get_string("/purple/credentials/active-provider");

		if(purple_strequal(wanted, id)) {
			purple_credential_manager_set_active_provider(manager, id, error);
		}
	}

	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 *old = NULL, *provider = NULL;

	g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE);

	priv = purple_credential_manager_get_instance_private(manager);

	/* First look up the new provider if we're given one. */
	if(id != NULL) {
		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;
		}
	}

	if(PURPLE_IS_CREDENTIAL_PROVIDER(priv->active_provider)) {
		old = PURPLE_CREDENTIAL_PROVIDER(g_object_ref(priv->active_provider));
	}

	if(g_set_object(&priv->active_provider, provider)) {
		if(PURPLE_IS_CREDENTIAL_PROVIDER(old)) {
			purple_credential_provider_deactivate(old);
		}

		if(PURPLE_IS_CREDENTIAL_PROVIDER(provider)) {
			purple_credential_provider_activate(provider);
		}

		g_signal_emit(G_OBJECT(manager), signals[SIG_ACTIVE_PROVIDER_CHANGED],
		              0, old, priv->active_provider);
	}

	g_clear_object(&old);

	purple_debug_info("credential-manager", "set active provider to '%s'", id);

	return TRUE;
}

PurpleCredentialProvider *
purple_credential_manager_get_active_provider(PurpleCredentialManager *manager)
{
	PurpleCredentialManagerPrivate *priv = NULL;

	g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), NULL);

	priv = purple_credential_manager_get_instance_private(manager);

	return priv->active_provider;
}

void
purple_credential_manager_read_password_async(PurpleCredentialManager *manager,
                                              PurpleAccount *account,
                                              GCancellable *cancellable,
                                              GAsyncReadyCallback callback,
                                              gpointer data)
{
	PurpleCredentialManagerPrivate *priv = 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);

	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);
		g_object_unref(G_OBJECT(task));

		return;
	}

	purple_credential_provider_read_password_async(priv->active_provider,
	                                               account,
	                                               cancellable,
	                                               purple_credential_manager_read_password_callback,
	                                               task);
}

gchar *
purple_credential_manager_read_password_finish(PurpleCredentialManager *manager,
                                               GAsyncResult *result,
                                               GError **error)
{
	g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), NULL);

	return g_task_propagate_pointer(G_TASK(result), error);
}

void
purple_credential_manager_write_password_async(PurpleCredentialManager *manager,
                                               PurpleAccount *account,
                                               const gchar *password,
                                               GCancellable *cancellable,
                                               GAsyncReadyCallback callback,
                                               gpointer data)
{
	PurpleCredentialManagerPrivate *priv = 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);

	if(!purple_account_get_remember_password(account)) {
		GError *error = NULL;
		const gchar *name = purple_account_get_username(account);

		error = g_error_new(PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0,
		                    _("account \"%s\" is not marked to be stored"),
		                    name);

		g_task_return_error(task, error);
		g_object_unref(G_OBJECT(task));

		return;
	}

	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);
		g_object_unref(G_OBJECT(task));

		return;
	}

	purple_credential_provider_write_password_async(priv->active_provider,
	                                                account, password,
	                                                cancellable,
	                                                purple_credential_manager_write_password_callback,
	                                                task);
}

gboolean
purple_credential_manager_write_password_finish(PurpleCredentialManager *manager,
                                                GAsyncResult *result,
                                                GError **error)
{
	g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE);

	return g_task_propagate_boolean(G_TASK(result), error);
}

void
purple_credential_manager_clear_password_async(PurpleCredentialManager *manager,
                                               PurpleAccount *account,
                                               GCancellable *cancellable,
                                               GAsyncReadyCallback callback,
                                               gpointer data)
{
	PurpleCredentialManagerPrivate *priv = 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);

	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);
		g_object_unref(G_OBJECT(task));

		return;
	}

	purple_credential_provider_clear_password_async(priv->active_provider,
	                                                account,
	                                                cancellable,
	                                                purple_credential_manager_clear_password_callback,
	                                                task);
}

gboolean
purple_credential_manager_clear_password_finish(PurpleCredentialManager *manager,
                                                GAsyncResult *result,
                                                GError **error)
{
	g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE);

	return g_task_propagate_boolean(G_TASK(result), 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);
}

void
purple_credential_manager_foreach_provider(PurpleCredentialManager *manager,
                                           PurpleCredentialManagerForeachFunc func,
                                           gpointer data)
{
	GHashTableIter iter;
	PurpleCredentialManagerPrivate *priv = NULL;
	gpointer value;

	g_return_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager));
	g_return_if_fail(func != NULL);

	priv = purple_credential_manager_get_instance_private(manager);

	g_hash_table_iter_init(&iter, priv->providers);
	while(g_hash_table_iter_next(&iter, NULL, &value)) {
		func(PURPLE_CREDENTIAL_PROVIDER(value), data);
	}
}

mercurial