Thu, 24 Apr 2025 21:42:59 -0500
Add Purple.Contact.is_own_account
This checks to see if a Purple.Contact shares the Purple.ContactInfo with the
account it is tied to.
Testing Done:
Ran the unit tests under valgrind and called in the turtles.
Reviewed at https://reviews.imfreedom.org/r/3985/
/* * 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 library 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 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 General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this library; if not, see <https://www.gnu.org/licenses/>. */ #include <glib/gi18n-lib.h> #include "purplecredentialmanager.h" #include "purplecredentialmanagerprivate.h" #include "core.h" #include "debug.h" #include "prefs.h" #include "purplecredentialproviderprivate.h" #include "purplenoopcredentialprovider.h" #include "purplenotification.h" #include "purplenotificationmanager.h" #include "util.h" enum { PROP_0, PROP_ITEM_TYPE, PROP_N_ITEMS, N_PROPERTIES, }; static GParamSpec *properties[N_PROPERTIES] = {NULL, }; enum { SIG_ADDED, SIG_REMOVED, SIG_ACTIVE_CHANGED, N_SIGNALS, }; static guint signals[N_SIGNALS] = {0, }; struct _PurpleCredentialManager { GObject parent; GPtrArray *providers; PurpleCredentialProvider *active; GSettings *settings; }; static PurpleCredentialManager *default_manager = NULL; /****************************************************************************** * Helpers *****************************************************************************/ static PurpleCredentialProvider * purple_credential_manager_find_provider_with_id(PurpleCredentialManager *manager, const char *id, guint *position) { g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), NULL); for(guint i = 0; i < manager->providers->len; i++) { PurpleCredentialProvider *provider = NULL; const char *provider_id = NULL; provider = g_ptr_array_index(manager->providers, i); provider_id = purple_credential_provider_get_id(provider); if(purple_strequal(provider_id, id)) { if(position != NULL) { *position = i; } return provider; } } return 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); char *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(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(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(task); } /****************************************************************************** * Purple Callbacks *****************************************************************************/ static void purple_credential_manager_ui_started_cb(G_GNUC_UNUSED PurpleUi *ui, gpointer data) { PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(data); if(!PURPLE_IS_CREDENTIAL_PROVIDER(manager->active)) { GError *error = NULL; char *id = NULL; id = g_settings_get_string(manager->settings, "active-provider"); if(!purple_credential_manager_set_active(manager, id, &error)) { PurpleNotification *notification = NULL; PurpleNotificationManager *manager = NULL; const char *title = NULL; g_warning("Failed to make %s the active credential provider : %s", id, error != NULL ? error->message : "unknown error"); title = _("Failed to load the selected credential provider."); notification = purple_notification_new(NULL, title); title = _("Check your system configuration or select another one " "in preferences"); purple_notification_set_subtitle(notification, title); manager = purple_notification_manager_get_default(); purple_notification_manager_add(manager, notification); g_clear_object(¬ification); } g_free(id); g_clear_error(&error); } } /****************************************************************************** * GListModel Implementation *****************************************************************************/ static GType purple_credential_manager_get_item_type(G_GNUC_UNUSED GListModel *list) { return PURPLE_TYPE_CREDENTIAL_PROVIDER; } static guint purple_credential_manager_get_n_items(GListModel *list) { PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(list); return manager->providers->len; } static gpointer purple_credential_manager_get_item(GListModel *list, guint position) { PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(list); PurpleCredentialProvider *provider = NULL; if(position < manager->providers->len) { provider = g_object_ref(g_ptr_array_index(manager->providers, position)); } return provider; } static void pidgin_credential_manager_list_model_iface_init(GListModelInterface *iface) { iface->get_item_type = purple_credential_manager_get_item_type; iface->get_n_items = purple_credential_manager_get_n_items; iface->get_item = purple_credential_manager_get_item; } /****************************************************************************** * GObject Implementation *****************************************************************************/ G_DEFINE_FINAL_TYPE_WITH_CODE(PurpleCredentialManager, purple_credential_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, pidgin_credential_manager_list_model_iface_init)) static void purple_credential_manager_finalize(GObject *obj) { PurpleCredentialManager *manager = NULL; manager = PURPLE_CREDENTIAL_MANAGER(obj); g_clear_pointer(&manager->providers, g_ptr_array_unref); g_clear_object(&manager->active); g_clear_object(&manager->settings); G_OBJECT_CLASS(purple_credential_manager_parent_class)->finalize(obj); } static void purple_credential_manager_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { GListModel *model = G_LIST_MODEL(obj); switch(param_id) { case PROP_ITEM_TYPE: g_value_set_gtype(value, purple_credential_manager_get_item_type(model)); break; case PROP_N_ITEMS: g_value_set_uint(value, purple_credential_manager_get_n_items(model)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void purple_credential_manager_init(PurpleCredentialManager *manager) { manager->providers = g_ptr_array_new_full(1, 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; obj_class->get_property = purple_credential_manager_get_property; /** * PurpleCredentialManager:item-type: * * The type of items. See [vfunc@Gio.ListModel.get_item_type]. * * Since: 3.0 */ properties[PROP_ITEM_TYPE] = g_param_spec_gtype( "item-type", NULL, NULL, G_TYPE_OBJECT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * PurpleCredentialManager:n-items: * * The number of items. See [vfunc@Gio.ListModel.get_n_items]. * * Since: 3.0 */ properties[PROP_N_ITEMS] = g_param_spec_uint( "n-items", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, N_PROPERTIES, properties); /** * PurpleCredentialManager::added: * @manager: The #PurpleCredentialManager instance. * @provider: The #PurpleCredentialProvider that was added. * * Emitted after @provider has been added to @manager. * * Since: 3.0 */ signals[SIG_ADDED] = g_signal_new_class_handler( "added", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 1, PURPLE_TYPE_CREDENTIAL_PROVIDER); /** * PurpleCredentialManager::removed: * @manager: The #PurpleCredentialManager instance. * @provider: The #PurpleCredentialProvider that was removed. * * Emitted after @provider has been removed from @manager. * * Since: 3.0 */ signals[SIG_REMOVED] = g_signal_new_class_handler( "removed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 1, PURPLE_TYPE_CREDENTIAL_PROVIDER); /** * PurpleCredentialManager::active-changed: * @manager: The #PurpleCredentialManager instance. * @previous: 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 */ signals[SIG_ACTIVE_CHANGED] = g_signal_new_class_handler( "active-changed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 2, PURPLE_TYPE_CREDENTIAL_PROVIDER, PURPLE_TYPE_CREDENTIAL_PROVIDER); } /****************************************************************************** * Private API *****************************************************************************/ /* Currently we're auto-registering the noop provider on the default manager, * this may get moved to purple core later, so we just want to keep it all in * one place for now. */ static PurpleCredentialProvider *noop = NULL; void purple_credential_manager_startup(G_GNUC_UNUSED PurpleCore *core) { if(default_manager == NULL) { PurpleUi *ui = NULL; GError *error = NULL; gpointer backend; default_manager = g_object_new(PURPLE_TYPE_CREDENTIAL_MANAGER, NULL); backend = purple_core_get_settings_backend(); default_manager->settings = g_settings_new_with_backend("im.pidgin.Purple.Credentials", backend); ui = purple_core_get_ui(); g_signal_connect_object(ui, "started", G_CALLBACK(purple_credential_manager_ui_started_cb), default_manager, G_CONNECT_DEFAULT); noop = purple_noop_credential_provider_new(); if(!purple_credential_manager_add(default_manager, noop, &error)) { g_warning("failed to register the noop credential manager: %s", error ? error->message : "unknown"); g_clear_error(&error); g_clear_object(&noop); } } } void purple_credential_manager_shutdown(G_GNUC_UNUSED PurpleCore *core) { if(PURPLE_IS_CREDENTIAL_MANAGER(default_manager)) { /* If we have an instance of the noop provider we need to unregister * it before continuing. */ if(PURPLE_IS_CREDENTIAL_PROVIDER(noop)) { GError *error = NULL; if(!purple_credential_manager_remove(default_manager, noop, &error)) { g_warning("failed to unregister the noop provider: %s", error ? error->message : "unknown"); g_clear_error(&error); } g_clear_object(&noop); } if(default_manager->providers->len > 0) { g_warning("purple_credential_manager_shutdown called while %u " "providers were still registered. Skipping shutdown", default_manager->providers->len); } else { g_clear_object(&default_manager); } } } /****************************************************************************** * Public API *****************************************************************************/ gboolean purple_credential_manager_add(PurpleCredentialManager *manager, PurpleCredentialProvider *provider, GError **error) { PurpleCredentialProvider *existing = NULL; const char *id = NULL; guint position = 0; 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; } id = purple_credential_provider_get_id(provider); existing = purple_credential_manager_find_provider_with_id(manager, id, NULL); if(PURPLE_IS_CREDENTIAL_PROVIDER(existing)) { g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, _("provider %s has already been added"), id); return FALSE; } /* Store the position to avoid doing math after adding. */ position = manager->providers->len; g_ptr_array_add(manager->providers, g_object_ref(provider)); g_signal_emit(G_OBJECT(manager), signals[SIG_ADDED], 0, provider); g_list_model_items_changed(G_LIST_MODEL(manager), position, 0, 1); g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_N_ITEMS]); /* 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(manager->active) && G_IS_SETTINGS(manager->settings)) { char *wanted = NULL; wanted = g_settings_get_string(manager->settings, "active-provider"); if(purple_strequal(wanted, id)) { purple_credential_manager_set_active(manager, id, error); } g_free(wanted); } return TRUE; } PurpleCredentialManager * purple_credential_manager_get_default(void) { return default_manager; } GListModel * purple_credential_manager_get_default_as_model(void) { if(PURPLE_IS_CREDENTIAL_MANAGER(default_manager)) { return G_LIST_MODEL(default_manager); } return NULL; } gboolean purple_credential_manager_set_active(PurpleCredentialManager *manager, const char *id, GError **error) { PurpleCredentialProvider *previous = NULL; PurpleCredentialProvider *provider = NULL; g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); /* First look up the new provider if we're given one. */ if(id != NULL) { provider = purple_credential_manager_find_provider_with_id(manager, id, NULL); 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(manager->active)) { previous = g_object_ref(manager->active); } if(g_set_object(&manager->active, provider)) { if(PURPLE_IS_CREDENTIAL_PROVIDER(previous)) { purple_credential_provider_deactivate(previous); } if(PURPLE_IS_CREDENTIAL_PROVIDER(provider)) { purple_credential_provider_activate(provider); } g_signal_emit(G_OBJECT(manager), signals[SIG_ACTIVE_CHANGED], 0, previous, manager->active); } g_clear_object(&previous); /* Finally update the preference if we were given a new id. We assume, that * a NULL id means we're shutting down and thus shouldn't update the * setting. */ if(id != NULL && G_IS_SETTINGS(manager->settings)) { g_settings_set_string(manager->settings, "active-provider", id); } purple_debug_info("credential-manager", "set active provider to '%s'", id); return TRUE; } PurpleCredentialProvider * purple_credential_manager_get_active(PurpleCredentialManager *manager) { g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), NULL); return manager->active; } void purple_credential_manager_read_password_async(PurpleCredentialManager *manager, PurpleAccount *account, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) { GTask *task = NULL; g_return_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager)); task = g_task_new(manager, cancellable, callback, data); g_task_set_source_tag(task, purple_credential_manager_read_password_async); if(manager->active != NULL) { purple_credential_provider_read_password_async(manager->active, account, cancellable, purple_credential_manager_read_password_callback, task); } else { g_task_return_new_error(task, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, _("can not read password, no active " "credential provider")); g_object_unref(task); } } char * 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 char *password, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) { GTask *task = NULL; g_return_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager)); task = g_task_new(manager, cancellable, callback, data); g_task_set_source_tag(task, purple_credential_manager_write_password_async); if(!purple_account_get_remember_password(account)) { const char *name = NULL; name = purple_account_get_username(account); g_task_return_new_error(task, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, _("account \"%s\" is not marked to be stored"), name); g_object_unref(task); return; } if(manager->active != NULL) { purple_credential_provider_write_password_async(manager->active, account, password, cancellable, purple_credential_manager_write_password_callback, task); } else { g_task_return_new_error(task, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, _("can not write password, no active " "credential provider")); g_object_unref(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) { GTask *task = NULL; g_return_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager)); g_return_if_fail(PURPLE_IS_ACCOUNT(account)); task = g_task_new(manager, cancellable, callback, data); g_task_set_source_tag(task, purple_credential_manager_clear_password_async); if(manager->active != NULL) { purple_credential_provider_clear_password_async(manager->active, account, cancellable, purple_credential_manager_clear_password_callback, task); } else { g_task_return_new_error(task, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, _("can not clear password, no active " "credential provider")); g_object_unref(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); } gboolean purple_credential_manager_remove(PurpleCredentialManager *manager, PurpleCredentialProvider *provider, GError **error) { PurpleCredentialProvider *candidate = NULL; const char *id = NULL; guint position = 0; g_return_val_if_fail(PURPLE_IS_CREDENTIAL_MANAGER(manager), FALSE); g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), FALSE); id = purple_credential_provider_get_id(provider); if(provider == manager->active) { g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, _("provider %s is currently in use"), id); return FALSE; } g_object_ref(provider); candidate = purple_credential_manager_find_provider_with_id(manager, id, &position); if(PURPLE_IS_CREDENTIAL_PROVIDER(candidate)) { g_ptr_array_remove_index(manager->providers, position); g_signal_emit(G_OBJECT(manager), signals[SIG_REMOVED], 0, provider); g_list_model_items_changed(G_LIST_MODEL(manager), position, 1, 0); g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_N_ITEMS]); g_object_unref(provider); return TRUE; } g_set_error(error, PURPLE_CREDENTIAL_MANAGER_DOMAIN, 0, _("provider %s has not been added"), id); g_object_unref(provider); return FALSE; }