libpurple/purpleconversationmanager.c

changeset 43284
50c1bcc45576
parent 43283
01eb1bbf4186
child 43287
1de854696dfc
--- a/libpurple/purpleconversationmanager.c	Fri Jul 11 01:55:31 2025 -0500
+++ b/libpurple/purpleconversationmanager.c	Fri Jul 11 02:46:28 2025 -0500
@@ -26,10 +26,17 @@
 #include "purpleconversationmanagerprivate.h"
 
 #include "core.h"
+#include "purpleaccount.h"
+#include "purpleaccountmanager.h"
 #include "purplecontact.h"
 #include "purpleui.h"
 #include "util.h"
 
+#ifdef G_LOG_DOMAIN
+#undef G_LOG_DOMAIN
+#endif /* G_LOG_DOMAIN */
+#define G_LOG_DOMAIN "PurpleConversationManager"
+
 enum {
 	PROP_0,
 	PROP_FILENAME,
@@ -58,6 +65,8 @@
 	SeagullStatement *delete_tags;
 	SeagullStatement *insert_properties;
 	SeagullStatement *insert_tags;
+	SeagullStatement *query_properties;
+	SeagullStatement *query_tags;
 
 	GPtrArray *conversations;
 };
@@ -145,6 +154,30 @@
 	}
 	manager->insert_tags = stmt;
 
+	stmt = seagull_statement_new_from_resource(manager->db,
+	                                           RESOURCE_PATH "/statements/query-properties.sql",
+	                                           &error);
+	if(error != NULL) {
+		g_warning("failed to loaded query-properties statement: %s",
+		          error->message);
+		g_clear_error(&error);
+
+		return;
+	}
+	manager->query_properties = stmt;
+
+	stmt = seagull_statement_new_from_resource(manager->db,
+	                                           RESOURCE_PATH "/statements/query-tags.sql",
+	                                           &error);
+	if(error != NULL) {
+		g_warning("failed to loaded query-tags statement: %s",
+		          error->message);
+		g_clear_error(&error);
+
+		return;
+	}
+	manager->query_tags = stmt;
+
 	manager->database_initialized = TRUE;
 }
 
@@ -155,10 +188,18 @@
 	g_return_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager));
 
 	if(g_set_str(&manager->filename, filename)) {
-		purple_conversation_manager_initialize_database(manager);
-
 		g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_FILENAME]);
 	}
+
+	/* The filename property is construct-only and nullable, so if it is set as
+	 * null, the g_set_str above won't return TRUE, but and we have to
+	 * initialize the database in any case so we just do it separately.
+	 *
+	 * Also, GObject will emit a Critical message if a construct only property
+	 * is set more than once and not call it again, so we don't need to
+	 * protect against that either.
+	 */
+	purple_conversation_manager_initialize_database(manager);
 }
 
 static gboolean
@@ -482,6 +523,193 @@
 	}
 }
 
+static void
+purple_conversation_manager_load_conversation_tags(PurpleConversationManager *manager,
+                                                   PurpleConversation *conversation,
+                                                   const char *account_id,
+                                                   const char *conversation_id,
+                                                   GError **error)
+{
+	PurpleTags *tags = NULL;
+	GError *local_error = NULL;
+
+	tags = purple_conversation_get_tags(conversation);
+
+	/* Conversations have some default tags when created, but we need to
+	 * replace everything so we clear those.
+	 */
+	purple_tags_remove_all(tags);
+
+	/* Reset the statement. */
+	seagull_statement_reset(manager->query_tags, &local_error);
+	if(local_error != NULL) {
+		g_propagate_error(error, local_error);
+
+		return;
+	}
+
+	/* Bind the account id. */
+	seagull_statement_bind_text(manager->query_tags, ":account_id", account_id,
+	                            -1, NULL, &local_error);
+	if(local_error != NULL) {
+		g_propagate_error(error, local_error);
+
+		return;
+	}
+
+	/* Bind the conversation id. */
+	seagull_statement_bind_text(manager->query_tags, ":conversation_id",
+	                            conversation_id, -1, NULL, &local_error);
+	if(local_error != NULL) {
+		g_propagate_error(error, local_error);
+
+		return;
+	}
+
+	while(seagull_statement_step(manager->query_tags, &local_error)) {
+		const char *tag = NULL;
+
+		tag = seagull_statement_column_text(manager->query_tags, "tag",
+		                                    &local_error);
+
+		if(local_error != NULL) {
+			g_propagate_error(error, local_error);
+
+			return;
+		}
+
+		purple_tags_add(tags, tag);
+	}
+
+	if(local_error != NULL) {
+		g_propagate_error(error, local_error);
+	}
+}
+
+static void
+purple_conversation_manager_load_conversations(PurpleConversationManager *manager)
+{
+	PurpleAccountManager *account_manager = NULL;
+	GError *error = NULL;
+
+	seagull_statement_reset(manager->query_properties, &error);
+	if(error != NULL) {
+		g_warning("failed to request query properties statement: %s",
+		          error->message);
+		g_clear_error(&error);
+
+		return;
+	}
+
+	account_manager = purple_account_manager_get_default();
+
+	while(seagull_statement_step(manager->query_properties, &error)) {
+		PurpleAccount *account = NULL;
+		PurpleConversation *conversation = NULL;
+		const char *account_id = NULL;
+		const char *conversation_id = NULL;
+		gint type = PURPLE_CONVERSATION_TYPE_UNSET;
+		gboolean federated = FALSE;
+
+		/* Grab the account id and attempt to find the account. */
+		account_id = seagull_statement_column_text(manager->query_properties,
+		                                           "account_id", &error);
+		if(error != NULL) {
+			g_warning("failed to read account id: %s", error->message);
+			g_clear_error(&error);
+
+			continue;
+		}
+
+		account = purple_account_manager_find_by_id(account_manager,
+		                                            account_id);
+		if(!PURPLE_IS_ACCOUNT(account)) {
+			g_warning("failed to find account for id %s", account_id);
+
+			continue;
+		}
+
+		conversation_id = seagull_statement_column_text(manager->query_properties,
+		                                                "conversation_id",
+		                                                &error);
+		if(error != NULL) {
+			g_warning("failed to read conversation id: %s", error->message);
+			g_clear_error(&error);
+
+			continue;
+		}
+
+		type = seagull_statement_column_enum(manager->query_properties, "type",
+		                                     &error);
+		if(error != NULL) {
+			g_warning("failed to read type: %s", error->message);
+			g_clear_error(&error);
+
+			continue;
+		}
+
+		federated = seagull_statement_column_int(manager->query_properties,
+		                                         "federated", &error);
+		if(error != NULL) {
+			g_warning("failed to read federated: %s", error->message);
+			g_clear_error(&error);
+
+			continue;
+		}
+
+		/* We have all the construct only properties, so let's create the
+		 * instance.
+		 */
+		conversation = g_object_new(
+			PURPLE_TYPE_CONVERSATION,
+			"account", account,
+			"federated", federated,
+			"id", conversation_id,
+			"type", type,
+			NULL);
+
+		/* Now set the rest of the properties. */
+		seagull_statement_column_object(manager->query_properties, "conv_",
+		                                G_OBJECT(conversation), &error);
+
+		if(error != NULL) {
+			g_warning("failed to read object properties: %s", error->message);
+			g_clear_error(&error);
+
+			g_clear_object(&conversation);
+
+			continue;
+		}
+
+		purple_conversation_manager_load_conversation_tags(manager,
+		                                                   conversation,
+		                                                   account_id,
+		                                                   conversation_id,
+		                                                   &error);
+		if(error != NULL) {
+			g_warning("failed to load tags for conversation %s: %s",
+			          conversation_id, error->message);
+
+			g_clear_error(&error);
+			g_clear_object(&conversation);
+
+			continue;
+		}
+
+		/* Finally, add the conversation to the manager. */
+		if(!purple_conversation_manager_add(manager, conversation)) {
+			g_warning("failed to add conversation: %p", conversation);
+		}
+
+		g_clear_object(&conversation);
+	}
+
+	if(error != NULL) {
+		g_warning("failed querying properties: %s", error->message);
+		g_clear_error(&error);
+	}
+}
+
 /******************************************************************************
  * Callbacks
  *****************************************************************************/
@@ -549,9 +777,6 @@
                               G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL,
                                                     pidgin_conversation_manager_list_model_iface_init))
 
-/******************************************************************************
- * GObject Implementation
- *****************************************************************************/
 static void
 purple_conversation_manager_finalize(GObject *obj) {
 	PurpleConversationManager *manager = PURPLE_CONVERSATION_MANAGER(obj);
@@ -562,6 +787,8 @@
 	g_clear_object(&manager->insert_properties);
 	g_clear_object(&manager->insert_tags);
 	g_clear_object(&manager->delete_tags);
+	g_clear_object(&manager->query_properties);
+	g_clear_object(&manager->query_tags);
 
 	if(manager->db != NULL) {
 		GError *error = NULL;
@@ -782,6 +1009,9 @@
 			                          (gpointer *)&default_manager);
 		}
 	}
+
+	/* Load conversations. */
+	purple_conversation_manager_load_conversations(default_manager);
 }
 
 void

mercurial