changeset 27577:6d26258e9f1d

propagate from branch 'im.pidgin.pidgin' (head ac87c285c7056f86005dc157b9870745de471f74) to branch 'im.pidgin.cpw.darkrain42.roster' (head 976d874853ac9745edb77d3cf107b92ebc037c10)
author Paul Aurich <paul@darkrain42.org>
date Mon, 06 Jul 2009 04:37:41 +0000
parents b9da56683499 (diff) b41b69e8b341 (current diff)
children 3bb1085235d0
files libpurple/blist.c libpurple/protocols/jabber/google.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jabber.h libpurple/protocols/jabber/presence.c
diffstat 7 files changed, 154 insertions(+), 167 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/blist.c	Mon Jul 06 04:19:26 2009 +0000
+++ b/libpurple/blist.c	Mon Jul 06 04:37:41 2009 +0000
@@ -2352,16 +2352,14 @@
 	g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
 
 	hb.account = account;
-	hb.name = g_strdup(purple_normalize(account, name));
+	hb.name = purple_normalize(account, name);
 
 	for (group = purplebuddylist->root; group; group = group->next) {
 		hb.group = group;
 		if ((buddy = g_hash_table_lookup(purplebuddylist->buddies, &hb))) {
-			g_free(hb.name);
 			return buddy;
 		}
 	}
-	g_free(hb.name);
 
 	return NULL;
 }
--- a/libpurple/protocols/jabber/google.c	Mon Jul 06 04:19:26 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Mon Jul 06 04:37:41 2009 +0000
@@ -1006,7 +1006,6 @@
 gboolean jabber_google_roster_incoming(JabberStream *js, xmlnode *item)
 {
 	PurpleAccount *account = purple_connection_get_account(js->gc);
-	GSList *list = account->deny;
 	const char *jid = xmlnode_get_attrib(item, "jid");
 	gboolean on_block_list = FALSE;
 
@@ -1024,18 +1023,20 @@
 
  	jid_norm = g_strdup(jabber_normalize(account, jid));
 
-	while (list) {
-		if (!strcmp(jid_norm, (char*)list->data)) {
-			on_block_list = TRUE;
-			break;
-		}
-		list = list->next;
-	}
+	on_block_list = NULL != g_slist_find_custom(account->deny, jid_norm,
+	                                            (GCompareFunc)strcmp);
 
 	if (grt && (*grt == 'H' || *grt == 'h')) {
-		PurpleBuddy *buddy = purple_find_buddy(account, jid_norm);
-		if (buddy)
-			purple_blist_remove_buddy(buddy);
+		/* Hidden; don't show this buddy. */
+		GSList *buddies = purple_find_buddies(account, jid_norm);
+		if (buddies)
+			purple_debug_info("jabber", "Removing %s from local buddy list\n",
+			                  jid_norm);
+
+		for ( ; buddies; buddies = g_slist_delete_link(buddies, buddies)) {
+			purple_blist_remove_buddy(buddies->data);
+		}
+
 		g_free(jid_norm);
 		return FALSE;
 	}
--- a/libpurple/protocols/jabber/jabber.c	Mon Jul 06 04:19:26 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Jul 06 04:37:41 2009 +0000
@@ -1620,7 +1620,8 @@
 
 			break;
 		case JABBER_STREAM_CONNECTED:
-			/* now we can alert the core that we're ready to send status */
+			/* Send initial presence */
+			jabber_presence_send(js, TRUE);
 			purple_connection_set_state(js->gc, PURPLE_CONNECTED);
 			break;
 	}
--- a/libpurple/protocols/jabber/jabber.h	Mon Jul 06 04:19:26 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Mon Jul 06 04:37:41 2009 +0000
@@ -116,7 +116,6 @@
 	char *expected_rspauth;
 
 	GHashTable *buddies;
-	gboolean roster_parsed;
 
 	/*
 	 * This boolean was added to eliminate a heinous bug where we would
--- a/libpurple/protocols/jabber/presence.c	Mon Jul 06 04:19:26 2009 +0000
+++ b/libpurple/protocols/jabber/presence.c	Mon Jul 06 04:37:41 2009 +0000
@@ -59,38 +59,44 @@
 	g_free(chat_full_jid);
 }
 
-void jabber_presence_fake_to_self(JabberStream *js, const PurpleStatus *gstatus) {
-	char *my_base_jid;
+void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status)
+{
+	PurpleAccount *account;
+	const char *username;
 
-	if(!js->user)
-		return;
+	g_return_if_fail(js->user != NULL);
 
-	my_base_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
-	if(purple_find_buddy(js->gc->account, my_base_jid)) {
-		JabberBuddy *jb;
+	account = purple_connection_get_account(js->gc);
+	username = purple_account_get_username(account);
+	if (status == NULL)
+		status = purple_account_get_active_status(account);
+
+	if (purple_find_buddy(account, username)) {
+		JabberBuddy *jb = jabber_buddy_find(js, username, TRUE);
 		JabberBuddyResource *jbr;
-		if((jb = jabber_buddy_find(js, my_base_jid, TRUE))) {
-			JabberBuddyState state;
-			char *msg;
-			int priority;
+		JabberBuddyState state;
+		char *msg;
+		int priority;
 
-			purple_status_to_jabber(gstatus, &state, &msg, &priority);
+		g_return_if_fail(jb != NULL);
+
+		purple_status_to_jabber(status, &state, &msg, &priority);
 
-			if (state == JABBER_BUDDY_STATE_UNAVAILABLE || state == JABBER_BUDDY_STATE_UNKNOWN) {
-				jabber_buddy_remove_resource(jb, js->user->resource);
-			} else {
-				jabber_buddy_track_resource(jb, js->user->resource, priority, state, msg);
-			}
-			if((jbr = jabber_buddy_find_resource(jb, NULL))) {
-				purple_prpl_got_user_status(js->gc->account, my_base_jid, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL);
-			} else {
-				purple_prpl_got_user_status(js->gc->account, my_base_jid, "offline", msg ? "message" : NULL, msg, NULL);
-			}
+		if (state == JABBER_BUDDY_STATE_UNAVAILABLE ||
+				state == JABBER_BUDDY_STATE_UNKNOWN) {
+			jabber_buddy_remove_resource(jb, js->user->resource);
+		} else {
+			jabber_buddy_track_resource(jb, js->user->resource, priority,
+			                            state, msg);
+		}
 
-			g_free(msg);
+		if ((jbr = jabber_buddy_find_resource(jb, NULL))) {
+			purple_prpl_got_user_status(js->gc->account, username, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL);
+		} else {
+			purple_prpl_got_user_status(js->gc->account, username, "offline", msg ? "message" : NULL, msg, NULL);
 		}
+		g_free(msg);
 	}
-	g_free(my_base_jid);
 }
 
 void jabber_set_status(PurpleAccount *account, PurpleStatus *status)
@@ -133,7 +139,7 @@
 	status = purple_presence_get_active_status(p);
 
 	/* we don't want to send presence before we've gotten our roster */
-	if(!js->roster_parsed) {
+	if (js->state != JABBER_STREAM_CONNECTED) {
 		purple_debug_info("jabber", "attempt to send presence before roster retrieved\n");
 		return;
 	}
--- a/libpurple/protocols/jabber/presence.h	Mon Jul 06 04:19:26 2009 +0000
+++ b/libpurple/protocols/jabber/presence.h	Mon Jul 06 04:37:41 2009 +0000
@@ -42,7 +42,7 @@
 void jabber_presence_parse(JabberStream *js, xmlnode *packet);
 void jabber_presence_subscription_set(JabberStream *js, const char *who,
 		const char *type);
-void jabber_presence_fake_to_self(JabberStream *js, const PurpleStatus *status);
+void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status);
 void purple_status_to_jabber(const PurpleStatus *status, JabberBuddyState *state, char **msg, int *priority);
 
 #endif /* PURPLE_JABBER_PRESENCE_H_ */
--- a/libpurple/protocols/jabber/roster.c	Mon Jul 06 04:19:26 2009 +0000
+++ b/libpurple/protocols/jabber/roster.c	Mon Jul 06 04:37:41 2009 +0000
@@ -31,6 +31,19 @@
 
 #include <string.h>
 
+/* Take a list of strings and join them with a ", " separator */
+static gchar *roster_groups_join(GSList *list)
+{
+	GString *out = g_string_new(NULL);
+	for ( ; list; list = list->next) {
+		out = g_string_append(out, (const char *)list->data);
+		if (list->next)
+			out = g_string_append(out, ", ");
+	}
+
+	return g_string_free(out, FALSE);
+}
+
 void jabber_roster_request(JabberStream *js)
 {
 	JabberIq *iq;
@@ -52,35 +65,40 @@
 	g_slist_free(buddies);
 }
 
-static void add_purple_buddies_to_groups(JabberStream *js, const char *jid,
-		const char *alias, GSList *groups)
+static void add_purple_buddy_to_groups(JabberStream *js, const char *jid,
+		const char *alias, GSList *groups, const char *own_jid)
 {
-	GSList *buddies, *g2, *l;
-	gchar *my_bare_jid;
-	GList *pool = NULL;
+	GSList *buddies, *l;
+	GSList *pool = NULL;
 
 	buddies = purple_find_buddies(js->gc->account, jid);
 
-	g2 = groups;
-
 	if(!groups) {
 		if(!buddies)
-			g2 = g_slist_append(g2, g_strdup(_("Buddies")));
+			groups = g_slist_append(groups, g_strdup(_("Buddies")));
 		else {
+			/* TODO: What should we do here? Removing the local buddies
+			 * is wrong, but so is letting the group state get out of sync with
+			 * the server.
+			 */
 			g_slist_free(buddies);
 			return;
 		}
 	}
 
-	my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
-
 	while(buddies) {
 		PurpleBuddy *b = buddies->data;
 		PurpleGroup *g = purple_buddy_get_group(b);
 
-		buddies = g_slist_remove(buddies, b);
+		buddies = g_slist_delete_link(buddies, buddies);
 
-		if((l = g_slist_find_custom(g2, purple_group_get_name(g), (GCompareFunc)strcmp))) {
+		/* XMPP groups are case-sensitive, but libpurple groups are
+		 * case-insensitive. We treat a buddy in both "Friends" and "friends"
+		 * as only being in one group, so if we push changes about the buddy
+		 * to the server, the buddy will be dropped from one of the groups.
+		 */
+		if((l = g_slist_find_custom(groups, purple_group_get_name(g), (GCompareFunc)purple_utf8_strcasecmp))) {
+			/* The buddy is already on the local list. Update info. */
 			const char *servernick, *balias;
 
 			/* Previously stored serverside / buddy-supplied alias */
@@ -89,28 +107,46 @@
 
 			/* Alias from our roster retrieval */
 			balias = purple_buddy_get_local_buddy_alias(b);
-			if(alias && (!balias || strcmp(balias, alias)))
+			if(alias && !purple_strequal(alias, balias))
 				purple_serv_got_private_alias(js->gc, jid, alias);
 			g_free(l->data);
-			g2 = g_slist_delete_link(g2, l);
+			groups = g_slist_delete_link(groups, l);
 		} else {
-			pool = g_list_prepend(pool, b);
+			/* This buddy isn't in the group on the server anymore */
+			pool = g_slist_prepend(pool, b);
 		}
 	}
 
-	while(g2) {
-		PurpleGroup *g = purple_find_group(g2->data);
+	if (pool) {
+		char *tmp = roster_groups_join(pool);
+		purple_debug_info("jabber", "jabber_roster_parse(): Removing %s from "
+		                  "groups: %s\n", jid, tmp);
+		g_free(tmp);
+	}
+
+	if (groups) {
+		char *tmp = roster_groups_join(groups);
+		purple_debug_info("jabber", "jabber_roster_parse(): Adding %s to "
+		                  "groups: %s\n", jid, tmp);
+		g_free(tmp);
+	}
+
+	while(groups) {
+		PurpleGroup *g = purple_find_group(groups->data);
 		PurpleBuddy *b = NULL;
 
+		/* If there are buddies we would otherwise delete, move them to
+		 * the new group (instead of deleting them below)
+		 */
 		if (pool) {
 			b = pool->data;
-			pool = g_list_delete_link(pool, pool);
+			pool = g_slist_delete_link(pool, pool);
 		} else {
 			b = purple_buddy_new(js->gc->account, jid, alias);
 		}
 
 		if(!g) {
-			g = purple_group_new(g2->data);
+			g = purple_group_new(groups->data);
 			purple_blist_add_group(g, NULL);
 		}
 
@@ -120,28 +156,21 @@
 		/* If we just learned about ourself, then fake our status,
 		 * because we won't be receiving a normal presence message
 		 * about ourself. */
-		if(!strcmp(purple_buddy_get_name(b), my_bare_jid)) {
-			PurplePresence *gpresence;
-			PurpleStatus *status;
-			PurpleAccount *account;
+		if(!strcmp(purple_buddy_get_name(b), own_jid))
+			jabber_presence_fake_to_self(js, NULL);
 
-			account = purple_connection_get_account(js->gc);
-			gpresence = purple_account_get_presence(account);
-			status = purple_presence_get_active_status(gpresence);
-			jabber_presence_fake_to_self(js, status);
-		}
-
-		g_free(g2->data);
-		g2 = g_slist_delete_link(g2, g2);
+		g_free(groups->data);
+		groups = g_slist_delete_link(groups, groups);
 	}
 
+	/* Remove this person from all the groups they're no longer in on the
+	 * server */
 	while (pool) {
 		PurpleBuddy *b = pool->data;
 		purple_blist_remove_buddy(b);
-		pool = g_list_delete_link(pool, pool);
+		pool = g_slist_delete_link(pool, pool);
 	}
 
-	g_free(my_bare_jid);
 	g_slist_free(buddies);
 }
 
@@ -149,28 +178,18 @@
                          JabberIqType type, const char *id, xmlnode *query)
 {
 	xmlnode *item, *group;
-
-	if(from) {
-		char *from_norm;
-		gboolean invalid;
-
-		from_norm = g_strdup(jabber_normalize(js->gc->account, from));
+	gchar *own_jid;
 
-		if(!from_norm)
-			return;
-
-		invalid = g_utf8_collate(from_norm,
-				jabber_normalize(js->gc->account,
-					purple_account_get_username(js->gc->account)));
-
-		g_free(from_norm);
-
-		if(invalid)
-			return;
+	if (!jabber_is_own_account(js, from)) {
+		purple_debug_warning("jabber", "Received bogon roster push from %s\n",
+		                     from);
+		return;
 	}
 
 	js->currently_parsing_roster_push = TRUE;
 
+	own_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
+
 	for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item))
 	{
 		const char *jid, *name, *subscription, *ask;
@@ -188,18 +207,11 @@
 			continue;
 
 		if(subscription) {
-			gint me = -1;
-			char *jid_norm;
-			const char *username;
+			gboolean me = FALSE;
 
-			jid_norm = g_strdup(jabber_normalize(js->gc->account, jid));
-			username = purple_account_get_username(js->gc->account);
-			me = g_utf8_collate(jid_norm,
-			                    jabber_normalize(js->gc->account,
-			                                     username));
-			g_free(jid_norm);
+			me = g_str_equal(own_jid, jabber_normalize(js->gc->account, jid));
 
-			if(me == 0)
+			if(me)
 				jb->subscription = JABBER_SUB_BOTH;
 			else if(!strcmp(subscription, "none"))
 				jb->subscription = JABBER_SUB_NONE;
@@ -211,20 +223,9 @@
 				jb->subscription = JABBER_SUB_BOTH;
 			else if(!strcmp(subscription, "remove"))
 				jb->subscription = JABBER_SUB_REMOVE;
-			/* XXX: if subscription is now "from" or "none" we need to
-			 * fake a signoff, since we won't get any presence from them
-			 * anymore */
-			/* YYY: I was going to use this, but I'm not sure it's necessary
-			 * anymore, but it's here in case it is. */
-			/*
-			if ((jb->subscription & JABBER_SUB_FROM) ||
-					(jb->subscription & JABBER_SUB_NONE)) {
-				purple_prpl_got_user_status(js->gc->account, jid, "offline", NULL);
-			}
-			*/
 		}
 
-		if(ask && !strcmp(ask, "subscribe"))
+		if(purple_strequal(ask, "subscribe"))
 			jb->subscription |= JABBER_SUB_PENDING;
 		else
 			jb->subscription &= ~JABBER_SUB_PENDING;
@@ -233,44 +234,44 @@
 			remove_purple_buddies(js, jid);
 		} else {
 			GSList *groups = NULL;
+			gboolean seen_empty = FALSE;
 
 			if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
 				if (!jabber_google_roster_incoming(js, item))
 					continue;
 
 			for(group = xmlnode_get_child(item, "group"); group; group = xmlnode_get_next_twin(group)) {
-				char *group_name;
-
-				if(!(group_name = xmlnode_get_data(group)))
-					group_name = g_strdup("");
+				char *group_name = xmlnode_get_data(group);
 
-				if (g_slist_find_custom(groups, group_name, (GCompareFunc)purple_utf8_strcasecmp) == NULL)
-					groups = g_slist_append(groups, group_name);
-				else
-					g_free(group_name);
+				if (!group_name && !seen_empty) {
+					group_name = g_strdup("");
+					seen_empty = TRUE;
+				}
+
+				groups = g_slist_prepend(groups, group_name);
 			}
-			add_purple_buddies_to_groups(js, jid, name, groups);
+
+			add_purple_buddy_to_groups(js, jid, name, groups, own_jid);
 		}
 	}
 
+	g_free(own_jid);
 	js->currently_parsing_roster_push = FALSE;
 
 	/* if we're just now parsing the roster for the first time,
-	 * then now would be the time to declare ourselves connected and
-	 * send our initial presence */
-	if(!js->roster_parsed) {
-		js->roster_parsed = TRUE;
-		jabber_presence_send(js, TRUE);
+	 * then now would be the time to declare ourselves connected.
+	 */
+	if (js->state != JABBER_STREAM_CONNECTED)
 		jabber_stream_set_state(js, JABBER_STREAM_CONNECTED);
-	}
 }
 
+/* jabber_roster_update frees the GSList* passed in */
 static void jabber_roster_update(JabberStream *js, const char *name,
-		GSList *grps)
+		GSList *groups)
 {
 	PurpleBuddy *b;
 	PurpleGroup *g;
-	GSList *groups = NULL, *l;
+	GSList *l;
 	JabberIq *iq;
 	xmlnode *query, *item, *group;
 	const char *balias;
@@ -281,23 +282,15 @@
 	if(!(b = purple_find_buddy(js->gc->account, name)))
 		return;
 
-	if(grps) {
-		GString *out = g_string_new(NULL);
-		groups = grps;
+	if (groups) {
+		char *tmp = roster_groups_join(groups);
 
-		for (l = groups; l; l = l->next) {
-			out = g_string_append(out, (const char *)l->data);
-			if (l->next)
-				out = g_string_append(out, ", ");
-		}
-
-		purple_debug_info("jabber", "jabber_roster_update(%s): [Source: grps]: groups: %s\n",
-		                  name, out->str);
-		g_string_free(out, TRUE);
-
+		purple_debug_info("jabber", "jabber_roster_update(%s): [Source: "
+		                  "groups]: groups: %s\n", name, tmp);
+		g_free(tmp);
 	} else {
 		GSList *buddies = purple_find_buddies(js->gc->account, name);
-		GString *out = g_string_new(NULL);
+		char *tmp;
 
 		if(!buddies)
 			return;
@@ -307,15 +300,11 @@
 			groups = g_slist_append(groups, (char *)purple_group_get_name(g));
 			buddies = g_slist_remove(buddies, b);
 		}
-		for (l = groups; l; l = l->next) {
-			out = g_string_append(out, (const char *)l->data);
-			if (l->next)
-				out = g_string_append(out, ", ");
-		}
 
+		tmp = roster_groups_join(groups);
 		purple_debug_info("jabber", "jabber_roster_update(%s): [Source: local blist]: groups: %s\n",
-		                  name, out->str);
-		g_string_free(out, TRUE);
+		                  name, tmp);
+		g_free(tmp);
 	}
 
 	iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
@@ -333,8 +322,7 @@
 		xmlnode_insert_data(group, l->data, -1);
 	}
 
-	if(!grps)
-		g_slist_free(groups);
+	g_slist_free(groups);
 
 	if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER) {
 		jabber_google_roster_outgoing(js, query, item);
@@ -351,10 +339,11 @@
 	char *who;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
-	char *my_bare_jid;
+	char *own_jid;
 	const char *name;
 
-	if(!js->roster_parsed)
+	/* If we haven't received the roster yet, ignore any adds */
+	if (js->state != JABBER_STREAM_CONNECTED)
 		return;
 
 	name = purple_buddy_get_name(buddy);
@@ -368,14 +357,9 @@
 
 	jabber_roster_update(js, who, NULL);
 
-	my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
-	if(!strcmp(who, my_bare_jid)) {
-		PurplePresence *gpresence;
-		PurpleStatus *status;
-
-		gpresence = purple_account_get_presence(js->gc->account);
-		status = purple_presence_get_active_status(gpresence);
-		jabber_presence_fake_to_self(js, status);
+	own_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
+	if (g_str_equal(who, own_jid)) {
+		jabber_presence_fake_to_self(js, NULL);
 	} else if(!jb || !(jb->subscription & JABBER_SUB_TO)) {
 		jabber_presence_subscription_set(js, who, "subscribe");
 	} else if((jbr =jabber_buddy_find_resource(jb, NULL))) {
@@ -384,7 +368,7 @@
 				"priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL);
 	}
 
-	g_free(my_bare_jid);
+	g_free(own_jid);
 	g_free(who);
 }
 
@@ -429,7 +413,6 @@
 	                  name, old_group, new_group);
 
 	jabber_roster_update(gc->proto_data, name, groups);
-	g_slist_free(groups);
 }
 
 void jabber_roster_group_rename(PurpleConnection *gc, const char *old_name,
@@ -465,7 +448,6 @@
 		                  purple_buddy_get_name(buddy), purple_group_get_name(group));
 
 		jabber_roster_update(gc->proto_data, name, groups);
-		g_slist_free(groups);
 	} else {
 		JabberIq *iq = jabber_iq_new_query(gc->proto_data, JABBER_IQ_SET,
 				"jabber:iq:roster");