--- a/libpurple/purpleconversation.c Tue Jun 04 14:06:15 2024 -0500
+++ b/libpurple/purpleconversation.c Wed Jun 05 14:21:07 2024 -0500
@@ -677,6 +677,7 @@
purple_request_close_with_handle(conversation);
+ g_clear_object(&conversation->account); g_clear_pointer(&conversation->id, g_free);
g_clear_object(&conversation->avatar);
g_clear_pointer(&conversation->name, g_free);
--- a/libpurple/purpleprotocolconversation.c Tue Jun 04 14:06:15 2024 -0500
+++ b/libpurple/purpleprotocolconversation.c Wed Jun 05 14:21:07 2024 -0500
@@ -148,6 +148,89 @@
+purple_protocol_conversation_implements_leave_conversation(PurpleProtocolConversation *protocol) + PurpleProtocolConversation *protocol_conversation = NULL; + PurpleProtocolConversationInterface *iface = NULL; + g_return_val_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol), FALSE); + protocol_conversation = PURPLE_PROTOCOL_CONVERSATION(protocol); + iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol_conversation); + if(iface->leave_conversation_async == NULL) { + if(iface->leave_conversation_finish == NULL) { +purple_protocol_conversation_leave_conversation_async(PurpleProtocolConversation *protocol, + PurpleConversation *conversation, + GCancellable *cancellable, + GAsyncReadyCallback callback, + PurpleProtocolConversationInterface *iface = NULL; + g_return_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol)); + g_return_if_fail(PURPLE_IS_CONVERSATION(conversation)); + iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol); + if(iface != NULL && iface->leave_conversation_async != NULL) { + iface->leave_conversation_async(protocol, conversation, cancellable, + task = g_task_new(protocol, cancellable, callback, data); + g_task_return_new_error(task, PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0, + "%s does not implement leave_conversation_async", + G_OBJECT_TYPE_NAME(protocol)); + g_task_set_source_tag(task, + purple_protocol_conversation_leave_conversation_async); +purple_protocol_conversation_leave_conversation_finish(PurpleProtocolConversation *protocol, + PurpleProtocolConversationInterface *iface = NULL; + g_return_val_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol), FALSE); + g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE); + iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol); + if(iface != NULL && iface->leave_conversation_finish != NULL) { + return iface->leave_conversation_finish(protocol, result, error); + if(G_IS_TASK(result)) { + GTask *task = G_TASK(result); + gpointer source = NULL; + source = g_task_get_source_tag(task); + if(source == purple_protocol_conversation_leave_conversation_async) { + return g_task_propagate_boolean(task, error); + g_warning("%s does not implement leave_conversation_finish", + G_OBJECT_TYPE_NAME(protocol)); purple_protocol_conversation_send_message_async(PurpleProtocolConversation *protocol,
PurpleConversation *conversation,
--- a/libpurple/purpleprotocolconversation.h Tue Jun 04 14:06:15 2024 -0500
+++ b/libpurple/purpleprotocolconversation.h Wed Jun 05 14:21:07 2024 -0500
@@ -77,6 +77,9 @@
void (*create_conversation_async)(PurpleProtocolConversation *protocol, PurpleAccount *account, PurpleCreateConversationDetails *details, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
PurpleConversation *(*create_conversation_finish)(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
+ void (*leave_conversation_async)(PurpleProtocolConversation *protocol, PurpleConversation *conversation, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data); + gboolean (*leave_conversation_finish)(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error); void (*send_message_async)(PurpleProtocolConversation *protocol, PurpleConversation *conversation, PurpleMessage *message, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
gboolean (*send_message_finish)(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
@@ -172,6 +175,59 @@
PurpleConversation *purple_protocol_conversation_create_conversation_finish(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
+ * purple_protocol_conversation_implements_leave_conversation: + * @protocol: The instance. + * Checks if @protocol implements + * [vfunc@ProtocolConversation.leave_conversation_async] and + * [vfunc@ProtocolConversation.leave_conversation_finish]. + * Returns: %TRUE if everything is implemented, otherwise %FALSE. +gboolean purple_protocol_conversation_implements_leave_conversation(PurpleProtocolConversation *protocol); + * purple_protocol_conversation_leave_conversation_async: + * @protocol: The instance. + * @conversation: The conversation to leave. + * @cancellable: (nullable): optional GCancellable object, %NULL to ignore. + * @callback: (nullable) (scope async): The callback to call after the + * conversation has been created. + * @data: (nullable): Optional user data to pass to @callback. + * Attempts to leave @conversation. + * This needs to support all conversation types that @protocol supports. + * Call [method@ProtocolConversation.leave_conversation_finish] from @callback + * to determine whether or not leaving was successful. +void purple_protocol_conversation_leave_conversation_async(PurpleProtocolConversation *protocol, PurpleConversation *conversation, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data); + * purple_protocol_conversation_leave_conversation_finish: + * @protocol: The instance. + * @result: The result that was passed to the callback. + * @error: A return address for a #GError. + * Finishes a previous call to + * [method@ProtocolConversation.leave_conversation_async]. + * Returns: %TRUE if the conversation was left successfully, otherwise %FALSE +gboolean purple_protocol_conversation_leave_conversation_finish(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error); * purple_protocol_conversation_send_message_async:
* @protocol: The instance.
* @conversation: The conversation to send a message to.
--- a/libpurple/tests/test_protocol_conversation.c Tue Jun 04 14:06:15 2024 -0500
+++ b/libpurple/tests/test_protocol_conversation.c Wed Jun 05 14:21:07 2024 -0500
@@ -189,6 +189,81 @@
g_test_trap_assert_stderr("*Purple-WARNING*TestPurpleProtocolConversationEmpty*create_conversation_finish*");
+/****************************************************************************** + * Empty Leave Conversation Tests + *****************************************************************************/ +test_purple_protocol_conversation_empty_leave_conversation_async_cb(GObject *source, + G_GNUC_UNUSED gpointer data) + g_assert_true(PURPLE_IS_PROTOCOL_CONVERSATION(source)); + left = purple_protocol_conversation_leave_conversation_finish(PURPLE_PROTOCOL_CONVERSATION(source), + g_assert_error(error, PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0); +test_purple_protocol_conversation_empty_leave_conversation_async(void) { + PurpleAccount *account = NULL; + PurpleConversation *conversation = NULL; + PurpleProtocolConversation *protocol = NULL; + protocol = g_object_new(test_purple_protocol_conversation_empty_get_type(), + account = purple_account_new("test", "test"); + conversation = g_object_new( + PURPLE_TYPE_CONVERSATION, + purple_protocol_conversation_leave_conversation_async(protocol, + test_purple_protocol_conversation_empty_leave_conversation_async_cb, + g_main_context_iteration(NULL, FALSE); + g_assert_finalize_object(conversation); + g_assert_finalize_object(account); + g_assert_finalize_object(protocol); +test_purple_protocol_conversation_empty_leave_conversation_finish(void) { + if(g_test_subprocess()) { + PurpleProtocolConversation *protocol = NULL; + protocol = g_object_new(test_purple_protocol_conversation_empty_get_type(), + task = g_task_new(protocol, NULL, NULL, NULL); + left = purple_protocol_conversation_leave_conversation_finish(protocol, + g_assert_no_error(error); + g_assert_finalize_object(task); + g_assert_finalize_object(protocol); + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_stderr("*Purple-WARNING*TestPurpleProtocolConversationEmpty*leave_conversation_finish*"); test_purple_protocol_conversation_empty_send_message_async(void) {
if(g_test_subprocess()) {
@@ -479,6 +554,9 @@
guint create_conversation_async;
guint create_conversation_finish;
+ guint leave_conversation_async; + guint leave_conversation_finish; guint send_message_async;
guint send_message_finish;
@@ -562,6 +640,46 @@
+test_purple_protocol_conversation_leave_conversation_async(PurpleProtocolConversation *protocol, + PurpleConversation *conversation, + GCancellable *cancellable, + GAsyncReadyCallback callback, + TestPurpleProtocolConversation *test_protocol = NULL; + g_assert_true(PURPLE_IS_CONVERSATION(conversation)); + test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(protocol); + test_protocol->leave_conversation_async += 1; + task = g_task_new(protocol, cancellable, callback, data); + if(test_protocol->should_error) { + GError *error = g_error_new_literal(TEST_PURPLE_PROTOCOL_CONVERSATION_DOMAIN, + g_task_return_error(task, error); + g_task_return_boolean(task, TRUE); +test_purple_protocol_conversation_leave_conversation_finish(PurpleProtocolConversation *protocol, + TestPurpleProtocolConversation *test_protocol = NULL; + test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(protocol); + test_protocol->leave_conversation_finish += 1; + return g_task_propagate_boolean(G_TASK(result), error); test_purple_protocol_conversation_send_message_async(PurpleProtocolConversation *protocol,
PurpleConversation *conversation,
@@ -763,6 +881,9 @@
iface->create_conversation_async = test_purple_protocol_conversation_create_conversation_async;
iface->create_conversation_finish = test_purple_protocol_conversation_create_conversation_finish;
+ iface->leave_conversation_async = test_purple_protocol_conversation_leave_conversation_async; + iface->leave_conversation_finish = test_purple_protocol_conversation_leave_conversation_finish; iface->send_message_async = test_purple_protocol_conversation_send_message_async;
iface->send_message_finish = test_purple_protocol_conversation_send_message_finish;
@@ -792,6 +913,9 @@
protocol->create_conversation_async = 0;
protocol->create_conversation_finish = 0;
+ protocol->leave_conversation_async = 0; + protocol->leave_conversation_finish = 0; protocol->send_message_async = 0;
protocol->send_message_finish = 0;
@@ -920,6 +1044,81 @@
/******************************************************************************
+ * TestProtocolConversation LeaveConversation Tests + *****************************************************************************/ +test_purple_protocol_conversation_implements_leave_conversation(void) { + PurpleProtocolConversation *protocol = NULL; + protocol = g_object_new(test_purple_protocol_conversation_get_type(), + g_assert_true(purple_protocol_conversation_implements_leave_conversation(protocol)); + g_assert_finalize_object(protocol); +test_purple_protocol_conversation_leave_conversation_cb(GObject *obj, + G_GNUC_UNUSED gpointer data) + TestPurpleProtocolConversation *test_protocol = NULL; + PurpleProtocolConversation *protocol = NULL; + protocol = PURPLE_PROTOCOL_CONVERSATION(obj); + test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(obj); + left = purple_protocol_conversation_leave_conversation_finish(protocol, + if(test_protocol->should_error) { + g_assert_error(error, TEST_PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0); + g_assert_no_error(error); +test_purple_protocol_conversation_leave_conversation_normal(gconstpointer data) + TestPurpleProtocolConversation *protocol = NULL; + PurpleAccount *account = NULL; + PurpleConversation *conversation = NULL; + protocol = g_object_new(test_purple_protocol_conversation_get_type(), + protocol->should_error = GPOINTER_TO_INT(data); + account = purple_account_new("test", "test"); + conversation = g_object_new( + PURPLE_TYPE_CONVERSATION, + purple_protocol_conversation_leave_conversation_async(PURPLE_PROTOCOL_CONVERSATION(protocol), + test_purple_protocol_conversation_leave_conversation_cb, + while(g_main_context_iteration(NULL, FALSE)); + g_assert_cmpuint(protocol->leave_conversation_async, ==, 1); + g_assert_cmpuint(protocol->leave_conversation_finish, ==, 1); + g_assert_finalize_object(conversation); + g_assert_finalize_object(account); + g_assert_finalize_object(protocol); +/****************************************************************************** * TestProtocolConversation SendMessage Tests
*****************************************************************************/
@@ -1310,6 +1509,12 @@
g_test_add_func("/protocol-conversation/empty/create-conversation-finish",
test_purple_protocol_conversation_empty_create_conversation_finish);
+ /* Empty leave conversation tests. */ + g_test_add_func("/protocol-conversation/empty/leave-conversation-async", + test_purple_protocol_conversation_empty_leave_conversation_async); + g_test_add_func("/protocol-conversation/empty/leave-conversation-finish", + test_purple_protocol_conversation_empty_leave_conversation_finish); /* Empty send message tests. */
g_test_add_func("/protocol-conversation/empty/send-message-async",
test_purple_protocol_conversation_empty_send_message_async);
@@ -1348,6 +1553,16 @@
test_purple_protocol_conversation_create_conversation_normal);
+ /* Normal leave conversation tests. */ + g_test_add_func("/protocol-conversation/normal/implements-leave-conversation", + test_purple_protocol_conversation_implements_leave_conversation); + g_test_add_data_func("/protocol-conversation/normal/leave-conversation-normal", + GINT_TO_POINTER(FALSE), + test_purple_protocol_conversation_leave_conversation_normal); + g_test_add_data_func("/protocol-conversation/normal/leave-conversation-error", + test_purple_protocol_conversation_leave_conversation_normal); /* Normal send message tests. */
g_test_add_data_func("/protocol-conversation/normal/send-message-normal",