--- a/libpurple/purplepresencemanager.c Tue Jan 23 00:03:38 2024 -0600
+++ b/libpurple/purplepresencemanager.c Tue Jan 23 00:04:57 2024 -0600
@@ -21,6 +21,7 @@
#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h> #define G_SETTINGS_ENABLE_BACKEND
#include <gio/gsettingsbackend.h>
@@ -29,17 +30,15 @@
#include "purplepresencemanagerprivate.h"
-#include "purplesavedpresenceprivate.h"
#define MANAGER_SCHEMA_ID "im.pidgin.Purple.PresenceManager"
-#define PRESENCE_SCHEMA_ID "im.pidgin.Purple.SavedPresence"
-#define MANAGER_PATH "/purple/presence-manager"
+#define SAVED_PRESENCE_SCHEMA_ID "im.pidgin.Purple.SavedPresence"
@@ -55,7 +54,7 @@
struct _PurplePresenceManager {
GSettingsBackend *backend;
@@ -69,8 +68,17 @@
*****************************************************************************/
-purple_presence_manager_path_for_presence(const char *id) {
- return g_strdup_printf("%s/presences/%s/", MANAGER_PATH, id);
+purple_presence_manager_get_path_for_id(PurplePresenceManager *manager, + basename = g_strdup_printf("%s.ini", id); + filename = g_build_filename(manager->path, basename, NULL); + g_clear_pointer(&basename, g_free); @@ -116,116 +124,171 @@
-purple_presence_manager_update_presences_setting(PurplePresenceManager *manager)
- GStrvBuilder *builder = NULL;
- GStrv presences = NULL;
+static PurpleSavedPresence * +purple_presence_manager_add(PurplePresenceManager *manager, const char *id) { + PurpleSavedPresence *presence = NULL; + GSettingsBackend *backend = NULL; + GSettings *settings = NULL; + g_return_val_if_fail(PURPLE_IS_PRESENCE_MANAGER(manager), NULL); + g_return_val_if_fail(!purple_strempty(id), NULL); - builder = g_strv_builder_new();
- for(guint i = 0; i < manager->presences->len; i++) {
- PurpleSavedPresence *presence = NULL;
+ /* Figure out which settings backend to use. */ + if(!purple_strempty(manager->path)) { - presence = g_ptr_array_index(manager->presences, i);
- id = purple_saved_presence_get_id(presence);
- g_strv_builder_add(builder, id);
+ filename = purple_presence_manager_get_path_for_id(manager, id); + backend = g_keyfile_settings_backend_new(filename, "/", NULL); + g_clear_pointer(&filename, g_free); + backend = g_memory_settings_backend_new(); - presences = g_strv_builder_end(builder);
- g_settings_set_strv(manager->settings, "presences",
- (const char * const *)presences);
- g_strv_builder_unref(builder);
+ /* Create the settings object with the determined backend. */ + settings = g_settings_new_with_backend(SAVED_PRESENCE_SCHEMA_ID, backend); + g_clear_object(&backend);
-purple_presence_manager_add(PurplePresenceManager *manager,
- PurpleSavedPresence *presence)
- g_return_if_fail(PURPLE_IS_PRESENCE_MANAGER(manager));
- g_return_if_fail(PURPLE_IS_SAVED_PRESENCE(presence));
+ /* Create the presence. */ + presence = g_object_new( + PURPLE_TYPE_SAVED_PRESENCE, g_ptr_array_add(manager->presences, g_object_ref(presence));
- purple_presence_manager_update_presences_setting(manager);
g_signal_emit(manager, signals[SIG_ADDED], 0, presence);
g_list_model_items_changed(G_LIST_MODEL(manager),
manager->presences->len - 1, 0, 1);
-purple_presence_manager_set_filename(PurplePresenceManager *manager,
+purple_presence_manager_set_path(PurplePresenceManager *manager, g_return_if_fail(PURPLE_IS_PRESENCE_MANAGER(manager));
- if(!purple_strequal(filename, manager->filename)) {
- g_free(manager->filename);
- manager->filename = g_strdup(filename);
+ if(!purple_strequal(path, manager->path)) { + manager->path = g_strdup(path); - g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_FILENAME]);
+ g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_PATH]); purple_presence_manager_load_saved_presences(PurplePresenceManager *manager) {
+ PurpleSavedPresence *presence = NULL; + /* Load the presences from disk. */ + if(manager->path != NULL) { + const char *basename = NULL; - ids = g_settings_get_strv(manager->settings, "presences");
+ dir = g_dir_open(manager->path, 0, &error); + g_warning("failed to open directory '%s': %s", manager->path, + GPatternSpec *pattern = NULL; + /* We use ?* to make sure we have at least one character before the + pattern = g_pattern_spec_new("?*.ini"); - /* If we don't have any existing presences, create an available one. */
- PurpleSavedPresence *presence = NULL;
+ while((basename = g_dir_read_name(dir)) != NULL) { + if(purple_strequal(basename, "manager.ini")) { + if(g_pattern_spec_match_string(pattern, basename)) { + PurpleSavedPresence *presence = NULL; - /* We aren't going to use the ids anymore so free them right away. */
+ id = g_strndup(basename, strlen(basename) - 4); + presence = purple_presence_manager_add(manager, id); + g_clear_pointer(&id, g_free); + g_clear_object(&presence); - /* Create the default available presence and set it as active. */
- presence = purple_presence_manager_create(manager);
+ g_clear_pointer(&pattern, g_pattern_spec_free); + /* Make sure we have our default available presence. */ + id = "ffffffff-ffff-ffff-ffff-ffffffffffff"; + presence = purple_presence_manager_find_with_id(manager, id, NULL); + if(!PURPLE_IS_SAVED_PRESENCE(presence)) { + presence = purple_presence_manager_add(manager, id); purple_saved_presence_set_name(presence, _("Available"));
purple_saved_presence_set_primitive(presence,
PURPLE_PRESENCE_PRIMITIVE_AVAILABLE);
- id = purple_saved_presence_get_id(presence);
- purple_presence_manager_set_active(manager, id);
+ g_clear_object(&presence); - g_clear_object(&presence);
- /* Create the default offline presence as well. */
- presence = purple_presence_manager_create(manager);
+ /* Make sure we have our default offline presence as well. */ + id = "00000000-0000-0000-0000-000000000000"; + presence = purple_presence_manager_find_with_id(manager, id, NULL); + if(!PURPLE_IS_SAVED_PRESENCE(presence)) { + presence = purple_presence_manager_add(manager, id); purple_saved_presence_set_name(presence, _("Offline"));
purple_saved_presence_set_primitive(presence,
PURPLE_PRESENCE_PRIMITIVE_OFFLINE);
g_clear_object(&presence);
+/****************************************************************************** + *****************************************************************************/ +purple_presence_manager_get_active_mapping(GValue *value, GVariant *variant, + PurplePresenceManager *manager = data; + PurpleSavedPresence *presence = NULL; + /* Get the id of the saved presence from gsettings. */ + id = g_variant_get_string(variant, NULL); + presence = purple_presence_manager_find_with_id(manager, id, NULL); + if(PURPLE_IS_SAVED_PRESENCE(presence)) { + g_value_set_object(value, G_OBJECT(presence)); - for(int i = 0; ids[i] != NULL; i++) {
- PurpleSavedPresence *presence = NULL;
- GSettings *settings = NULL;
- path = purple_presence_manager_path_for_presence(ids[i]);
- settings = g_settings_new_with_backend_and_path(PRESENCE_SCHEMA_ID,
- g_clear_pointer(&path, g_free);
+purple_presence_manager_set_active_mapping(const GValue *value, + const GVariantType *expected_type, + G_GNUC_UNUSED gpointer data) + PurpleSavedPresence *presence = NULL; - presence = g_object_new(
- PURPLE_TYPE_SAVED_PRESENCE,
- purple_presence_manager_add(manager, presence);
- g_clear_object(&presence);
+ if(!g_variant_type_equal(expected_type, G_VARIANT_TYPE_STRING)) {
+ presence = g_value_get_object(value); + if(!PURPLE_IS_SAVED_PRESENCE(presence)) { + id = purple_saved_presence_get_id(presence); + return g_variant_new_string(id); /******************************************************************************
@@ -274,15 +337,17 @@
purple_presence_manager_constructed(GObject *obj) {
PurplePresenceManager *manager = PURPLE_PRESENCE_MANAGER(obj);
- char *active_id = NULL;
G_OBJECT_CLASS(purple_presence_manager_parent_class)->constructed(obj);
- if(manager->filename == NULL) {
+ if(manager->path == NULL) { manager->backend = g_memory_settings_backend_new();
- manager->backend = g_keyfile_settings_backend_new(manager->filename,
+ filename = g_build_filename(manager->path, "manager.ini", NULL); + manager->backend = g_keyfile_settings_backend_new(filename, "/", NULL); + g_clear_pointer(&filename, g_free); manager->settings = g_settings_new_with_backend(MANAGER_SCHEMA_ID,
@@ -290,21 +355,18 @@
purple_presence_manager_load_saved_presences(manager);
- active_id = g_settings_get_string(manager->settings, "active");
- if(!purple_strempty(active_id)) {
- if(!purple_presence_manager_set_active(manager, active_id)) {
- g_warning("failed to set the active saved presence to %s",
- g_clear_pointer(&active_id, g_free);
+ g_settings_bind_with_mapping(manager->settings, "active", manager, + "active-presence", G_SETTINGS_BIND_DEFAULT, + purple_presence_manager_get_active_mapping, + purple_presence_manager_set_active_mapping, purple_presence_manager_finalize(GObject *obj) {
PurplePresenceManager *manager = PURPLE_PRESENCE_MANAGER(obj);
- g_clear_pointer(&manager->filename, g_free);
+ g_clear_pointer(&manager->path, g_free); g_clear_object(&manager->active);
if(manager->presences != NULL) {
@@ -325,9 +387,9 @@
PurplePresenceManager *manager = PURPLE_PRESENCE_MANAGER(obj);
g_value_set_string(value,
- purple_presence_manager_get_filename(manager));
+ purple_presence_manager_get_path(manager)); g_value_set_object(value,
@@ -346,9 +408,13 @@
PurplePresenceManager *manager = PURPLE_PRESENCE_MANAGER(obj);
- purple_presence_manager_set_filename(manager,
- g_value_get_string(value));
+ purple_presence_manager_set_path(manager, + g_value_get_string(value)); + purple_presence_manager_set_active(manager, + g_value_get_object(value)); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
@@ -372,16 +438,16 @@
obj_class->set_property = purple_presence_manager_set_property;
- * PurplePresenceManager:filename:
+ * PurplePresenceManager:path: - * The filename where settings should be stored. If this is %NULL settings
- * will not be saved to disk.
+ * The directory path where settings should be stored. If this is %NULL + * settings will not be saved to disk. - properties[PROP_FILENAME] = g_param_spec_string(
- "filename", "filename",
- "The name of the file where settings should be stored.",
+ properties[PROP_PATH] = g_param_spec_string( + "The directory path where settings should be stored.", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
@@ -396,7 +462,7 @@
"active-presence", "active-presence",
PURPLE_TYPE_SAVED_PRESENCE,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
@@ -484,17 +550,17 @@
-purple_presence_manager_new(const char *filename) {
+purple_presence_manager_new(const char *path) { return g_object_new(PURPLE_TYPE_PRESENCE_MANAGER,
-purple_presence_manager_get_filename(PurplePresenceManager *manager) {
+purple_presence_manager_get_path(PurplePresenceManager *manager) { g_return_val_if_fail(PURPLE_IS_PRESENCE_MANAGER(manager), NULL);
- return manager->filename;
@@ -506,27 +572,21 @@
purple_presence_manager_set_active(PurplePresenceManager *manager,
+ PurpleSavedPresence *presence) - PurpleSavedPresence *presence = NULL;
+ PurpleSavedPresence *found = NULL; g_return_val_if_fail(PURPLE_IS_PRESENCE_MANAGER(manager), FALSE);
+ g_return_val_if_fail(PURPLE_IS_SAVED_PRESENCE(presence), FALSE);
- presence = purple_presence_manager_find_with_id(manager, id, NULL);
- if(!PURPLE_IS_SAVED_PRESENCE(presence)) {
+ /* We need to make sure the manager knows about the passed in presence. */ + id = purple_saved_presence_get_id(presence); + found = purple_presence_manager_find_with_id(manager, id, NULL); + g_return_val_if_fail(PURPLE_IS_SAVED_PRESENCE(found), FALSE); if(g_set_object(&manager->active, presence)) {
g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_ACTIVE]);
- /* g_settings_set_string can't handle nulls, so use an empty string if
- g_settings_set_string(manager->settings, "active",
@@ -535,28 +595,13 @@
purple_presence_manager_create(PurplePresenceManager *manager) {
PurpleSavedPresence *presence = NULL;
- GSettings *settings = NULL;
g_return_val_if_fail(PURPLE_IS_PRESENCE_MANAGER(manager), FALSE);
id = g_uuid_string_random();
- path = purple_presence_manager_path_for_presence(id);
- settings = g_settings_new_with_backend_and_path(PRESENCE_SCHEMA_ID,
- manager->backend, path);
- presence = g_object_new(
- PURPLE_TYPE_SAVED_PRESENCE,
- g_clear_pointer(&id, g_free);
- g_clear_object(&settings);
- purple_presence_manager_add(manager, presence);
+ presence = purple_presence_manager_add(manager, id); @@ -565,6 +610,7 @@
purple_presence_manager_remove(PurplePresenceManager *manager, const char *id)
PurpleSavedPresence *presence = NULL;
g_return_val_if_fail(PURPLE_IS_PRESENCE_MANAGER(manager), FALSE);
@@ -581,8 +627,10 @@
g_ptr_array_remove_index(manager->presences, index);
- purple_presence_manager_update_presences_setting(manager);
- purple_saved_presence_reset(presence);
+ filename = purple_presence_manager_get_path_for_id(manager, id); + g_clear_pointer(&filename, g_free); g_signal_emit(manager, signals[SIG_REMOVED], 0, presence);
g_list_model_items_changed(G_LIST_MODEL(manager), index, 1, 0);
--- a/libpurple/purplepresencemanager.h Tue Jan 23 00:03:38 2024 -0600
+++ b/libpurple/purplepresencemanager.h Tue Jan 23 00:04:57 2024 -0600
@@ -48,7 +48,7 @@
* A manager for [class@SavedPresence]'s.
@@ -63,7 +63,7 @@
* Returns: (transfer none) (nullable): The default presence manager.
PurplePresenceManager *purple_presence_manager_get_default(void);
@@ -84,39 +84,39 @@
* Returns: (transfer none) (nullable): The default presence manager type cast
GListModel *purple_presence_manager_get_default_as_model(void);
* purple_presence_manager_new:
- * @filename: (nullable): An optional filename where settings will be stored.
+ * @path: (nullable): An optional directory path where settings will be stored. * Creates a new presence manager instance.
- * If @filename is %NULL, settings will not be stored to disk.
+ * If @path is %NULL, settings will not be stored to disk. * Returns: (transfer full): The new instance.
-PurplePresenceManager *purple_presence_manager_new(const char *filename);
+PurplePresenceManager *purple_presence_manager_new(const char *path); - * purple_presence_manager_get_filename:
+ * purple_presence_manager_get_path: * @manager: The instance.
- * Gets the file name that @manager is using for storage.
+ * Gets the directory path that @manager is using for storage. - * Returns: (nullable): The filename where settings are being saved to disk or
+ * Returns: (nullable): The directory path where settings are being saved to
-const char *purple_presence_manager_get_filename(PurplePresenceManager *manager);
+const char *purple_presence_manager_get_path(PurplePresenceManager *manager); * purple_presence_manager_get_active:
@@ -127,7 +127,7 @@
* Returns: (transfer none) (nullable): The active presence if there is one,
PurpleSavedPresence *purple_presence_manager_get_active(PurplePresenceManager *manager);
@@ -135,21 +135,19 @@
* purple_presence_manager_set_active:
* @manager: The instance.
- * @id: The id of the presence to make active.
+ * @presence: The presence to set as active. - * Sets the active presence to the [class@SavedPresence] with a
- * [property@SavedPresence:id] of @id.
+ * Sets the active presence to @presence. - * If @manager doesn't know of a presence with an id of @id, %FALSE will be
- * returned to indicate failure.
+ * If @manager doesn't know about @presence, %FALSE will be returned to - * Returns: %TRUE if a presence was found with an id of @id and made active,
+ * Returns: %TRUE if a presence was found and made active, otherwise %FALSE.
-gboolean purple_presence_manager_set_active(PurplePresenceManager *manager, const char *id);
+gboolean purple_presence_manager_set_active(PurplePresenceManager *manager, PurpleSavedPresence *presence); * purple_presence_manager_create:
@@ -163,7 +161,7 @@
* Returns: (transfer full): The new [class@SavedPresence].
PurpleSavedPresence *purple_presence_manager_create(PurplePresenceManager *manager);
@@ -179,7 +177,7 @@
* Returns: %TRUE if a [class@SavedPresence] was found with @id and removed,
gboolean purple_presence_manager_remove(PurplePresenceManager *manager, const char *id);
--- a/libpurple/tests/test_presence_manager.c Tue Jan 23 00:03:38 2024 -0600
+++ b/libpurple/tests/test_presence_manager.c Tue Jan 23 00:04:57 2024 -0600
@@ -71,7 +71,6 @@
test_purple_presence_manager_add_remove(void) {
PurplePresenceManager *manager = NULL;
PurpleSavedPresence *presence = NULL;
- GListModel *model = NULL;
gboolean success = FALSE;
@@ -81,26 +80,6 @@
manager = purple_presence_manager_new(NULL);
- /* When a presence manager is created, if there are no saved statuses it
- * adds some default ones and sets the active presence. We need to clear
- * all of that to get to a known state.
- * The default statuses are checked in test_purple_presence_manager_new.
- model = G_LIST_MODEL(manager);
- purple_presence_manager_set_active(manager, NULL);
- while(g_list_model_get_n_items(model) > 0) {
- PurpleSavedPresence *presence = NULL;
- /* Since we're removing items, the positions change, so we just always
- * want to remove the item at position 0.
- presence = g_list_model_get_item(model, 0);
- purple_presence_manager_remove(manager,
- purple_saved_presence_get_id(presence));
- g_assert_finalize_object(presence);
/* Connect all of our signals to make sure they're being emitted. */
g_signal_connect(manager, "added",
G_CALLBACK(test_purple_presence_manager_add_remove_counter),
@@ -113,7 +92,8 @@
len = g_list_model_get_n_items(G_LIST_MODEL(manager));
- g_assert_cmpuint(len, ==, 0);
+ /* The manager makes sure we always have online and offline presences. */ + g_assert_cmpuint(len, ==, 2); presence = purple_presence_manager_create(manager);
g_assert_true(PURPLE_IS_SAVED_PRESENCE(presence));
@@ -126,7 +106,7 @@
len = g_list_model_get_n_items(G_LIST_MODEL(manager));
- g_assert_cmpuint(len, ==, 1);
+ g_assert_cmpuint(len, ==, 3); success = purple_presence_manager_remove(manager, id);
@@ -135,7 +115,7 @@
g_assert_cmpuint(changed, ==, 2);
len = g_list_model_get_n_items(G_LIST_MODEL(manager));
- g_assert_cmpuint(len, ==, 0);
+ g_assert_cmpuint(len, ==, 2); g_clear_object(&presence);
g_clear_object(&manager);
@@ -145,32 +125,34 @@
test_purple_presence_manager_persistence(void) {
PurplePresenceManager *manager = NULL;
PurpleSavedPresence *presence = NULL;
- filename = g_build_filename(TEST_CACHE_DIR,
- "presence_manager_persistence.ini",
+ path = g_build_filename(TEST_CACHE_DIR, "presence_manager_persistence", /* Remove the file if it exists so we can start from a known state. */
+ filename = g_build_filename(path, "manager.ini", NULL); + g_clear_pointer(&filename, g_free); /* Create the manager, add a presence, and make it active. */
- manager = purple_presence_manager_new(filename);
+ manager = purple_presence_manager_new(path); g_assert_true(PURPLE_IS_PRESENCE_MANAGER(manager));
presence = purple_presence_manager_create(manager);
- g_assert_true(PURPLE_IS_SAVED_PRESENCE(presence));
+ purple_saved_presence_set_name(presence, "test-presence"); /* Save the id of the presence as we need to use it later. */
id = purple_saved_presence_get_id(presence);
/* Make the presence active. */
- ret = purple_presence_manager_set_active(manager, id);
+ ret = purple_presence_manager_set_active(manager, presence); g_clear_object(&presence);
@@ -187,7 +169,7 @@
g_clear_object(&manager);
/* Now create the manager again and verify that everything was restored. */
- manager = purple_presence_manager_new(filename);
+ manager = purple_presence_manager_new(path); g_assert_true(PURPLE_IS_PRESENCE_MANAGER(manager));
presence = purple_presence_manager_get_active(manager);
@@ -199,7 +181,7 @@
g_clear_object(&manager);
- g_clear_pointer(&filename, g_free);
+ g_clear_pointer(&path, g_free); /******************************************************************************