changeset 26765:998f471bb801

merge of '04463c4228e4760066ef026178e08886db366915' and '277dbfa504b60a873008deb330ed734a384e33f5'
author Paul Aurich <paul@darkrain42.org>
date Fri, 01 May 2009 16:09:50 +0000
parents 0e99b80b54c6 (diff) 69d5d6ab4229 (current diff)
children 9331016df8ac
files COPYRIGHT
diffstat 6 files changed, 174 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Fri May 01 16:00:35 2009 +0000
+++ b/COPYRIGHT	Fri May 01 16:09:50 2009 +0000
@@ -216,6 +216,7 @@
 David Jedelsky
 Henry Jen
 Benjamin Kahn
+Jan Kaluza
 Anders Kaseorg
 Praveen Karadakal
 Jaromír Karmazín
--- a/ChangeLog.API	Fri May 01 16:00:35 2009 +0000
+++ b/ChangeLog.API	Fri May 01 16:09:50 2009 +0000
@@ -9,7 +9,10 @@
 		* PURPLE_CONTACT
 		* PURPLE_BUDDY
 		* PURPLE_CHAT
-		* account-actions-changed (see account-signals.dox)
+		* Account signals (see account-signals.dox)
+			* account-actions-changed
+			* account-created
+			* account-destroying
 		* purple_buddy_destroy
 		* purple_buddy_get_protocol_data
 		* purple_buddy_set_protocol_data
--- a/doc/account-signals.dox	Fri May 01 16:00:35 2009 +0000
+++ b/doc/account-signals.dox	Fri May 01 16:09:50 2009 +0000
@@ -1,6 +1,8 @@
 /** @page account-signals Account Signals
 
  @signals
+  @signal account-created
+  @signal account-destroying
   @signal account-added
   @signal account-connecting
   @signal account-removed
@@ -21,6 +23,26 @@
 
  <hr>
 
+ @signaldef account-created
+  @signalproto
+void (*account_created)(PurpleAccount *account);
+  @endsignalproto
+  @signaldesc
+   Emitted when an account is created by calling purple_account_new.
+  @param account The account.
+  @since 2.6.0
+ @endsignaldef
+
+ @signaldef account-destroying
+  @signalproto
+void (*account_destroying)(PurpleAccount *account);
+  @endsignalproto
+  @signaldesc
+   Emitted when an account is about to be destroyed.
+  @param account The account.
+  @since 2.6.0
+ @endsignaldef
+
  @signaldef account-added
   @signalproto
 void (*account_added)(PurpleAccount *account);
@@ -28,6 +50,7 @@
   @signaldesc
    Emitted when an account is added.
   @param account The account that was added.
+  @see purple_accounts_add
  @endsignaldef
 
  @signaldef account-connecting
@@ -46,6 +69,7 @@
   @signaldesc
    Emitted when an account is removed.
   @param account The account that was removed.
+  @see purple_accounts_remove
  @endsignaldef
 
  @signaldef account-disabled
--- a/libpurple/account.c	Fri May 01 16:00:35 2009 +0000
+++ b/libpurple/account.c	Fri May 01 16:09:50 2009 +0000
@@ -954,6 +954,8 @@
 	/* 0 is not a valid privacy setting */
 	account->perm_deny = PURPLE_PRIVACY_ALLOW_ALL;
 
+	purple_signal_emit(purple_accounts_get_handle(), "account-created", account);
+
 	prpl = purple_find_prpl(protocol_id);
 
 	if (prpl == NULL)
@@ -987,6 +989,7 @@
 	g_return_if_fail(account != NULL);
 
 	purple_debug_info("account", "Destroying account %p\n", account);
+	purple_signal_emit(purple_accounts_get_handle(), "account-destroying", account);
 
 	for (l = purple_get_conversations(); l != NULL; l = l->next)
 	{
@@ -2725,6 +2728,14 @@
 										PURPLE_SUBTYPE_ACCOUNT),
 						 purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "account-created",
+						 purple_marshal_VOID__POINTER, NULL, 1,
+						 purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT));
+
+	purple_signal_register(handle, "account-destroying",
+						 purple_marshal_VOID__POINTER, NULL, 1,
+						 purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT));
+
 	purple_signal_register(handle, "account-added",
 						 purple_marshal_VOID__POINTER, NULL, 1,
 						 purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT));
--- a/libpurple/blist.c	Fri May 01 16:00:35 2009 +0000
+++ b/libpurple/blist.c	Fri May 01 16:09:50 2009 +0000
@@ -40,6 +40,14 @@
 static PurpleBlistUiOps *blist_ui_ops = NULL;
 
 static PurpleBuddyList *purplebuddylist = NULL;
+
+/**
+ * A hash table used for efficient lookups of buddies by name.
+ * PurpleAccount* => GHashTable*, with the inner hash table being
+ * struct _purple_hbuddy => PurpleBuddy*
+ */
+static GHashTable *buddies_cache = NULL;
+
 static guint          save_timer = 0;
 static gboolean       blist_loaded = FALSE;
 
@@ -91,6 +99,21 @@
 	g_free(hb);
 }
 
+static void
+purple_blist_buddies_cache_add_account(PurpleAccount *account)
+{
+	GHashTable *account_buddies = g_hash_table_new_full((GHashFunc)_purple_blist_hbuddy_hash,
+						(GEqualFunc)_purple_blist_hbuddy_equal,
+						(GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
+	g_hash_table_insert(buddies_cache, account, account_buddies);
+}
+
+static void
+purple_blist_buddies_cache_remove_account(const PurpleAccount *account)
+{
+	g_hash_table_remove(buddies_cache, account);
+}
+
 
 /*********************************************************************
  * Writing to disk                                                   *
@@ -666,6 +689,7 @@
 PurpleBuddyList *purple_blist_new()
 {
 	PurpleBlistUiOps *ui_ops;
+	GList *account;
 	PurpleBuddyList *gbl = g_new0(PurpleBuddyList, 1);
 	PURPLE_DBUS_REGISTER_POINTER(gbl, PurpleBuddyList);
 
@@ -675,6 +699,14 @@
 					 (GEqualFunc)_purple_blist_hbuddy_equal,
 					 (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
 
+	buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+					 NULL, (GDestroyNotify)g_hash_table_destroy);
+
+	for (account = purple_accounts_get_all(); account != NULL; account = account->next)
+	{
+		purple_blist_buddies_cache_add_account(account->data);
+	}
+
 	if (ui_ops != NULL && ui_ops->new_list != NULL)
 		ui_ops->new_list(gbl);
 
@@ -898,7 +930,8 @@
 void purple_blist_rename_buddy(PurpleBuddy *buddy, const char *name)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	struct _purple_hbuddy *hb;
+	struct _purple_hbuddy *hb, *hb2;
+	GHashTable *account_buddies;
 
 	g_return_if_fail(buddy != NULL);
 
@@ -907,11 +940,21 @@
 	hb->account = buddy->account;
 	hb->group = ((PurpleBlistNode *)buddy)->parent->parent;
 	g_hash_table_remove(purplebuddylist->buddies, hb);
+	
+	account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
+	g_hash_table_remove(account_buddies, hb);
 
 	g_free(hb->name);
 	hb->name = g_strdup(purple_normalize(buddy->account, name));
 	g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
 
+	hb2 = g_new(struct _purple_hbuddy, 1);
+	hb2->name = g_strdup(hb->name);
+	hb2->account = buddy->account;
+	hb2->group = ((PurpleBlistNode *)buddy)->parent->parent;
+
+	g_hash_table_replace(account_buddies, hb2, buddy);
+
 	g_free(buddy->name);
 	buddy->name = g_strdup(name);
 
@@ -1433,7 +1476,8 @@
 	PurpleGroup *g;
 	PurpleContact *c;
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	struct _purple_hbuddy *hb;
+	struct _purple_hbuddy *hb, *hb2;
+	GHashTable *account_buddies;
 
 	g_return_if_fail(buddy != NULL);
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY((PurpleBlistNode*)buddy));
@@ -1503,6 +1547,10 @@
 			hb->account = buddy->account;
 			hb->group = bnode->parent->parent;
 			g_hash_table_remove(purplebuddylist->buddies, hb);
+
+			account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
+			g_hash_table_remove(account_buddies, hb);
+
 			g_free(hb->name);
 			g_free(hb);
 		}
@@ -1549,6 +1597,15 @@
 
 	g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
 
+	account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
+
+	hb2 = g_new(struct _purple_hbuddy, 1);
+	hb2->name = g_strdup(hb->name);
+	hb2->account = buddy->account;
+	hb2->group = ((PurpleBlistNode*)buddy)->parent->parent;
+
+	g_hash_table_replace(account_buddies, hb2, buddy);
+
 	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
 
 	purple_blist_schedule_save();
@@ -1707,18 +1764,31 @@
 			while (bnode) {
 				PurpleBlistNode *next_bnode = bnode->next;
 				PurpleBuddy *b = (PurpleBuddy*)bnode;
-
-				struct _purple_hbuddy *hb = g_new(struct _purple_hbuddy, 1);
+				GHashTable *account_buddies;
+
+				struct _purple_hbuddy *hb, *hb2;
+				
+				hb = g_new(struct _purple_hbuddy, 1);
 				hb->name = g_strdup(purple_normalize(b->account, b->name));
 				hb->account = b->account;
 				hb->group = cnode->parent;
 
 				g_hash_table_remove(purplebuddylist->buddies, hb);
 
+				account_buddies = g_hash_table_lookup(buddies_cache, b->account);
+				g_hash_table_remove(account_buddies, hb);
+
 				if (!purple_find_buddy_in_group(b->account, b->name, g)) {
 					hb->group = gnode;
 					g_hash_table_replace(purplebuddylist->buddies, hb, b);
 
+					hb2 = g_new(struct _purple_hbuddy, 1);
+					hb2->name = g_strdup(hb->name);
+					hb2->account = b->account;
+					hb2->group = gnode;
+
+					g_hash_table_replace(account_buddies, hb2, b);
+
 					if (purple_account_get_connection(b->account))
 						serv_move_buddy(b, (PurpleGroup *)cnode->parent, g);
 				} else {
@@ -1936,6 +2006,7 @@
 	PurpleContact *contact;
 	PurpleGroup *group;
 	struct _purple_hbuddy hb;
+	GHashTable *account_buddies;
 
 	g_return_if_fail(buddy != NULL);
 
@@ -1982,6 +2053,10 @@
 	hb.account = buddy->account;
 	hb.group = gnode;
 	g_hash_table_remove(purplebuddylist->buddies, &hb);
+
+	account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
+	g_hash_table_remove(account_buddies, &hb);
+
 	g_free(hb.name);
 
 	/* Update the UI */
@@ -2256,13 +2331,10 @@
 
 static void find_acct_buddies(gpointer key, gpointer value, gpointer data)
 {
-	struct _purple_hbuddy *hb = key;
 	PurpleBuddy *buddy = value;
-	struct _list_account_buddies *ab = data;
-
-	if (hb->account == ab->account) {
-		ab->list = g_slist_prepend(ab->list, buddy);
-	}
+	GSList **list = data;
+
+	*list = g_slist_prepend(*list, buddy);
 }
 
 GSList *purple_find_buddies(PurpleAccount *account, const char *name)
@@ -2274,7 +2346,6 @@
 	g_return_val_if_fail(purplebuddylist != NULL, NULL);
 	g_return_val_if_fail(account != NULL, NULL);
 
-
 	if ((name != NULL) && (*name != '\0')) {
 		struct _purple_hbuddy hb;
 
@@ -2288,11 +2359,10 @@
 		}
 		g_free(hb.name);
 	} else {
-		struct _list_account_buddies *ab = g_new0(struct _list_account_buddies, 1);
-		ab->account = account;
-		g_hash_table_foreach(purplebuddylist->buddies, find_acct_buddies, ab);
-		ret = ab->list;
-		g_free(ab);
+		GSList *list = NULL;
+		GHashTable *buddies = g_hash_table_lookup(buddies_cache, account);
+		g_hash_table_foreach(buddies, find_acct_buddies, &list);
+		ret = list;
 	}
 
 	return ret;
@@ -2931,6 +3001,16 @@
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
 										PURPLE_SUBTYPE_BLIST_NODE),
 						 purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_signal_connect(purple_accounts_get_handle(), "account-created",
+			handle,
+			PURPLE_CALLBACK(purple_blist_buddies_cache_add_account),
+			NULL);
+
+	purple_signal_connect(purple_accounts_get_handle(), "account-destroying",
+			handle,
+			PURPLE_CALLBACK(purple_blist_buddies_cache_remove_account),
+			NULL);
 }
 
 void
@@ -2951,6 +3031,10 @@
 		node = next_node;
 	}
 	purplebuddylist->root = NULL;
-
+	
+	g_hash_table_destroy(purplebuddylist->buddies);
+	g_hash_table_destroy(buddies_cache);
+
+	purple_signals_disconnect_by_handle(purple_blist_get_handle());
 	purple_signals_unregister_by_instance(purple_blist_get_handle());
 }
--- a/libpurple/protocols/oscar/oscar.c	Fri May 01 16:00:35 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri May 01 16:09:50 2009 +0000
@@ -5122,58 +5122,43 @@
 	aim_ssi_cleanlist(od);
 
 	{ /* If not in server list then prune from local list */
-		PurpleBlistNode *gnode, *cnode, *bnode;
-		PurpleBuddyList *blist;
 		GSList *cur, *next;
-
+		GSList *buddies = purple_find_buddies(account, NULL);
+		
 		/* Buddies */
 		cur = NULL;
-		if ((blist = purple_get_blist()) != NULL) {
-			for (gnode = purple_blist_get_root(); gnode;
-					gnode = purple_blist_node_get_sibling_next(gnode)) {
-				const char *gname;
-				if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
-					continue;
-				g = (PurpleGroup *)gnode;
-				gname = purple_group_get_name(g);
-				for (cnode = purple_blist_node_get_first_child(gnode);
-						cnode;
-						cnode = purple_blist_node_get_sibling_next(cnode)) {
-					if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
-						continue;
-					for (bnode = purple_blist_node_get_first_child(cnode);
-							bnode;
-							bnode = purple_blist_node_get_sibling_next(bnode)) {
-						const char *bname;
-						if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
-							continue;
-						b = (PurpleBuddy *)bnode;
-						bname = purple_buddy_get_name(b);
-						if (purple_buddy_get_account(b) == account) {
-							if (aim_ssi_itemlist_exists(od->ssi.local, bname)) {
-								/* If the buddy is an ICQ user then load his nickname */
-								const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick");
-								char *alias;
-								const char *balias;
-								if (servernick)
-									serv_got_alias(gc, bname, servernick);
-
-								/* Store local alias on server */
-								alias = aim_ssi_getalias(od->ssi.local, gname, bname);
-								balias = purple_buddy_get_local_buddy_alias(b);
-								if (!alias && balias && *balias)
-									aim_ssi_aliasbuddy(od, gname, bname, balias);
-								g_free(alias);
-							} else {
-								purple_debug_info("oscar",
-										"ssi: removing buddy %s from local list\n", bname);
-								/* We can't actually remove now because it will screw up our looping */
-								cur = g_slist_prepend(cur, b);
-							}
-						}
-					}
-				}
+
+		while(buddies) {
+			PurpleGroup *g;
+			const char *gname;
+			const char *bname;
+
+			b = buddies->data;
+			g = purple_buddy_get_group(b);
+			gname = purple_group_get_name(g);
+			bname = purple_buddy_get_name(b);
+
+			if (aim_ssi_itemlist_exists(od->ssi.local, bname)) {
+				/* If the buddy is an ICQ user then load his nickname */
+				const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick");
+				char *alias;
+				const char *balias;
+				if (servernick)
+					serv_got_alias(gc, bname, servernick);
+
+				/* Store local alias on server */
+				alias = aim_ssi_getalias(od->ssi.local, gname, bname);
+				balias = purple_buddy_get_local_buddy_alias(b);
+				if (!alias && balias && *balias)
+					aim_ssi_aliasbuddy(od, gname, bname, balias);
+				g_free(alias);
+			} else {
+				purple_debug_info("oscar",
+						"ssi: removing buddy %s from local list\n", bname);
+				/* We can't actually remove now because it will screw up our looping */
+				cur = g_slist_prepend(cur, b);
 			}
+			buddies = g_slist_delete_link(buddies, buddies);
 		}
 
 		while (cur != NULL) {