changeset 26602:0cbdc2639ce2

Add purple_{buddy,chat,group,contact}_destroy to the blist API, free blist data on libpurple unload. Thanks to Nick Hebner for this. References #8683 committer: Ethan Blanton <elb@pidgin.im>
author Nick Hebner <hebnern@gmail.com>
date Thu, 09 Apr 2009 17:33:20 +0000
parents 00f13da82914
children 6b0c6a370cda
files ChangeLog ChangeLog.API libpurple/blist.c libpurple/blist.h
diffstat 4 files changed, 132 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Apr 09 01:59:24 2009 +0000
+++ b/ChangeLog	Thu Apr 09 17:33:20 2009 +0000
@@ -9,6 +9,9 @@
 	* Removed the unmaintained and unneeded toc protocol plugin.
 	* Fixed NTLM authentication on big-endian systems.
 
+	libpurple:
+	* Various memory cleanups when unloading libpurple. (Nick Hebner)
+
 	XMPP:
 	* Add voice & video support with Jingle (XEP-0166, 0167, 0176, & 0177),
 	  and voice support with GTalk and GMail. (Mike "Maiku" Ruprecht)
--- a/ChangeLog.API	Thu Apr 09 01:59:24 2009 +0000
+++ b/ChangeLog.API	Thu Apr 09 17:33:20 2009 +0000
@@ -9,6 +9,7 @@
 		* PURPLE_CONTACT
 		* PURPLE_BUDDY
 		* PURPLE_CHAT
+		* purple_buddy_destroy
 		* purple_buddy_get_protocol_data
 		* purple_buddy_set_protocol_data
 		* purple_buddy_get_local_buddy_alias
@@ -17,9 +18,12 @@
 		* purple_blist_set_ui_data
 		* purple_blist_node_get_ui_data
 		* purple_blist_node_set_ui_data
+		* purple_chat_destroy
 		* purple_connection_get_protocol_data
 		* purple_connection_set_protocol_data
+		* purple_contact_destroy
 		* purple_global_proxy_set_info
+		* purple_group_destroy
 		* purple_log_get_activity_score
 		* purple_network_force_online
 		* purple_network_set_stun_server
--- a/libpurple/blist.c	Thu Apr 09 01:59:24 2009 +0000
+++ b/libpurple/blist.c	Thu Apr 09 17:33:20 2009 +0000
@@ -1202,6 +1202,16 @@
 	return chat;
 }
 
+void
+purple_chat_destroy(PurpleChat *chat)
+{
+	g_hash_table_destroy(chat->components);
+	g_hash_table_destroy(chat->node.settings);
+	g_free(chat->alias);
+	PURPLE_DBUS_UNREGISTER_POINTER(chat);
+	g_free(chat);
+}
+
 PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
@@ -1229,6 +1239,40 @@
 }
 
 void
+purple_buddy_destroy(PurpleBuddy *buddy)
+{
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
+
+	/*
+	 * Tell the owner PRPL that we're about to free the buddy so it
+	 * can free proto_data
+	 */
+	prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	if (prpl_info && prpl_info->buddy_free)
+		prpl_info->buddy_free(buddy);
+
+	/* Delete the node */
+	purple_buddy_icon_unref(buddy->icon);
+	g_hash_table_destroy(buddy->node.settings);
+	purple_presence_destroy(buddy->presence);
+	g_free(buddy->name);
+	g_free(buddy->alias);
+	g_free(buddy->server_alias);
+
+	PURPLE_DBUS_UNREGISTER_POINTER(buddy);
+	g_free(buddy);
+
+	/* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can
+	 * g_object_ref() it when connecting the callback and
+	 * g_object_unref() it in the handler.  That way, it won't
+	 * get freed while the timeout is pending and this line can
+	 * be removed. */
+	while (g_source_remove_by_user_data((gpointer *)buddy));
+}
+
+void
 purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon)
 {
 	g_return_if_fail(buddy != NULL);
@@ -1519,6 +1563,14 @@
 	return contact;
 }
 
+void
+purple_contact_destroy(PurpleContact *contact)
+{
+	g_hash_table_destroy(contact->node.settings);
+	PURPLE_DBUS_UNREGISTER_POINTER(contact);
+	g_free(contact);
+}
+
 void purple_contact_set_alias(PurpleContact *contact, const char *alias)
 {
 	purple_blist_alias_contact(contact,alias);
@@ -1588,6 +1640,15 @@
 	return group;
 }
 
+void
+purple_group_destroy(PurpleGroup *group)
+{
+	g_hash_table_destroy(group->node.settings);
+	g_free(group->name);
+	PURPLE_DBUS_UNREGISTER_POINTER(group);
+	g_free(group);
+}
+
 void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
@@ -1848,9 +1909,7 @@
 			ops->remove(purplebuddylist, node);
 
 		/* Delete the node */
-		g_hash_table_destroy(contact->node.settings);
-		PURPLE_DBUS_UNREGISTER_POINTER(contact);
-		g_free(contact);
+		purple_contact_destroy(contact);
 	}
 }
 
@@ -1861,8 +1920,6 @@
 	PurpleContact *contact;
 	PurpleGroup *group;
 	struct _purple_hbuddy hb;
-	PurplePlugin *prpl;
-	PurplePluginProtocolInfo *prpl_info = NULL;
 
 	g_return_if_fail(buddy != NULL);
 
@@ -1918,33 +1975,7 @@
 	/* Signal that the buddy has been removed before freeing the memory for it */
 	purple_signal_emit(purple_blist_get_handle(), "buddy-removed", buddy);
 
-	/*
-	 * Tell the owner PRPL that we're about to free the buddy so it
-	 * can free proto_data
-	 */
-	prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
-	if (prpl)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-	if (prpl_info && prpl_info->buddy_free)
-		prpl_info->buddy_free(buddy);
-
-	/* Delete the node */
-	purple_buddy_icon_unref(buddy->icon);
-	g_hash_table_destroy(buddy->node.settings);
-	purple_presence_destroy(buddy->presence);
-	g_free(buddy->name);
-	g_free(buddy->alias);
-	g_free(buddy->server_alias);
-
-	PURPLE_DBUS_UNREGISTER_POINTER(buddy);
-	g_free(buddy);
-
-	/* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can
-	 * g_object_ref() it when connecting the callback and
-	 * g_object_unref() it in the handler.  That way, it won't
-	 * get freed while the timeout is pending and this line can
-	 * be removed. */
-	while (g_source_remove_by_user_data((gpointer *)buddy));
+	purple_buddy_destroy(buddy);
 
 	/* If the contact is empty then remove it */
 	if ((contact != NULL) && !cnode->child)
@@ -1988,11 +2019,7 @@
 		ops->remove(purplebuddylist, node);
 
 	/* Delete the node */
-	g_hash_table_destroy(chat->components);
-	g_hash_table_destroy(chat->node.settings);
-	g_free(chat->alias);
-	PURPLE_DBUS_UNREGISTER_POINTER(chat);
-	g_free(chat);
+	purple_chat_destroy(chat);
 }
 
 void purple_blist_remove_group(PurpleGroup *group)
@@ -2033,10 +2060,7 @@
 	}
 
 	/* Delete the node */
-	g_hash_table_destroy(group->node.settings);
-	g_free(group->name);
-	PURPLE_DBUS_UNREGISTER_POINTER(group);
-	g_free(group);
+	purple_group_destroy(group);
 }
 
 PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact)
@@ -2587,6 +2611,28 @@
 }
 
 static void
+purple_blist_node_destroy(PurpleBlistNode *node)
+{
+	PurpleBlistNode *child, *next_child;
+  
+	child = node->child;
+	while (child) {
+		next_child = child->next;
+		purple_blist_node_destroy(child);
+		child = next_child;
+	}
+
+	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+		purple_buddy_destroy((PurpleBuddy*)node);
+	else if (PURPLE_BLIST_NODE_IS_CHAT(node))
+		purple_chat_destroy((PurpleChat*)node);
+	else if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+		purple_contact_destroy((PurpleContact*)node);
+	else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+		purple_group_destroy((PurpleGroup*)node);
+}
+
+static void
 purple_blist_node_setting_free(gpointer data)
 {
 	PurpleValue *value;
@@ -2874,12 +2920,20 @@
 void
 purple_blist_uninit(void)
 {
-	if (save_timer != 0)
-	{
+	PurpleBlistNode *node, *next_node;
+
+	if (save_timer != 0) {
 		purple_timeout_remove(save_timer);
 		save_timer = 0;
 		purple_blist_sync();
 	}
 
+	node = purple_blist_get_root();
+	while (node) {
+		next_node = node->next;
+		purple_blist_node_destroy(node);
+		node = next_node;
+	}
+
 	purple_signals_unregister_by_instance(purple_blist_get_handle());
 }
--- a/libpurple/blist.h	Thu Apr 09 01:59:24 2009 +0000
+++ b/libpurple/blist.h	Thu Apr 09 17:33:20 2009 +0000
@@ -478,6 +478,13 @@
 PurpleChat *purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components);
 
 /**
+ * Destroys a chat
+ *
+ * @param chat       The chat to destroy
+ */
+void purple_chat_destroy(PurpleChat *chat);
+
+/**
  * Adds a new chat to the buddy list.
  *
  * The chat will be inserted right after node or appended to the end
@@ -501,6 +508,13 @@
 PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias);
 
 /**
+ * Destroys a buddy
+ *
+ * @param buddy     The buddy to destroy
+ */
+void purple_buddy_destroy(PurpleBuddy *buddy);
+
+/**
  * Sets a buddy's icon.
  *
  * This should only be called from within Purple. You probably want to
@@ -611,6 +625,13 @@
 PurpleGroup *purple_group_new(const char *name);
 
 /**
+ * Destroys a group
+ *
+ * @param group  The group to destroy
+*/
+void purple_group_destroy(PurpleGroup *group);
+
+/**
  * Adds a new group to the buddy list.
  *
  * The new group will be inserted after insert or prepended to the list if
@@ -629,6 +650,13 @@
 PurpleContact *purple_contact_new(void);
 
 /**
+ * Destroys a contact
+ *
+ * @param contact  The contact to destroy
+ */
+void purple_contact_destroy(PurpleContact *contact);
+
+/**
  * Adds a new contact to the buddy list.
  *
  * The new contact will be inserted after insert or prepended to the list if