Wed, 16 Jul 2025 01:08:36 -0500
Delete conversations from the database when the user leaves them
Testing Done:
Left some remembered IRC channels and restarted and verified that they weren't restored. Also changed the topic in an existing room to verify that deleting tags in a save worked fine.
And of coursed, called in the turtles.
Bugs closed: PIDGIN-17989
Reviewed at https://reviews.imfreedom.org/r/4064/
--- a/libpurple/purpleconversationmanager.c Tue Jul 15 03:10:33 2025 -0500 +++ b/libpurple/purpleconversationmanager.c Wed Jul 16 01:08:36 2025 -0500 @@ -62,6 +62,7 @@ SeagullSqlite3 *db; gboolean database_initialized; + SeagullStatement *delete_properties; SeagullStatement *delete_tags; SeagullStatement *insert_properties; SeagullStatement *insert_tags; @@ -119,10 +120,22 @@ } stmt = seagull_statement_new_from_resource(manager->db, + RESOURCE_PATH "/statements/delete-properties.sql", + &error); + if(error != NULL) { + g_warning("failed to load delete-properties statement: %s", + error->message); + g_clear_error(&error); + + return; + } + manager->delete_properties = stmt; + + stmt = seagull_statement_new_from_resource(manager->db, RESOURCE_PATH "/statements/delete-tags.sql", &error); if(error != NULL) { - g_warning("failed to loaded delete-tags statement: %s", + g_warning("failed to load delete-tags statement: %s", error->message); g_clear_error(&error); @@ -134,7 +147,7 @@ RESOURCE_PATH "/statements/insert-properties.sql", &error); if(error != NULL) { - g_warning("failed to loaded insert-properties statement: %s", + g_warning("failed to load insert-properties statement: %s", error->message); g_clear_error(&error); @@ -146,7 +159,7 @@ RESOURCE_PATH "/statements/insert-tags.sql", &error); if(error != NULL) { - g_warning("failed to loaded insert-tags statement: %s", + g_warning("failed to load insert-tags statement: %s", error->message); g_clear_error(&error); @@ -158,7 +171,7 @@ RESOURCE_PATH "/statements/query-properties.sql", &error); if(error != NULL) { - g_warning("failed to loaded query-properties statement: %s", + g_warning("failed to load query-properties statement: %s", error->message); g_clear_error(&error); @@ -170,7 +183,7 @@ RESOURCE_PATH "/statements/query-tags.sql", &error); if(error != NULL) { - g_warning("failed to loaded query-tags statement: %s", + g_warning("failed to load query-tags statement: %s", error->message); g_clear_error(&error); @@ -203,6 +216,200 @@ } static gboolean +purple_conversation_manager_delete_conversation_tags(PurpleConversationManager *manager, + const char *conversation_id, + const char *account_id, + GError **error) +{ + GError *local_error = NULL; + gboolean success = FALSE; + + g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), FALSE); + g_return_val_if_fail(!purple_strempty(conversation_id), FALSE); + g_return_val_if_fail(!purple_strempty(account_id), FALSE); + + /* Clear any existing usage. */ + if(!seagull_statement_clear_bindings(manager->delete_tags, error)) { + return FALSE; + } + + if(!seagull_statement_reset(manager->delete_tags, error)) { + return FALSE; + } + + /* Bind the conversation id to the delete statement. */ + success = seagull_statement_bind_text(manager->delete_tags, + ":conversation_id", + conversation_id, + -1, + NULL, + error); + if(!success) { + return FALSE; + } + + /* Bind the account id to the delete statement. */ + success = seagull_statement_bind_text(manager->delete_tags, + ":account_id", + account_id, + -1, + NULL, + error); + if(!success) { + return FALSE; + } + + seagull_statement_step(manager->delete_tags, &local_error); + if(local_error != NULL) { + g_propagate_error(error, local_error); + + return FALSE; + } + + return TRUE; +} + +static gboolean +purple_conversation_manager_delete_conversation_properties(PurpleConversationManager *manager, + const char *conversation_id, + const char *account_id, + GError **error) +{ + GError *local_error = NULL; + gboolean success = FALSE; + + g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), FALSE); + g_return_val_if_fail(!purple_strempty(conversation_id), FALSE); + g_return_val_if_fail(!purple_strempty(account_id), FALSE); + + /* Clear any existing usage. */ + if(!seagull_statement_clear_bindings(manager->delete_properties, error)) { + return FALSE; + } + + if(!seagull_statement_reset(manager->delete_properties, error)) { + return FALSE; + } + + /* Bind the conversation id to the delete statement. */ + success = seagull_statement_bind_text(manager->delete_properties, + ":conversation_id", + conversation_id, + -1, + NULL, + error); + if(!success) { + return FALSE; + } + + /* Bind the account id to the delete statement. */ + success = seagull_statement_bind_text(manager->delete_properties, + ":account_id", + account_id, + -1, + NULL, + error); + if(!success) { + return FALSE; + } + + seagull_statement_step(manager->delete_properties, &local_error); + if(local_error != NULL) { + g_propagate_error(error, local_error); + + return FALSE; + } + + return TRUE; +} + +static void +purple_conversation_manager_delete_conversation(PurpleConversationManager *manager, + PurpleConversation *conversation) +{ + PurpleAccount *account = NULL; + GError *error = NULL; + const char *conversation_id = NULL; + const char *account_id = NULL; + gboolean success = FALSE; + + if(!manager->database_initialized) { + return; + } + + /* Start a transaction so that everything is atomic. */ + if(!seagull_transaction_begin(manager->db, &error)) { + g_warning("failed to begin transaction to delete conversation: %s", + error->message); + g_clear_error(&error); + + return; + } + + /* Grab the conversation and account ids as we need them for everything. */ + conversation_id = purple_conversation_get_id(conversation); + account = purple_conversation_get_account(conversation); + account_id = purple_account_get_id(account); + + /* Delete the properties. We do this first, because it is used to restore + * conversations. So if it fails to delete, we might be able to salvage a + * rejoin as the other data still exists. This would then allow the user + * to attempt to leave again, and hopefully it'll work that time. + */ + success = purple_conversation_manager_delete_conversation_properties(manager, + conversation_id, + account_id, + &error); + if(error != NULL || !success) { + const char *message = "unknown reason"; + + if(error != NULL) { + message = error->message; + } + + g_warning("failed to delete properties: %s", message); + g_clear_error(&error); + + if(!seagull_transaction_rollback(manager->db, &error)) { + g_warning("failed to rollback transaction: %s", error->message); + g_clear_error(&error); + } + + return; + } + + /* Delete the tags. */ + success = purple_conversation_manager_delete_conversation_tags(manager, + conversation_id, + account_id, + &error); + if(error != NULL || !success) { + const char *message = "unknown reason"; + + if(error != NULL) { + message = error->message; + } + + g_warning("failed to delete tags: %s", message); + g_clear_error(&error); + + if(!seagull_transaction_rollback(manager->db, &error)) { + g_warning("failed to rollback transaction: %s", error->message); + g_clear_error(&error); + } + + return; + } + + /* Commit the transaction. */ + if(!seagull_transaction_commit(manager->db, &error)) { + g_warning("failed to commit transaction to delete conversation: %s", + error->message); + g_clear_error(&error); + } +} + +static gboolean purple_conversation_manager_save_conversation_properties(PurpleConversationManager *manager, PurpleConversation *conversation) { @@ -315,41 +522,20 @@ account = purple_conversation_get_account(conversation); account_id = purple_account_get_id(account); - /* Bind the conversation id to the delete statement. */ - success = seagull_statement_bind_text(manager->delete_tags, - ":conversation_id", - conversation_id, - -1, - NULL, - &error); - if(!success) { - g_warning("failed to bind conversation_id to delete tags: %s", - error->message); + /* Delete the existing tags. */ + success = purple_conversation_manager_delete_conversation_tags(manager, + conversation_id, + account_id, + &error); + if(error != NULL) { + g_warning("failed to delete tags: %s", error->message); g_clear_error(&error); return FALSE; } - /* Bind the account id to the delete statement. */ - success = seagull_statement_bind_text(manager->delete_tags, - ":account_id", - account_id, - -1, - NULL, - &error); if(!success) { - g_warning("failed to bind account_id to delete tags: %s", - error->message); - g_clear_error(&error); - - return FALSE; - } - - /* Delete the existing tags, since we just replace everything. */ - seagull_statement_step(manager->delete_tags, &error); - if(error != NULL) { - g_warning("failed to delete tags: %s", error->message); - g_clear_error(&error); + g_warning("failed to delete tags: unknown reason"); return FALSE; } @@ -476,21 +662,6 @@ /* Save the conversation tags. */ success = purple_conversation_manager_save_conversation_tags(manager, conversation); - if(!seagull_statement_clear_bindings(manager->delete_tags, - &error)) - { - g_warning("failed to clear bindings on the delete tags statement: " - "%s", - error->message); - g_clear_error(&error); - } - - if(!seagull_statement_reset(manager->delete_tags, &error)) { - g_warning("failed to reset the delete tags statement: %s", - error->message); - g_clear_error(&error); - } - if(!seagull_statement_clear_bindings(manager->insert_tags, &error)) { @@ -784,9 +955,10 @@ g_clear_pointer(&manager->conversations, g_ptr_array_unref); g_clear_pointer(&manager->filename, g_free); + g_clear_object(&manager->delete_properties); + g_clear_object(&manager->delete_tags); 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); @@ -1203,6 +1375,9 @@ g_list_model_items_changed(G_LIST_MODEL(manager), position, 1, 0); g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_N_ITEMS]); + /* Finally remove it from the database. */ + purple_conversation_manager_delete_conversation(manager, conversation); + g_object_unref(conversation); return TRUE;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/resources/conversationmanager/statements/delete-properties.sql Wed Jul 16 01:08:36 2025 -0500 @@ -0,0 +1,5 @@ +DELETE FROM conversations +WHERE + conversation_id = :conversation_id +AND + account_id = :account_id
--- a/libpurple/resources/libpurple.gresource.xml Tue Jul 15 03:10:33 2025 -0500 +++ b/libpurple/resources/libpurple.gresource.xml Wed Jul 16 01:08:36 2025 -0500 @@ -2,6 +2,7 @@ <gresources> <gresource prefix="/im/pidgin/libpurple/"> <file compressed="true">conversationmanager/migrations/01-initial.sql</file> + <file compressed="true">conversationmanager/statements/delete-properties.sql</file> <file compressed="true">conversationmanager/statements/delete-tags.sql</file> <file compressed="true">conversationmanager/statements/insert-properties.sql</file> <file compressed="true">conversationmanager/statements/insert-tags.sql</file>