Thu, 24 Apr 2025 22:19:39 -0500
IRCv3: check if a member is in a conversation before adding them
Awhile ago we update the NAMREPLY handler to build a separate
Purple.ConversationMembers and then splice that onto the existing one to help
with sorting and other performance issues. However, we didn't check if the
users already existed in the existing list, so we would get duplicates. This
address that.
Also fixed a reference leak.
Testing Done:
Joined a channel and sent `/quote names #channel` multiple times and verified that the member list did not grow to include a bunch of duplicates.
Reviewed at https://reviews.imfreedom.org/r/3987/
/* * Purple - Internet Messaging Library * Copyright (C) Pidgin Developers <devel@pidgin.im> * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this library; if not, see <https://www.gnu.org/licenses/>. */ #if !defined(PURPLE_IRCV3_GLOBAL_HEADER_INSIDE) && \ !defined(PURPLE_IRCV3_COMPILATION) # error "only <libpurple/protocols/ircv3.h> may be included directly" #endif #ifndef PURPLE_IRCV3_CONNECTION_H #define PURPLE_IRCV3_CONNECTION_H #include <glib.h> #include <glib-object.h> #include <gplugin.h> #include <gplugin-native.h> #include <purple.h> #include <ibis.h> #include "purpleircv3version.h" G_BEGIN_DECLS #define PURPLE_IRCV3_TYPE_CONNECTION (purple_ircv3_connection_get_type()) PURPLE_IRCV3_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE(PurpleIRCv3Connection, purple_ircv3_connection, PURPLE_IRCV3, CONNECTION, PurpleConnection) /** * purple_ircv3_connection_register: (skip) * @plugin: The GTypeModule * * Registers the dynamic type using @plugin. * * Since: 3.0 */ G_GNUC_INTERNAL void purple_ircv3_connection_register(GPluginNativePlugin *plugin); /** * purple_ircv3_connection_get_client: * @connection: The instance. * * Gets the [class@Ibis.Client] from @connection. * * Returns: (transfer none): The client instance if connected, otherwise %NULL. * * Since: 3.0 */ PURPLE_IRCV3_AVAILABLE_IN_ALL IbisClient *purple_ircv3_connection_get_client(PurpleIRCv3Connection *connection); /** * purple_ircv3_connection_add_status_message: * @connection: The instance. * @message: The message. * * Adds a message to the status conversation/window for @connection. * * Since: 3.0 */ PURPLE_IRCV3_AVAILABLE_IN_ALL void purple_ircv3_connection_add_status_message(PurpleIRCv3Connection *connection, IbisMessage *message); /** * purple_ircv3_connection_find_or_create_conversation: * @connection: The instance. * @id: The id of the conversation. * * Looks for an existing conversation belonging to @connection and returns it * if found. If not found a new conversation will be created. * * This will only ever return %NULL if @connection is invalid or @id is %NULL. * * Note that ownership of the conversation remains with the default * [class@Purple.ConversationManager]. * * Returns: (transfer none) (nullable): The conversation. * * Since: 3.0 */ PURPLE_IRCV3_AVAILABLE_IN_ALL PurpleConversation *purple_ircv3_connection_find_or_create_conversation(PurpleIRCv3Connection *connection, const char *id); /** * purple_ircv3_connection_find_or_create_contact: * @connection: The instance. * @message: The message to get the contact for. * * Looks for an existing contact from @message belonging to @connection and * returns it if found. If not a new contact will be created. * * This will only ever return %NULL if @connection is invalid or @message is * %NULL. * * Note that the ownership of the contact remains with the default * [class@Purple.ContactManager]. * * Returns: (transfer none): The contact. * * Since: 3.0 */ PURPLE_IRCV3_AVAILABLE_IN_ALL PurpleContact *purple_ircv3_connection_find_or_create_contact(PurpleIRCv3Connection *connection, IbisMessage *message); /** * purple_ircv3_connection_find_or_create_contact_from_nick: * @connection: The instance. * @nick: The nick to find or create a contact for. * * Finds an existing contact or creates one for @nick. * * Returns: (transfer none): The contact. * * Since: 3.0 */ PURPLE_IRCV3_AVAILABLE_IN_ALL PurpleContact *purple_ircv3_connection_find_or_create_contact_from_nick(PurpleIRCv3Connection *connection, const char *nick); /** * purple_ircv3_connection_get_status_conversation: (skip) * @connection: The instance. * * Gets the status conversation from @connection. * * Returns: (transfer none): The status conversation. * * Since: 3.0 */ G_GNUC_INTERNAL PurpleConversation *purple_ircv3_connection_get_status_conversation(PurpleIRCv3Connection *connection); /** * purple_ircv3_connection_write_status_message: (skip) * @connection: The instance. * @ibis_message: The message to write. * @show_command: Whether or not to display the command. * @event: whether the message is an event * * Writes @ibis_message to the status window of @connection. * * Since: 3.0 */ G_GNUC_INTERNAL void purple_ircv3_connection_write_status_message(PurpleIRCv3Connection *connection, IbisMessage *ibis_message, gboolean show_command, gboolean event); G_END_DECLS #endif /* PURPLE_IRCV3_CONNECTION_H */