protocols/demo/purpledemoprotocolconversation.c

Tue, 20 Aug 2024 16:10:56 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Tue, 20 Aug 2024 16:10:56 -0500
changeset 42892
ca6c19529734
parent 42888
ed22ea598bec
child 42906
e0d5b79e7431
permissions
-rw-r--r--

Port the Demo protocol plugin to Purple.ConversationMembers

Testing Done:
Greped the build and verified there were no more deprecation warnings in the Demo protocol relating to conversation members.

Also called in the turtles.

Reviewed at https://reviews.imfreedom.org/r/3419/

/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib/gi18n-lib.h>

#include "purpledemoprotocolconversation.h"

#include "purpledemoplugin.h"
#include "purpledemoprotocol.h"

typedef struct {
	PurpleConversation *conversation;
	PurpleMessage *message;
} PurpleDemoProtocolIMInfo;

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
purple_demo_protocol_im_info_free(PurpleDemoProtocolIMInfo *info) {
	g_clear_object(&info->conversation);
	g_clear_object(&info->message);
	g_free(info);
}

static gint
purple_demo_protocol_contact_sort(gconstpointer a, gconstpointer b,
                                  G_GNUC_UNUSED gpointer data)
{
	return purple_contact_info_compare(PURPLE_CONTACT_INFO((gpointer)a),
	                                   PURPLE_CONTACT_INFO((gpointer)b));
}

static char *
purple_demo_protocol_generate_conversation_id(PurpleAccount *account,
                                              PurpleCreateConversationDetails *details)
{
	GChecksum *checksum = NULL;
	GListModel *participants = NULL;
	GListStore *sorted = NULL;
	char *ret = NULL;
	const char *id = NULL;

	/* Sort the participants. */
	sorted = g_list_store_new(PURPLE_TYPE_CONTACT);
	participants = purple_create_conversation_details_get_participants(details);
	for(guint i = 0; i < g_list_model_get_n_items(participants); i++) {
		PurpleContactInfo *info = NULL;

		info = g_list_model_get_item(participants, i);
		g_list_store_insert_sorted(sorted, info,
		                           purple_demo_protocol_contact_sort,
		                           NULL);
		g_clear_object(&info);
	}

	/* Build a checksum of the account and the sorted participants. */
	checksum = g_checksum_new(G_CHECKSUM_SHA256);

	id = purple_account_get_id(account);
	g_checksum_update(checksum, (guchar *)id, -1);

	for(guint i = 0; i < g_list_model_get_n_items(G_LIST_MODEL(sorted)); i++) {
		PurpleContactInfo *info = NULL;

		info = g_list_model_get_item(G_LIST_MODEL(sorted), i);
		id = purple_contact_info_get_id(info);
		g_checksum_update(checksum, (guchar *)id, -1);
		g_clear_object(&info);
	}

	ret = g_strdup(g_checksum_get_string(checksum));

	g_clear_pointer(&checksum, g_checksum_free);
	g_clear_object(&sorted);

	return ret;
}

/******************************************************************************
 * Callbacks
 *****************************************************************************/
static gboolean
purple_demo_protocol_echo_im_cb(gpointer data) {
	PurpleDemoProtocolIMInfo *info = data;

	purple_conversation_write_message(info->conversation, info->message);

	return G_SOURCE_REMOVE;
}

/******************************************************************************
 * PurpleProtocolConversation Implementation
 *****************************************************************************/
static PurpleCreateConversationDetails *
purple_demo_protocol_get_create_conversation_details(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                                     G_GNUC_UNUSED PurpleAccount *account)
{
	return purple_create_conversation_details_new(9);
}

static void
purple_demo_protocol_create_conversation_async(PurpleProtocolConversation *protocol,
                                               PurpleAccount *account,
                                               PurpleCreateConversationDetails *details,
                                               GCancellable *cancellable,
                                               GAsyncReadyCallback callback,
                                               gpointer data)
{
	PurpleConversation *conversation = NULL;
	PurpleConversationManager *manager = NULL;
	PurpleConversationType type = PURPLE_CONVERSATION_TYPE_UNSET;
	GListModel *participants = NULL;
	GTask *task = NULL;
	char *id = NULL;

	task = g_task_new(protocol, cancellable, callback, data);
	g_task_set_source_tag(task,
	                      purple_demo_protocol_create_conversation_async);

	participants = purple_create_conversation_details_get_participants(details);
	if(g_list_model_get_n_items(participants) == 1) {
		type = PURPLE_CONVERSATION_TYPE_DM;
	} else {
		type = PURPLE_CONVERSATION_TYPE_GROUP_DM;
	}
	id = purple_demo_protocol_generate_conversation_id(account, details);

	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"id", id,
		"type", type,
		NULL);
	g_clear_pointer(&id, g_free);

	for(guint i = 0; i < g_list_model_get_n_items(participants); i++) {
		PurpleContactInfo *info = NULL;
		PurpleConversationMembers *members = NULL;

		info = g_list_model_get_item(participants, i);
		members = purple_conversation_get_members(conversation);
		purple_conversation_members_add_member(members, info, FALSE, NULL);
		g_clear_object(&info);
	}
	g_clear_object(&details);

	manager = purple_conversation_manager_get_default();
	if(!purple_conversation_manager_register(manager, conversation)) {
		g_task_return_new_error(task, PURPLE_DEMO_DOMAIN, 0,
		                        _("This conversation already exists."));
		g_clear_object(&task);

		return;
	}

	g_task_return_pointer(task, conversation, g_object_unref);

	g_clear_object(&task);
}

static PurpleConversation *
purple_demo_protocol_create_conversation_finish(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                                GAsyncResult *result,
                                                GError **error)
{
	GTask *task = G_TASK(result);

	g_return_val_if_fail(g_task_get_source_tag(task) ==
	                     purple_demo_protocol_create_conversation_async,
	                     NULL);

	return g_task_propagate_pointer(task, error);
}

static void
purple_demo_protocol_send_message_async(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                        PurpleConversation *conversation,
                                        PurpleMessage *message,
                                        GCancellable *cancellable,
                                        GAsyncReadyCallback callback,
                                        gpointer data)
{
	GTask *task = NULL;

	if(purple_conversation_is_dm(conversation)) {
		PurpleAccount *account = NULL;
		PurpleContact *contact = NULL;
		PurpleContactInfo *info = NULL;
		PurpleContactManager *manager = NULL;
		PurpleConversationMembers *members = NULL;

		account = purple_conversation_get_account(conversation);
		members = purple_conversation_get_members(conversation);

		manager = purple_contact_manager_get_default();

		/* Check if this dm is with echo. */
		contact = purple_contact_manager_find_with_username(manager, account,
		                                                    "Echo");
		info = PURPLE_CONTACT_INFO(contact);
		if(purple_conversation_members_has_member(members, info, NULL)) {
			PurpleDemoProtocolIMInfo *info = NULL;
			PurpleMessageFlags flags;

			flags = purple_message_get_flags(message);
			flags &= ~PURPLE_MESSAGE_SEND;
			flags |= PURPLE_MESSAGE_RECV;

			info = g_new(PurpleDemoProtocolIMInfo, 1);
			info->conversation = g_object_ref(conversation);
			info->message = g_object_new(
				PURPLE_TYPE_MESSAGE,
				"author", "Echo",
				"flags", flags,
				"contents", purple_message_get_contents(message),
				NULL);

			g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
			                purple_demo_protocol_echo_im_cb, info,
			                (GDestroyNotify)purple_demo_protocol_im_info_free);
		}

		/* Check if this dm is with aegina. */
		contact = purple_contact_manager_find_with_username(manager, account,
		                                                    "Aegina");
		info = PURPLE_CONTACT_INFO(contact);
		if(purple_conversation_members_has_member(members, info, NULL)) {
			PurpleDemoProtocolIMInfo *info = g_new(PurpleDemoProtocolIMInfo, 1);
			const char *author = purple_message_get_author_name(message);
			const char *contents = NULL;

			if(purple_strequal(author, "Hades")) {
				contents = "🫥️";
			} else {
				/* TRANSLATORS: This is a reference to the Cap of Invisibility owned by
				 * various Greek gods, such as Hades, as mentioned. */
				contents = _("Don't tell Hades I have his Cap");
			}

			info->conversation = g_object_ref(conversation);
			info->message = g_object_new(
				PURPLE_TYPE_MESSAGE,
				"author", author,
				"contents", contents,
				"flags", PURPLE_MESSAGE_SEND,
				NULL);

			g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, purple_demo_protocol_echo_im_cb,
			                info, (GDestroyNotify)purple_demo_protocol_im_info_free);
		}
	}

	purple_conversation_write_message(conversation, message);

	task = g_task_new(protocol, cancellable, callback, data);
	g_task_return_boolean(task, TRUE);

	g_clear_object(&task);
}

static gboolean
purple_demo_protocol_send_message_finish(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                         GAsyncResult *result,
                                         GError **error)
{
	g_return_val_if_fail(G_IS_TASK(result), FALSE);

	return g_task_propagate_boolean(G_TASK(result), error);
}

void
purple_demo_protocol_conversation_init(PurpleProtocolConversationInterface *iface) {
	iface->get_create_conversation_details =
		purple_demo_protocol_get_create_conversation_details;
	iface->create_conversation_async =
		purple_demo_protocol_create_conversation_async;
	iface->create_conversation_finish =
		purple_demo_protocol_create_conversation_finish;

	iface->send_message_async = purple_demo_protocol_send_message_async;
	iface->send_message_finish = purple_demo_protocol_send_message_finish;
}

mercurial