changeset 28751:bd8a1dad7f1c

merged with im.pidgin.pidgin
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Wed, 14 Oct 2009 13:26:08 +0900
parents 659dde8c53a9 (current diff) 912b9999bbe6 (diff)
children 75aab8587e90
files libpurple/protocols/jabber/google.c libpurple/protocols/jabber/jabber.c libpurple/protocols/yahoo/libymsg.c pidgin/gtkutils.c
diffstat 18 files changed, 665 insertions(+), 312 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Oct 11 17:36:59 2009 +0900
+++ b/ChangeLog	Wed Oct 14 13:26:08 2009 +0900
@@ -1,7 +1,17 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 2.6.3 (??/??/20??):
+	General:
+	* New 'plugins' sub-command to 'debug' command (i.e. '/debug plugins')
+	  to announce the list of loaded plugins (in both Finch and Pidgin).
+	* Fix a crash when performing DNS queries on Unixes that use the
+	  blocking DNS lookups.  (Brian Lu)
+	* Fix building the GnuTLS plugin with older versions of GnuTLS.
+	* Fix DNS TXT query resolution.
+
 	XMPP:
+	* Users connecting to Google Talk now have an "Initiate Chat" context menu
+	  option for their buddies.  (Eion Robb)
 	* Fix a crash when attempting to validate an invalid JID.
 	* Resolve an issue when connecting to iChat Server when no resource
 	  is specified.
@@ -9,13 +19,11 @@
 
 	Yahoo:
 	* Fix sending /buzz.
-
-	General:
-	* New 'plugins' sub-command to 'debug' command (i.e. '/debug plugins')
-	  to announce the list of loaded plugins (in both Finch and Pidgin).
-	* Fix a crash when performing DNS queries on Unixes that use the
-	  blocking DNS lookups.  (Brian Lu)
-	* Fix building the GnuTLS plugin with older versions of GnuTLS.
+	* Fix blocking behavior for federated (MSN/OCS/Sametime) service users.
+	  (Jason Cohen)
+	* Add support for adding OCS and Sametime buddies.  OCS users are added
+	  as "ocs/user@domain.tld" and Sametime users are added as
+	  "ibm/sametime_id".  (Jason Cohen)
 
 	Finch:
 	* The TinyURL plugin now creates shorter URLs for long non-conversation
--- a/libpurple/dnssrv.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/dnssrv.c	Wed Oct 14 13:26:08 2009 +0900
@@ -361,8 +361,12 @@
 		/* TODO: Check return value */
 		if (query.type == T_SRV)
 			write(out, ret->data, sizeof(PurpleSrvResponse));
-		if (query.type == T_TXT)
-			write(out, ret->data, sizeof(PurpleTxtResponse));
+		if (query.type == T_TXT) {
+			PurpleTxtResponse *response = ret->data;
+			gsize l = strlen(response->content) + 1 /* null byte */;
+			write(out, &l, sizeof(l));
+			write(out, response->content, l);
+		}
 
 		g_free(ret->data);
 		ret = g_list_remove(ret, ret->data);
@@ -429,21 +433,38 @@
 					PurpleTxtCallback cb = query_data->cb.txt;
 					ssize_t red;
 					purple_debug_info("dnssrv","found %d TXT entries\n", size);
-					res = g_new0(PurpleTxtResponse, 1);
 					for (i = 0; i < size; i++) {
-						red = read(source, res, sizeof(PurpleTxtResponse));
-						if (red != sizeof(PurpleTxtResponse)) {
+						gsize len;
+
+						red = read(source, &len, sizeof(len));
+						if (red != sizeof(len)) {
 							purple_debug_error("dnssrv","unable to read txt "
-									"response: %s\n", g_strerror(errno));
+									"response length: %s\n", g_strerror(errno));
 							size = 0;
-							g_free(res);
 							g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
 							g_list_free(responses);
 							responses = NULL;
 							break;
 						}
+
+						res = g_new0(PurpleTxtResponse, 1);
+						res->content = g_new0(gchar, len);
+
+						red = read(source, res->content, len);
+						if (red != len) {
+							purple_debug_error("dnssrv","unable to read txt "
+									"response: %s\n", g_strerror(errno));
+							size = 0;
+							purple_txt_response_destroy(res);
+							g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
+							g_list_free(responses);
+							responses = NULL;
+							break;
+						}
+						responses = g_list_prepend(responses, res);
 					}
 
+					responses = g_list_reverse(responses);
 					cb(responses, query_data->extradata);
 				} else {
 					purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno);
@@ -788,6 +809,7 @@
 
 	internal_query.type = T_TXT;
 	strncpy(internal_query.query, query, 255);
+	internal_query.query[255] = '\0';
 
 	if (write(in[1], &internal_query, sizeof(internal_query)) < 0)
 		purple_debug_error("dnssrv", "Could not write to TXT resolver\n");
--- a/libpurple/protocols/jabber/buddy.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/jabber/buddy.c	Wed Oct 14 13:26:08 2009 +0900
@@ -38,6 +38,7 @@
 #include "xdata.h"
 #include "pep.h"
 #include "adhoccommands.h"
+#include "google.h"
 
 typedef struct {
 	long idle_seconds;
@@ -1842,6 +1843,13 @@
 		m = g_list_append(m, act);
 	}
 
+	if (js->googletalk) {
+		act = purple_menu_action_new(_("Initiate _Chat"),
+		                           PURPLE_CALLBACK(google_buddy_node_chat),
+		                           NULL, NULL);
+		m = g_list_append(m, act);
+	}
+
 	/*
 	 * This if-condition implements parts of XEP-0100: Gateway Interaction
 	 *
--- a/libpurple/protocols/jabber/chat.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/jabber/chat.c	Wed Oct 14 13:26:08 2009 +0900
@@ -209,19 +209,96 @@
 	g_hash_table_insert(hash_table, g_strdup(key), g_strdup(value));
 }
 
-void jabber_chat_join(PurpleConnection *gc, GHashTable *data)
+static JabberChat *jabber_chat_new(JabberStream *js, const char *room,
+                                   const char *server, const char *handle,
+                                   const char *password, GHashTable *data)
 {
 	JabberChat *chat;
-	char *room, *server, *handle, *passwd;
+	char *jid;
+
+	g_return_val_if_fail(jabber_chat_find(js, room, server) == NULL, NULL);
+
+	chat = g_new0(JabberChat, 1);
+	chat->js = js;
+
+	chat->room = g_strdup(room);
+	chat->server = g_strdup(server);
+	chat->handle = g_strdup(handle);
+
+	/* Copy the data hash table to chat->components */
+	chat->components = g_hash_table_new_full(g_str_hash, g_str_equal,
+			g_free, g_free);
+	if (data == NULL) {
+		g_hash_table_insert(chat->components, g_strdup("handle"), g_strdup(handle));
+		g_hash_table_insert(chat->components, g_strdup("room"), g_strdup(room));
+		g_hash_table_insert(chat->components, g_strdup("server"), g_strdup(server));
+		/* g_hash_table_insert(chat->components, g_strdup("password"), g_strdup(server)); */
+	} else {
+		g_hash_table_foreach(data, insert_in_hash_table, chat->components);
+	}
+
+	chat->members = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+			(GDestroyNotify)jabber_chat_member_free);
+
+	jid = g_strdup_printf("%s@%s", room, server);
+	g_hash_table_insert(js->chats, jid, chat);
+
+	return chat;
+}
+
+JabberChat *jabber_join_chat(JabberStream *js, const char *room,
+                             const char *server, const char *handle,
+                             const char *password, GHashTable *data)
+{
+	JabberChat *chat;
+
+	PurpleConnection *gc;
+	PurpleAccount *account;
+	PurpleStatus *status;
+
 	xmlnode *presence, *x;
-	char *tmp, *room_jid, *full_jid;
-	JabberStream *js = gc->proto_data;
-	PurplePresence *gpresence;
-	PurpleStatus *status;
 	JabberBuddyState state;
 	char *msg;
 	int priority;
 
+	char *jid;
+
+	chat = jabber_chat_new(js, room, server, handle, password, data);
+	g_return_val_if_fail(chat != NULL, NULL);
+
+	gc = js->gc;
+	account = purple_connection_get_account(gc);
+	status = purple_account_get_active_status(account);
+	purple_status_to_jabber(status, &state, &msg, &priority);
+
+	presence = jabber_presence_create_js(js, state, msg, priority);
+	g_free(msg);
+
+	jid = g_strdup_printf("%s@%s/%s", room, server, handle);
+	xmlnode_set_attrib(presence, "to", jid);
+	g_free(jid);
+
+	x = xmlnode_new_child(presence, "x");
+	xmlnode_set_namespace(x, "http://jabber.org/protocol/muc");
+
+	if (password && *password) {
+		xmlnode *p = xmlnode_new_child(x, "password");
+		xmlnode_insert_data(p, password, -1);
+	}
+
+	jabber_send(js, presence);
+	xmlnode_free(presence);
+
+	return chat;
+}
+
+void jabber_chat_join(PurpleConnection *gc, GHashTable *data)
+{
+	char *room, *server, *handle, *passwd;
+	JabberID *jid;
+	JabberStream *js = gc->proto_data;
+	char *tmp;
+
 	room = g_hash_table_lookup(data, "room");
 	server = g_hash_table_lookup(data, "server");
 	handle = g_hash_table_lookup(data, "handle");
@@ -256,51 +333,23 @@
 		return;
 	}
 
-	if(jabber_chat_find(js, room, server))
-		return;
-
+	/* Normalize the room and server parameters */
 	tmp = g_strdup_printf("%s@%s", room, server);
-	room_jid = g_strdup(jabber_normalize(NULL, tmp));
+	jid = jabber_id_new(tmp);
 	g_free(tmp);
 
-	chat = g_new0(JabberChat, 1);
-	chat->js = gc->proto_data;
-
-	chat->room = g_strdup(room);
-	chat->server = g_strdup(server);
-	chat->handle = g_strdup(handle);
-
-	/* Copy the data hash table to chat->components */
-	chat->components = g_hash_table_new_full(g_str_hash, g_str_equal,
-			g_free, g_free);
-	g_hash_table_foreach(data, insert_in_hash_table, chat->components);
-
-	chat->members = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
-			(GDestroyNotify)jabber_chat_member_free);
-
-	g_hash_table_insert(js->chats, room_jid, chat);
+	if (jid == NULL) {
+		/* TODO: Error message */
 
-	gpresence = purple_account_get_presence(gc->account);
-	status = purple_presence_get_active_status(gpresence);
-
-	purple_status_to_jabber(status, &state, &msg, &priority);
-
-	presence = jabber_presence_create_js(js, state, msg, priority);
-	full_jid = g_strdup_printf("%s/%s", room_jid, handle);
-	xmlnode_set_attrib(presence, "to", full_jid);
-	g_free(full_jid);
-	g_free(msg);
-
-	x = xmlnode_new_child(presence, "x");
-	xmlnode_set_namespace(x, "http://jabber.org/protocol/muc");
-
-	if(passwd && *passwd) {
-		xmlnode *password = xmlnode_new_child(x, "password");
-		xmlnode_insert_data(password, passwd, -1);
+		g_return_if_reached();
 	}
 
-	jabber_send(js, presence);
-	xmlnode_free(presence);
+	/*
+	 * Now that we've done all that nice core-interface stuff, let's join
+	 * this room!
+	 */
+	jabber_join_chat(js, jid->node, jid->domain, handle, passwd, data);
+	jabber_id_free(jid);
 }
 
 void jabber_chat_leave(PurpleConnection *gc, int id)
--- a/libpurple/protocols/jabber/chat.h	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/jabber/chat.h	Wed Oct 14 13:26:08 2009 +0900
@@ -57,6 +57,19 @@
 GList *jabber_chat_info(PurpleConnection *gc);
 GHashTable *jabber_chat_info_defaults(PurpleConnection *gc, const char *chat_name);
 char *jabber_get_chat_name(GHashTable *data);
+
+/**
+ * in-prpl function for joining a chat room. Doesn't require sticking goop
+ * into a hash table.
+ *
+ * @param password The password (if required) to join the room. May be NULL.
+ * @param data     The chat hash table.  May be NULL (it will be generated
+ *                 for current core<>prpl API interface.)
+ */
+JabberChat *jabber_join_chat(JabberStream *js, const char *room,
+                             const char *server, const char *handle,
+                             const char *password, GHashTable *data);
+
 void jabber_chat_join(PurpleConnection *gc, GHashTable *data);
 JabberChat *jabber_chat_find(JabberStream *js, const char *room,
 		const char *server);
--- a/libpurple/protocols/jabber/google.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/jabber/google.c	Wed Oct 14 13:26:08 2009 +0900
@@ -32,6 +32,7 @@
 #include "jabber.h"
 #include "presence.h"
 #include "iq.h"
+#include "chat.h"
 
 #include "jingle/jingle.h"
 
@@ -96,14 +97,14 @@
 		gchar *participant, GoogleSession *session)
 {
 	GList *candidates = purple_media_get_local_candidates(
-			session->media, session_id, session->remote_jid);
+			session->media, session_id, session->remote_jid), *iter;
 	PurpleMediaCandidate *transport;
 	gboolean video = FALSE;
 
 	if (!strcmp(session_id, "google-video"))
 		video = TRUE;
 
-	for (;candidates;candidates = candidates->next) {
+	for (iter = candidates; iter; iter = iter->next) {
 		JabberIq *iq;
 		gchar *ip, *port, *username, *password;
 		gchar pref[16];
@@ -111,7 +112,7 @@
 		xmlnode *sess;
 		xmlnode *candidate;
 		guint component_id;
-		transport = (PurpleMediaCandidate*)(candidates->data);
+		transport = PURPLE_MEDIA_CANDIDATE(iter->data);
 		component_id = purple_media_candidate_get_component_id(
 				transport);
 
@@ -169,6 +170,7 @@
 
 		jabber_iq_send(iq);
 	}
+	purple_media_candidate_list_free(candidates);
 }
 
 static void
@@ -1428,3 +1430,43 @@
 	purple_debug_info("jabber", "sending google:jingleinfo query\n");
 	jabber_iq_send(jingle_info);
 }
+
+void google_buddy_node_chat(PurpleBlistNode *node, gpointer data)
+{
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+	JabberStream *js;
+	JabberChat *chat;
+	gchar *room;
+	guint32 tmp, a, b;
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = PURPLE_BUDDY(node);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	g_return_if_fail(gc != NULL);
+	js = purple_connection_get_protocol_data(gc);
+
+	/* Generate a version 4 UUID */
+	tmp = g_random_int();
+	a = 0x4000 | (tmp & 0xFFF); /* 0x4000 to 0x4FFF */
+	tmp >>= 12;
+	b = ((1 << 3) << 12) | (tmp & 0x3FFF); /* 0x8000 to 0xBFFF */
+
+	tmp = g_random_int();
+	room = g_strdup_printf("private-chat-%08x-%04x-%04x-%04x-%04x%08x",
+			g_random_int(),
+			tmp & 0xFFFF,
+			a,
+			b,
+			(tmp >> 16) & 0xFFFF, g_random_int());
+
+	chat = jabber_join_chat(js, room, GOOGLE_GROUPCHAT_SERVER, js->user->node,
+	                        NULL, NULL);
+	if (chat) {
+		chat->muc = TRUE;
+		jabber_chat_invite(gc, chat->id, "", buddy->name);
+	}
+
+	g_free(room);
+}
--- a/libpurple/protocols/jabber/google.h	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/jabber/google.h	Wed Oct 14 13:26:08 2009 +0900
@@ -31,6 +31,8 @@
 #define GOOGLE_VIDEO_CAP "http://www.google.com/xmpp/protocol/video/v1"
 #define GOOGLE_JINGLE_INFO_NAMESPACE "google:jingleinfo"
 
+#define GOOGLE_GROUPCHAT_SERVER "groupchat.google.com"
+
 void jabber_gmail_init(JabberStream *js);
 void jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type,
                        const char *id, xmlnode *new_mail);
@@ -59,4 +61,6 @@
                                       xmlnode *child);
 void jabber_google_send_jingle_info(JabberStream *js);
 
+void google_buddy_node_chat(PurpleBlistNode *node, gpointer data);
+
 #endif   /* PURPLE_JABBER_GOOGLE_H_ */
--- a/libpurple/protocols/jabber/jabber.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/jabber/jabber.c	Wed Oct 14 13:26:08 2009 +0900
@@ -1081,39 +1081,19 @@
 					return;
 				}
 			} else {
+				const char *ids[] = {"username", "password", "name", "email", "nick", "first",
+					"last", "address", "city", "state", "zip", "phone", "url", "date",
+					NULL};
 				const char *value = purple_request_field_string_get_value(field);
-
-				if(!strcmp(id, "username")) {
-					y = xmlnode_new_child(query, "username");
-				} else if(!strcmp(id, "password")) {
-					y = xmlnode_new_child(query, "password");
-				} else if(!strcmp(id, "name")) {
-					y = xmlnode_new_child(query, "name");
-				} else if(!strcmp(id, "email")) {
-					y = xmlnode_new_child(query, "email");
-				} else if(!strcmp(id, "nick")) {
-					y = xmlnode_new_child(query, "nick");
-				} else if(!strcmp(id, "first")) {
-					y = xmlnode_new_child(query, "first");
-				} else if(!strcmp(id, "last")) {
-					y = xmlnode_new_child(query, "last");
-				} else if(!strcmp(id, "address")) {
-					y = xmlnode_new_child(query, "address");
-				} else if(!strcmp(id, "city")) {
-					y = xmlnode_new_child(query, "city");
-				} else if(!strcmp(id, "state")) {
-					y = xmlnode_new_child(query, "state");
-				} else if(!strcmp(id, "zip")) {
-					y = xmlnode_new_child(query, "zip");
-				} else if(!strcmp(id, "phone")) {
-					y = xmlnode_new_child(query, "phone");
-				} else if(!strcmp(id, "url")) {
-					y = xmlnode_new_child(query, "url");
-				} else if(!strcmp(id, "date")) {
-					y = xmlnode_new_child(query, "date");
-				} else {
+				int i;
+				for (i = 0; ids[i]; i++) {
+					if (!strcmp(id, ids[i]))
+						break;
+				}
+
+				if (!ids[i])
 					continue;
-				}
+				y = xmlnode_new_child(query, ids[i]);
 				xmlnode_insert_data(y, value, -1);
 				if(cbdata->js->registration && !strcmp(id, "username")) {
 					g_free(cbdata->js->user->node);
--- a/libpurple/protocols/msn/contact.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/msn/contact.c	Wed Oct 14 13:26:08 2009 +0900
@@ -356,9 +356,10 @@
 	char *type;
 	char *member_id;
 	MsnUser *user;
-	xmlnode *annotation;
+	xmlnode *annotation, *display;
 	guint nid = MSN_NETWORK_UNKNOWN;
 	char *invite = NULL;
+	char *display_text;
 
 	passport = xmlnode_get_data(xmlnode_get_child(member, node));
 	if (!purple_email_is_valid(passport)) {
@@ -368,7 +369,13 @@
 
 	type = xmlnode_get_data(xmlnode_get_child(member, "Type"));
 	member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId"));
-	user = msn_userlist_find_add_user(session->userlist, passport, NULL);
+	if ((display = xmlnode_get_child(member, "DisplayName"))) {
+		display_text = xmlnode_get_data(display);
+	} else {
+		display_text = NULL;
+	}
+
+	user = msn_userlist_find_add_user(session->userlist, passport, display_text);
 
 	for (annotation = xmlnode_get_child(member, "Annotations/Annotation");
 	     annotation;
@@ -409,6 +416,7 @@
 	g_free(type);
 	g_free(member_id);
 	g_free(invite);
+	g_free(display_text);
 }
 
 static void
--- a/libpurple/protocols/msn/nexus.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/msn/nexus.c	Wed Oct 14 13:26:08 2009 +0900
@@ -508,6 +508,7 @@
 	}
 
 	g_free(ud);
+	g_free(key);
 }
 
 void
--- a/libpurple/protocols/msn/notification.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/msn/notification.c	Wed Oct 14 13:26:08 2009 +0900
@@ -963,7 +963,7 @@
 			if (cmd->trans->data) {
 				MsnFqyCbData *fqy_data = cmd->trans->data;
 				fqy_data->cb(session, passport, network, fqy_data->data);
-				/* Don't free fqy_data yet since the server responds to FQY multipe times.
+				/* Don't free fqy_data yet since the server responds to FQY multiple times.
 				   It will be freed when cmd->trans is freed. */
 			}
 
@@ -1098,7 +1098,6 @@
 	}
 
 	if (msn_user_set_friendly_name(user, friendly)) {
-		serv_got_alias(gc, passport, friendly);
 		msn_update_contact(session, passport, MSN_UPDATE_DISPLAY, friendly);
 	}
 	g_free(friendly);
@@ -1263,7 +1262,6 @@
 
 	if (msn_user_set_friendly_name(user, friendly))
 	{
-		serv_got_alias(gc, passport, friendly);
 		msn_update_contact(session, passport, MSN_UPDATE_DISPLAY, friendly);
 	}
 
--- a/libpurple/protocols/msn/servconn.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/msn/servconn.c	Wed Oct 14 13:26:08 2009 +0900
@@ -299,8 +299,8 @@
 static gboolean
 servconn_idle_timeout_cb(MsnServConn *servconn)
 {
+	servconn->timeout_handle = 0;
 	msn_servconn_disconnect(servconn);
-	servconn->timeout_handle = 0;	/* XXX: servconn may not be valid anymore */
 	return FALSE;
 }
 
--- a/libpurple/protocols/msn/user.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/msn/user.c	Wed Oct 14 13:26:08 2009 +0900
@@ -183,12 +183,15 @@
 {
 	g_return_val_if_fail(user != NULL, FALSE);
 
-	if (user->friendly_name && name && !strcmp(user->friendly_name, name))
+	if (user->friendly_name && name && (!strcmp(user->friendly_name, name) ||
+				!strcmp(user->passport, name)))
 		return FALSE;
 
 	g_free(user->friendly_name);
 	user->friendly_name = g_strdup(name);
 
+	serv_got_alias(purple_account_get_connection(user->userlist->session->account),
+			user->passport, name);
 	return TRUE;
 }
 
--- a/libpurple/protocols/yahoo/libymsg.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/yahoo/libymsg.c	Wed Oct 14 13:26:08 2009 +0900
@@ -153,7 +153,7 @@
 	char *name = NULL;
 	gboolean unicode = FALSE;
 	char *message = NULL;
-	char *msn_name = NULL;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
 		if (!purple_account_get_remember_password(account))
@@ -184,27 +184,35 @@
 			f = NULL;
 			if (pair->value && g_utf8_validate(pair->value, -1, NULL)) {
 				GSList *tmplist;
-				int protocol = 0;
 
 				name = pair->value;
 
-				/* Look ahead to see if we have the protocol info about the buddy */
+				/* Look ahead to see if we have the federation info about the buddy */
 				for (tmplist = l->next; tmplist; tmplist = tmplist->next) {
 					struct yahoo_pair *p = tmplist->data;
 					if (p->key == 7)
 						break;
 					if (p->key == 241) {
-						if(strtol(p->value, NULL, 10) == 2) {
-							g_free(msn_name);
-							msn_name = g_strconcat("msn/", name, NULL);
-							name = msn_name;
-							protocol = 2;
+						fed = strtol(p->value, NULL, 10);
+						switch (fed) {
+							case YAHOO_FEDERATION_MSN:
+								name = g_strconcat("msn/", name, NULL);
+								break;
+							case YAHOO_FEDERATION_OCS:
+								name = g_strconcat("ocs/", name, NULL);
+								break;
+							case YAHOO_FEDERATION_IBM:
+								name = g_strconcat("ibm/", name, NULL);
+								break;
+							case YAHOO_FEDERATION_NONE:
+							default:
+								break;
 						}
 						break;
 					}
 				}
 				f = yahoo_friend_find_or_new(gc, name);
-				f->protocol = protocol;
+				f->fed = fed;
 			}
 			break;
 		case 10: /* state */
@@ -361,7 +369,7 @@
 			if(f && strtol(pair->value, NULL, 10))
 				f->version_id = strtol(pair->value, NULL, 10);
 			break;
-		case 241: /* protocol buddy belongs to */
+		case 241: /* Federated network buddy belongs to */
 			break;  /* We process this when get '7' */
 		default:
 			purple_debug_warning("yahoo",
@@ -381,7 +389,7 @@
 		if (name) /* update the last buddy */
 			yahoo_update_status(gc, name, f);
 	}
-	g_free(msn_name);
+
 }
 
 static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group)
@@ -488,13 +496,13 @@
 	PurpleAccount *account = purple_connection_get_account(gc);
 	YahooData *yd = gc->proto_data;
 	GHashTable *ht;
-	char *norm_bud;
+	char *norm_bud = NULL;
 	char *temp = NULL;
 	YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */
 	                       /* But what if you had no friends? */
 	PurpleBuddy *b;
 	PurpleGroup *g;
-	int protocol = 0;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 	int stealth = 0;
 
 	ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
@@ -519,11 +527,20 @@
 			break;
 		case 301: /* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */
 			if(temp != NULL) {
-				if(protocol == 2)
-					norm_bud = g_strconcat("msn/", temp, NULL);
-				else
-					norm_bud = g_strdup(temp);
-
+				switch (fed) {
+					case YAHOO_FEDERATION_MSN:
+						norm_bud = g_strconcat("msn/", temp, NULL);
+						break;
+					case YAHOO_FEDERATION_OCS:
+						norm_bud = g_strconcat("ocs/", temp, NULL);
+						break;
+					case YAHOO_FEDERATION_IBM:
+						norm_bud = g_strconcat("ibm/", temp, NULL);
+						break;
+					case YAHOO_FEDERATION_NONE:
+						norm_bud = g_strdup(temp);
+						break;
+				}
 				if (yd->current_list15_grp) {
 					/* This buddy is in a group */
 					f = yahoo_friend_find_or_new(gc, norm_bud);
@@ -536,15 +553,15 @@
 						purple_blist_add_buddy(b, NULL, g, NULL);
 					}
 					yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp);
-					if(protocol != 0) {
-						f->protocol = protocol;
-						purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol);
+					if(fed) {
+						f->fed = fed;
+						purple_debug_info("yahoo", "Setting federation to %d\n", f->fed);
 					}
 					if(stealth == 2)
 						f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
 
 					/* set p2p status not connected and no p2p packet sent */
-					if(protocol == 0) {
+					if(fed == YAHOO_FEDERATION_NONE) {
 						yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
 						f->p2p_packet_sent = 0;
 					} else
@@ -556,8 +573,8 @@
 				}
 
 				g_free(norm_bud);
-
-				protocol = 0;
+				norm_bud=NULL;
+				fed = YAHOO_FEDERATION_NONE;
 				stealth = 0;
 				g_free(temp);
 				temp = NULL;
@@ -573,8 +590,8 @@
 			g_free(temp);
 			temp = g_strdup(purple_normalize(account, pair->value));
 			break;
-		case 241: /* another protocol user */
-			protocol = strtol(pair->value, NULL, 10);
+		case 241: /* user on federated network */
+			fed = strtol(pair->value, NULL, 10);
 			break;
 		case 59: /* somebody told cookies come here too, but im not sure */
 			yahoo_process_cookie(yd, pair->value);
@@ -766,7 +783,7 @@
 	GSList *l = pkt->hash;
 	gint val_11 = 0;
 	YahooData *yd = gc->proto_data;
-	gboolean msn = FALSE;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	account = purple_connection_get_account(gc);
 
@@ -783,8 +800,7 @@
 		if (pair->key == 11)
 			val_11 = strtol(pair->value, NULL, 10);
 		if (pair->key == 241)
-			if(strtol(pair->value, NULL, 10) == 2)
-				msn = TRUE;
+			fed = strtol(pair->value, NULL, 10);
 		l = l->next;
 	}
 
@@ -802,20 +818,30 @@
 	if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
 		&& (purple_privacy_check(account, from)))
 	{
-		if(msn) {
-			char *msn_from = g_strconcat("msn/", from, NULL);
-			if (*stat == '1')
-				serv_got_typing(gc, msn_from, 0, PURPLE_TYPING);
-			else
-				serv_got_typing_stopped(gc, msn_from);
-			g_free(msn_from);
+		char *fed_from = from;
+		switch (fed) {
+			case YAHOO_FEDERATION_MSN:
+				fed_from = g_strconcat("msn/", from, NULL);
+				break;
+			case YAHOO_FEDERATION_OCS:
+				fed_from = g_strconcat("ocs/", from, NULL);
+				break;
+			case YAHOO_FEDERATION_IBM:
+				fed_from = g_strconcat("ibm/", from, NULL);
+				break;
+			case YAHOO_FEDERATION_NONE:
+			default:
+				break;
 		}
-		else	{
-			if (*stat == '1')
-				serv_got_typing(gc, from, 0, PURPLE_TYPING);
-			else
-				serv_got_typing_stopped(gc, from);
-		}
+	
+		if (*stat == '1')
+			serv_got_typing(gc, fed_from, 0, PURPLE_TYPING);
+		else
+			serv_got_typing_stopped(gc, fed_from);
+		
+		if (fed_from != from)
+			g_free(fed_from);			
+	
 	} else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) {
 		PurpleBuddy *bud = purple_find_buddy(account, from);
 
@@ -852,7 +878,7 @@
 	int buddy_icon;
 	char *id;
 	char *msg;
-	gboolean msn;
+	YahooFederation fed;
 };
 
 static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -926,6 +952,7 @@
 	struct _yahoo_im *im = NULL;
 	const char *imv = NULL;
 	gint val_11 = 0;
+	char *fed_from = NULL;
 
 	account = purple_connection_get_account(gc);
 
@@ -936,9 +963,10 @@
 			if (pair->key == 4 || pair->key == 1) {
 				im = g_new0(struct _yahoo_im, 1);
 				list = g_slist_append(list, im);
-				im->from = pair->value;
+				im->from = fed_from = pair->value;
 				im->time = time(NULL);
 				im->utf8 = TRUE;
+				im->fed = YAHOO_FEDERATION_NONE;
 			}
 			if (im && pair->key == 5)
 				im->active_id = pair->value;
@@ -956,8 +984,23 @@
 					im->msg = pair->value;
 			}
 			if (im && pair->key == 241) {
-				if(strtol(pair->value, NULL, 10) == 2)
-					im->msn = TRUE;
+				im->fed = strtol(pair->value, NULL, 10);
+				switch (im->fed) {
+					case YAHOO_FEDERATION_MSN:
+						fed_from = g_strconcat("msn/",im->from, NULL);
+						break;
+					case YAHOO_FEDERATION_OCS:
+						fed_from = g_strconcat("ocs/",im->from, NULL);
+						break;
+					case YAHOO_FEDERATION_IBM:
+						fed_from = g_strconcat("ibm/",im->from, NULL);
+						break;
+					case YAHOO_FEDERATION_NONE:
+					default:
+						break;
+				}
+				purple_debug_info("yahoo", "Message from federated (%d) buddy %s.\n", im->fed, fed_from);
+					
 			}
 			/* peer session id */
 			if (pair->key == 11) {
@@ -981,10 +1024,10 @@
 
 	/* disconnect the peer if connected through p2p and sends wrong value for session id */
 	if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) {
-		purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im ? im->from : "(im was null)");
+		purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im ? fed_from : "(im was null)");
 		/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
 		if (im) {
-			g_hash_table_remove(yd->peers, im->from);
+			g_hash_table_remove(yd->peers, fed_from);
 			g_free(im);
 		}
 		return;
@@ -992,6 +1035,7 @@
 
 	/* TODO: It seems that this check should be per IM, not global */
 	/* Check for the Doodle IMV */
+	/* no doodle with federated buddies -- assumption???  */
 	if (im != NULL && imv!= NULL && im->from != NULL)
 	{
 		g_hash_table_replace(yd->imvironments, g_strdup(im->from), g_strdup(imv));
@@ -1026,17 +1070,15 @@
 	for (l = list; l; l = l->next) {
 		YahooFriend *f;
 		char *m, *m2;
-		char *msn_from = NULL;
-		const char *from;
 		im = l->data;
 
-		if (!im->from || !im->msg) {
+		if (!fed_from || !im->msg) {
 			g_free(im);
 			continue;
 		}
 
-		if (!purple_privacy_check(account, im->from)) {
-			purple_debug_info("yahoo", "Message from %s dropped.\n", im->from);
+		if (!purple_privacy_check(account, fed_from)) {
+			purple_debug_info("yahoo", "Message from %s dropped.\n", fed_from);
 			return;
 		}
 
@@ -1071,34 +1113,25 @@
 		g_free(m);
 		m = m2;
 		purple_util_chrreplace(m, '\r', '\n');
-
-		if (im->msn) {
-			msn_from = g_strconcat("msn/", im->from, NULL);
-			from = msn_from;
-		} else {
-			from = im->from;
-		}
-
 		if (!strcmp(m, "<ding>")) {
 			char *username;
 
-			username = g_markup_escape_text(from, -1);
+			username = g_markup_escape_text(fed_from, -1);
 			purple_prpl_got_attention(gc, username, YAHOO_BUZZ);
 			g_free(username);
 			g_free(m);
 			g_free(im);
-			g_free(msn_from);
 			continue;
 		}
 
 		m2 = yahoo_codes_to_html(m);
 		g_free(m);
 
-		serv_got_im(gc, from, m2, 0, im->time);
+		serv_got_im(gc, fed_from, m2, 0, im->time);
 		g_free(m2);
 
-		/* laters : implement buddy icon for msn friends */
-		if (!im->msn) {
+		/* Official clients don't share buddy images with federated buddies */
+		if (im->fed == YAHOO_FEDERATION_NONE) {
 			if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) {
 				if (yahoo_friend_get_buddy_icon_need_request(f)) {
 					yahoo_send_picture_request(gc, im->from);
@@ -1108,8 +1141,9 @@
 		}
 
 		g_free(im);
-		g_free(msn_from);
 	}
+	if (fed_from != im->from)
+		g_free(fed_from);
 	g_slist_free(list);
 }
 
@@ -1142,7 +1176,7 @@
 	PurpleConnection *gc;
 	char *id;
 	char *who;
-	int protocol;
+	YahooFederation fed;
 };
 
 static void
@@ -1153,16 +1187,24 @@
 	YahooData *yd = add_req->gc->proto_data;
 	const char *who = add_req->who;
 
-	if (add_req->protocol == 2)
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	if (add_req->fed) {
 		who += 4;
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
-	yahoo_packet_hash(pkt, "ssiii",
-					  1, add_req->id,
-					  5, who,
-					  241, add_req->protocol,
-					  13, 1,
-					  334, 0);
+		yahoo_packet_hash(pkt, "ssiii",
+						  1, add_req->id,
+						  5, who,
+						  241, add_req->fed,
+						  13, 1,
+						  334, 0);
+	}
+	else {
+		yahoo_packet_hash(pkt, "ssii",
+						  1, add_req->id,
+						  5, who,
+						  13, 1,
+						  334, 0);
+	}
+		
 	yahoo_packet_send_and_free(pkt, yd);
 
 	g_free(add_req->id);
@@ -1178,23 +1220,33 @@
 	char *encoded_msg = NULL;
 	const char *who = add_req->who;
 
-	if (add_req->protocol == 2)
-		who += 4; /* Skip 'msn/' */
-
 	if (msg && *msg)
 		encoded_msg = yahoo_string_encode(add_req->gc, msg, NULL);
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
 			YAHOO_STATUS_AVAILABLE, yd->session_id);
 
-	yahoo_packet_hash(pkt, "ssiiiis",
-			1, add_req->id,
-			5, who,
-			241, add_req->protocol,
-			13, 2,
-			334, 0,
-			97, 1,
-			14, encoded_msg ? encoded_msg : "");
+	if (add_req->fed) {
+		who += 4; /* Skip fed identifier (msn|ocs|ibm)/' */
+		yahoo_packet_hash(pkt, "ssiiiis",
+						  1, add_req->id,
+						  5, who,
+						  241, add_req->fed,
+						  13, 2,
+						  334, 0,
+						  97, 1,
+						  14, encoded_msg ? encoded_msg : "");
+	}
+	else {
+		yahoo_packet_hash(pkt, "ssiiis",
+						  1, add_req->id,
+						  5, who,
+						  13, 2,
+						  334, 0,
+						  97, 1,
+						  14, encoded_msg ? encoded_msg : "");
+	}
+
 
 	yahoo_packet_send_and_free(pkt, yd);
 
@@ -1249,8 +1301,7 @@
 	PurpleAccount *account;
 	GSList *l = pkt->hash;
 	const char *msg = NULL;
-	int protocol = 0;
-
+	
 	account = purple_connection_get_account(gc);
 
 	/* Buddy authorized/declined our addition */
@@ -1258,6 +1309,7 @@
 		char *temp = NULL;
 		char *who = NULL;
 		int response = 0;
+		YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 		while (l) {
 			struct yahoo_pair *pair = l->data;
@@ -1273,16 +1325,27 @@
 				msg = pair->value;
 				break;
 			case 241:
-				protocol = strtol(pair->value, NULL, 10);
+				fed = strtol(pair->value, NULL, 10);
 				break;
 			}
 			l = l->next;
 		}
 
-		if(protocol == 0)
-			who = g_strdup(temp);
-		else if(protocol == 2)
-			who = g_strconcat("msn/", temp, NULL);
+		switch (fed) {
+			case YAHOO_FEDERATION_MSN:
+				who = g_strconcat("msn/", temp, NULL);
+				break;
+			case YAHOO_FEDERATION_OCS:
+				who = g_strconcat("ocs/", temp, NULL);
+				break;
+			case YAHOO_FEDERATION_IBM:
+				who = g_strconcat("ibm/", temp, NULL);
+				break;
+			case YAHOO_FEDERATION_NONE:
+			default:
+				who = g_strdup(temp);
+				break;
+		}
 
 		if (response == 1) /* Authorized */
 			purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
@@ -1301,6 +1364,7 @@
 
 		add_req = g_new0(struct yahoo_add_request, 1);
 		add_req->gc = gc;
+		add_req->fed = YAHOO_FEDERATION_NONE;
 
 		while (l) {
 			struct yahoo_pair *pair = l->data;
@@ -1319,7 +1383,7 @@
 				firstname = pair->value;
 				break;
 			case 241:
-				add_req->protocol = strtol(pair->value, NULL, 10);
+				add_req->fed = strtol(pair->value, NULL, 10);
 				break;
 			case 254:
 				lastname = pair->value;
@@ -1328,10 +1392,21 @@
 			}
 			l = l->next;
 		}
-		if(add_req->protocol == 2)
-			add_req->who = g_strconcat("msn/", temp, NULL);
-		else
-			add_req->who = g_strdup(temp);
+		switch (add_req->fed) {
+			case YAHOO_FEDERATION_MSN:
+				add_req->who = g_strconcat("msn/", temp, NULL);
+				break;
+			case YAHOO_FEDERATION_OCS:
+				add_req->who = g_strconcat("ocs/", temp, NULL);
+				break;
+			case YAHOO_FEDERATION_IBM:
+				add_req->who = g_strconcat("ibm/", temp, NULL);
+				break;
+			case YAHOO_FEDERATION_NONE:
+			default:
+				add_req->who = g_strdup(temp);
+				break;
+		}
 
 		if (add_req->id && add_req->who) {
 			char *alias = NULL, *dec_msg = NULL;
@@ -2137,8 +2212,7 @@
 	YahooFriend *f;
 	GSList *l = pkt->hash;
 	YahooData *yd = gc->proto_data;
-	int protocol = 0;
-	gboolean msn = FALSE;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
@@ -2154,9 +2228,7 @@
 			group = pair->value;
 			break;
 		case 241:
-			protocol = strtol(pair->value, NULL, 10);
-			if(protocol == 2)
-				msn = TRUE;
+			fed = strtol(pair->value, NULL, 10);
 			break;
 		}
 
@@ -2168,20 +2240,30 @@
 	if (!group)
 		group = "";
 
-	if(msn)
-		who = g_strconcat("msn/", temp, NULL);
-	else
-		who = g_strdup(temp);
+	switch (fed) {
+		case YAHOO_FEDERATION_MSN:
+			who = g_strconcat("msn/", temp, NULL);
+			break;
+		case YAHOO_FEDERATION_OCS:
+			who = g_strconcat("ocs/", temp, NULL);
+			break;
+		case YAHOO_FEDERATION_IBM:
+			who = g_strconcat("ibm/", temp, NULL);
+			break;
+		case YAHOO_FEDERATION_NONE:
+		default:
+			who = g_strdup(temp);
+			break;
+	}
 
 	if (!err || (err == 2)) { /* 0 = ok, 2 = already on serv list */
 		f = yahoo_friend_find_or_new(gc, who);
 		yahoo_update_status(gc, who, f);
-		if(protocol)
-			f->protocol = protocol;
+		f->fed = fed;
 
 		if( !g_hash_table_lookup(yd->peers, who) ) {
 			/* we are not connected as client, so set friend to not connected */
-			if(msn)
+			if(fed)
 				yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT);
 			else	{
 				yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
@@ -2553,7 +2635,7 @@
 		return;
 
 	/* Dont send p2p packet to buddies of other protocols */
-	if(f->protocol)
+	if(f->fed)
 		return;
 
 	/* Finally, don't try to connect to buddies not online or on sms */
@@ -3594,8 +3676,9 @@
 	if (purple_presence_is_online(presence)) {
 		if (yahoo_friend_get_game(f))
 			return "game";
-		if (f->protocol == 2)
-			return "msn";
+
+		if (f->fed)
+			return "external";
 	}
 	return NULL;
 }
@@ -4265,11 +4348,11 @@
 	gboolean utf8 = TRUE;
 	PurpleWhiteboard *wb;
 	int ret = 1;
-	YahooFriend *f = NULL;
+	const char *fed_who;
 	gsize lenb = 0;
 	glong lenc = 0;
 	struct yahoo_p2p_data *p2p_data;
-	gboolean msn = FALSE;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 	msg2 = yahoo_string_encode(gc, msg, &utf8);
 
 	if(msg2) {
@@ -4287,7 +4370,17 @@
 		}
 	}
 
-	msn = !g_ascii_strncasecmp(who, "msn/", 4);
+	if (who[3] == '/') {
+		if (!g_ascii_strncasecmp(who, "msn/", 4)) {
+			fed = YAHOO_FEDERATION_MSN;
+		}
+		else if (!g_ascii_strncasecmp(who, "ocs/", 4)) {
+			fed = YAHOO_FEDERATION_OCS;
+		}
+		else if (!g_ascii_strncasecmp(who, "ibm/", 4)) {
+			fed = YAHOO_FEDERATION_IBM;
+		}
+	}
 
 	if (who[0] == '+') {
 		/* we have an sms to be sent */
@@ -4337,15 +4430,20 @@
 	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id);
-	if(msn) {
-		yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who+4);
-		yahoo_packet_hash_int(pkt, 241, 2);
+	fed_who = who;
+	switch (fed) {
+		case YAHOO_FEDERATION_MSN:
+		case YAHOO_FEDERATION_OCS:
+		case YAHOO_FEDERATION_IBM:
+			fed_who += 4;
+			break;
+		case YAHOO_FEDERATION_NONE:
+		default:
+			break;
 	}
-	else	{
-		yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who);
-		if ((f = yahoo_friend_find(gc, who)) && f->protocol)
-			yahoo_packet_hash_int(pkt, 241, f->protocol);
-	}
+	yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, fed_who);
+	if (fed)
+		yahoo_packet_hash_int(pkt, 241, fed);
 
 	if (utf8)
 		yahoo_packet_hash_str(pkt, 97, "1");
@@ -4361,7 +4459,7 @@
 	 * just so that we don't inadvertantly reset their IMVironment back
 	 * to nothing.
 	 *
-	 * If they have no set an IMVironment, then use the default.
+	 * If they have not set an IMVironment, then use the default.
 	 */
 	wb = purple_whiteboard_get_session(gc->account, who);
 	if (wb)
@@ -4386,13 +4484,13 @@
 	/* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */
 	if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) {
 		/* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */
-		if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !msn ) {
+		if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !fed) {
 			yahoo_packet_hash_int(pkt, 11, p2p_data->session_id);
 			yahoo_p2p_write_pkt(p2p_data->source, pkt);
 		}
 		else	{
 			yahoo_packet_send(pkt, yd);
-			if(!msn)
+			if(!fed)
 				yahoo_send_p2p_pkt(gc, who, 0);		/* send p2p packet, with val_13=0 */
 		}
 	}
@@ -4411,9 +4509,21 @@
 {
 	YahooData *yd = gc->proto_data;
 	struct yahoo_p2p_data *p2p_data;
-	gboolean msn = !g_ascii_strncasecmp(who, "msn/", 4);
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 	struct yahoo_packet *pkt = NULL;
 
+	if (who[3] == '/') {
+		if (!g_ascii_strncasecmp(who, "msn/", 4)) {
+			fed = YAHOO_FEDERATION_MSN;
+		}
+		else if (!g_ascii_strncasecmp(who, "ocs/", 4)) {
+			fed = YAHOO_FEDERATION_OCS;
+		}
+		else if (!g_ascii_strncasecmp(who, "ibm/", 4)) {
+			fed = YAHOO_FEDERATION_IBM;
+		}
+	}
+
 	/* Don't do anything if sms is being typed */
 	if( strncmp(who, "+", 1) == 0 )
 		return 0;
@@ -4421,7 +4531,7 @@
 	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, yd->session_id);
 
 	/* check to see if p2p link exists, send through it */
-	if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !msn ) {
+	if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !fed) {
 		yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc),
 	                  14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
 	                  5, who, 11, p2p_data->session_id, 1002, "1");	/* To-do: key 15 to be sent in case of p2p */
@@ -4429,14 +4539,24 @@
 		yahoo_packet_free(pkt);
 	}
 	else	{	/* send through yahoo server */
-		if(msn)
-			yahoo_packet_hash(pkt, "sssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
-	                  14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
-	                  5, who+4, 1002, "1", 241, "2");
-		else
-			yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
-	                  14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
-	                  5, who, 1002, "1");
+
+		const char *fed_who = who;
+		switch (fed) {
+			case YAHOO_FEDERATION_MSN:
+			case YAHOO_FEDERATION_OCS:
+			case YAHOO_FEDERATION_IBM:
+				fed_who += 4;
+				break;
+			case YAHOO_FEDERATION_NONE:
+			default:
+				break;
+		}
+		
+		yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
+                  14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
+                  5, fed_who, 1002, "1");
+        if (fed)
+        	yahoo_packet_hash_int(pkt, 241, fed);
 		yahoo_packet_send_and_free(pkt, yd);
 	}
 
@@ -4684,17 +4804,29 @@
 	char *group2;
 	YahooFriend *f;
 	const char *bname;
-	gboolean msn = FALSE;
+	const char *fed_bname;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	if (!yd->logged_in)
 		return;
 
-	bname = purple_buddy_get_name(buddy);
+	fed_bname = bname = purple_buddy_get_name(buddy);
 	if (!purple_privacy_check(purple_connection_get_account(gc), bname))
 		return;
 
 	f = yahoo_friend_find(gc, bname);
-	msn = !g_ascii_strncasecmp(bname, "msn/", 4);
+	if (bname[3] == '/') {
+		fed_bname += 4;
+		if (!g_ascii_strncasecmp(bname, "msn/", 4)) {
+			fed = YAHOO_FEDERATION_MSN;
+		}
+		else if (!g_ascii_strncasecmp(bname, "ocs/", 4)) {
+			fed = YAHOO_FEDERATION_OCS;
+		}
+		else if (!g_ascii_strncasecmp(bname, "ibm/", 4)) {
+			fed = YAHOO_FEDERATION_IBM;
+		}
+	}
 
 	g = purple_buddy_get_group(buddy);
 	if (g)
@@ -4704,37 +4836,35 @@
 
 	group2 = yahoo_string_encode(gc, group, NULL);
 	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
-	if(msn) {
-		yahoo_packet_hash(pkt, "sssssssssss",
-			14, "",
-			65, group2,
-			97, "1",
-			1, purple_connection_get_display_name(gc),
-			302, "319",
-			300, "319",
-			7, bname + 4,
-			241, "2",
-			334, "0",
-			301, "319",
-			303, "319"
+	if (fed) {
+		yahoo_packet_hash(pkt, "sssssssisss",
+						  14, "",
+						  65, group2,
+						  97, "1",
+						  1, purple_connection_get_display_name(gc),
+						  302, "319",
+						  300, "319",
+						  7, fed_bname,
+						  241, fed,
+						  334, "0",
+						  301, "319",
+						  303, "319"
 		);
 	}
-	else	{
+	else {
 		yahoo_packet_hash(pkt, "ssssssssss",
-			14, "",
-			65, group2,
-			97, "1",
-			1, purple_connection_get_display_name(gc),
-			302, "319",
-			300, "319",
-	                7, bname,
-			334, "0",
-			301, "319",
-			303, "319"
+						  14, "",
+						  65, group2,
+						  97, "1",
+						  1, purple_connection_get_display_name(gc),
+						  302, "319",
+						  300, "319",
+						  7, fed_bname,
+						  334, "0",
+						  301, "319",
+						  303, "319"
 		);
 	}
-	if (f && f->protocol && !msn)
-		yahoo_packet_hash_int(pkt, 241, f->protocol);
 
 	yahoo_packet_send_and_free(pkt, yd);
 	g_free(group2);
@@ -4750,17 +4880,16 @@
 	char *cg;
 	const char *bname, *gname;
 	YahooFriend *f = NULL;
-	gboolean msn = FALSE;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	bname = purple_buddy_get_name(buddy);
 	f = yahoo_friend_find(gc, bname);
 	if (!f)
 		return;
+	fed = f->fed;
 
 	gname = purple_group_get_name(group);
 	buddies = purple_find_buddies(purple_connection_get_account(gc), bname);
-	if(f->protocol == 2)
-		msn = TRUE;
 	for (l = buddies; l; l = l->next) {
 		g = purple_buddy_get_group(l->data);
 		if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) {
@@ -4771,20 +4900,29 @@
 
 	g_slist_free(buddies);
 
-	if (remove)
+	if (remove) {
 		g_hash_table_remove(yd->friends, bname);
+		f = NULL; /* f no longer valid - Just making it clear */
+	}
 
 	cg = yahoo_string_encode(gc, gname, NULL);
 	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
 
-	if(msn)
-		yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
-	                  7, bname+4, 65, cg);
-	else
-		yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
+	switch (fed) {
+		case YAHOO_FEDERATION_MSN:
+		case YAHOO_FEDERATION_OCS:
+		case YAHOO_FEDERATION_IBM:
+			bname += 4;
+			break;
+		case YAHOO_FEDERATION_NONE:
+		default:
+			break;
+	}
+
+	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
 	                  7, bname, 65, cg);
-	if(f->protocol)
-		yahoo_packet_hash_int(pkt, 241, f->protocol);
+	if (fed)
+		yahoo_packet_hash_int(pkt, 241, fed);
 	yahoo_packet_send_and_free(pkt, yd);
 	g_free(cg);
 }
@@ -4792,7 +4930,7 @@
 void yahoo_add_deny(PurpleConnection *gc, const char *who) {
 	YahooData *yd = (YahooData *)gc->proto_data;
 	struct yahoo_packet *pkt;
-	gboolean msn = FALSE;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	if (!yd->logged_in)
 		return;
@@ -4800,11 +4938,22 @@
 	if (!who || who[0] == '\0')
 		return;
 
-	msn = !g_ascii_strncasecmp(who, "msn/", 4);
+	if (who[3] == '/') {
+		if (!g_ascii_strncasecmp(who, "msn/", 4)) {
+			fed = YAHOO_FEDERATION_MSN;
+		}
+		else if (!g_ascii_strncasecmp(who, "ocs/", 4)) {
+			fed = YAHOO_FEDERATION_OCS;
+		}
+		else if (!g_ascii_strncasecmp(who, "ibm/", 4)) {
+			fed = YAHOO_FEDERATION_IBM;
+		}
+	}
+	
 	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
 
-	if(msn)
-		yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc), 7, who+4, 241, "2", 13, "1");
+	if(fed)
+		yahoo_packet_hash(pkt, "ssis", 1, purple_connection_get_display_name(gc), 7, who+4, 241, fed, 13, "1");
 	else
 		yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "1");
 
@@ -4814,19 +4963,28 @@
 void yahoo_rem_deny(PurpleConnection *gc, const char *who) {
 	YahooData *yd = (YahooData *)gc->proto_data;
 	struct yahoo_packet *pkt;
-	gboolean msn = FALSE;
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	if (!yd->logged_in)
 		return;
 
 	if (!who || who[0] == '\0')
 		return;
-
-	msn = !g_ascii_strncasecmp(who, "msn/", 4);
+	if (who[3] == '/') {
+		if (!g_ascii_strncasecmp(who, "msn/", 4)) {
+			fed = YAHOO_FEDERATION_MSN;
+		}
+		else if (!g_ascii_strncasecmp(who, "ocs/", 4)) {
+			fed = YAHOO_FEDERATION_OCS;
+		}
+		else if (!g_ascii_strncasecmp(who, "ibm/", 4)) {
+			fed = YAHOO_FEDERATION_IBM;
+		}
+	}
 	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
 
-	if(msn)
-		yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc), 7, who+4, 241, "2", 13, "2");
+	if(fed)
+		yahoo_packet_hash(pkt, "ssis", 1, purple_connection_get_display_name(gc), 7, who+4, 241, fed, 13, "2");
 	else
 		yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "2");
 	
@@ -4864,7 +5022,6 @@
 	struct yahoo_packet *pkt;
 	char *gpn, *gpo;
 	YahooFriend *f = yahoo_friend_find(gc, who);
-	gboolean msn = FALSE;
 	const char *temp = NULL;
 
 	/* Step 0:  If they aren't on the server list anyway,
@@ -4873,8 +5030,7 @@
 	if (!f)
 		return;
 
-	if(f->protocol == 2) {
-		msn = TRUE;
+	if(f->fed) {
 		temp = who+4;
 	} else
 		temp = who;
@@ -4892,9 +5048,9 @@
 	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
-	if(f->protocol)
+	if(f->fed)
 		yahoo_packet_hash(pkt, "ssssissss", 1, purple_connection_get_display_name(gc),
-	                  302, "240", 300, "240", 7, temp, 241, f->protocol, 224, gpo, 264, gpn, 301,
+	                  302, "240", 300, "240", 7, temp, 241, f->fed, 224, gpo, 264, gpn, 301,
 	                  "240", 303, "240");
 	else
 		yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc),
--- a/libpurple/protocols/yahoo/libymsg.h	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/yahoo/libymsg.h	Wed Oct 14 13:26:08 2009 +0900
@@ -128,6 +128,20 @@
 	YAHOO_STATUS_DISCONNECTED = 0xffffffff /* in ymsg 15. doesnt mean the normal sense of 'disconnected' */
 };
 
+/*
+ * Yahoo federated networks.  Key 241 in ymsg.
+ * If it doesn't exist, it is on Yahoo's netowrk.
+ * It if does exist, send to another IM network.
+ */
+
+typedef enum {
+	YAHOO_FEDERATION_NONE = 0, /* No federation - Yahoo! network */
+	YAHOO_FEDERATION_OCS = 1,  /* LCS or OCS private networks */
+	YAHOO_FEDERATION_MSN = 2,  /* MSN or Windows Live network */
+	YAHOO_FEDERATION_IBM = 9   /* IBM/Sametime network */
+} YahooFederation;
+
+
 struct yahoo_buddy_icon_upload_data {
 	PurpleConnection *gc;
 	GString *str;
--- a/libpurple/protocols/yahoo/yahoo_friend.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/yahoo/yahoo_friend.c	Wed Oct 14 13:26:08 2009 +0900
@@ -151,9 +151,8 @@
 	char *temp = NULL;
 	char *who = NULL;
 	int value = 0;
-	int protocol = 0;
-	gboolean msn = FALSE;
-
+	YahooFederation fed = YAHOO_FEDERATION_NONE;
+	
 	while (l) {
 		struct yahoo_pair *pair = l->data;
 
@@ -165,8 +164,7 @@
 				value = strtol(pair->value, NULL, 10);
 				break;
 			case 241:
-				protocol = strtol(pair->value, NULL, 10);
-				msn = TRUE;
+				fed = strtol(pair->value, NULL, 10);
 				break;
 		}
 
@@ -177,12 +175,21 @@
 		purple_debug_error("yahoo", "Received unknown value for presence key: %d\n", value);
 		return;
 	}
-
-	if(msn)
-		who = g_strconcat("msn/", temp, NULL);
-	else
-		who = g_strdup(temp);
-
+	
+	switch (fed) {
+		case YAHOO_FEDERATION_MSN:
+			who = g_strconcat("msn/", temp, NULL);
+			break;
+		case YAHOO_FEDERATION_OCS:
+			who = g_strconcat("ocs/", temp, NULL);
+			break;
+		case YAHOO_FEDERATION_IBM:
+			who = g_strconcat("ibm/", temp, NULL);
+			break;
+		case YAHOO_FEDERATION_NONE:
+			who = g_strdup(temp);
+			break;
+	}
 	g_return_if_fail(who != NULL);
 
 	f = yahoo_friend_find(gc, who);
@@ -228,12 +235,12 @@
 	f = yahoo_friend_find(gc, name);
 	if (!f)
 		return;
-
-	if(f->protocol == 2)
+	
+	if(f->fed != YAHOO_FEDERATION_NONE)
 		temp = name+4;
 	else
 		temp = name;
-
+    
 	/* No need to change the value if it is already correct */
 	if (f->presence == presence) {
 		purple_debug_info("yahoo", "Not setting presence because there are no changes.\n");
@@ -258,12 +265,12 @@
 		if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) {
 			pkt = yahoo_packet_new(YAHOO_SERVICE_PRESENCE_PERM,
 					YAHOO_STATUS_AVAILABLE, yd->session_id);
-			if(f->protocol)
+			if(f->fed)
 				yahoo_packet_hash(pkt, "ssssssiss",
 					1, purple_connection_get_display_name(gc),
 					31, "2", 13, "2",
 					302, "319", 300, "319",
-					7, temp, 241, f->protocol,
+					7, temp, 241, f->fed,
 					301, "319", 303, "319");
 			else
 				yahoo_packet_hash(pkt, "ssssssss",
@@ -285,12 +292,12 @@
 		pkt = yahoo_packet_new(service,
 				YAHOO_STATUS_AVAILABLE, yd->session_id);
 
-		if(f->protocol)
+		if(f->fed)
 			yahoo_packet_hash(pkt, "ssssssiss",
 				1, purple_connection_get_display_name(gc),
 				31, thirtyone, 13, thirteen,
 				302, "319", 300, "319",
-				7, temp, 241, f->protocol,
+				7, temp, 241, f->fed,
 				301, "319", 303, "319");
 		else
 			yahoo_packet_hash(pkt, "ssssssss",
--- a/libpurple/protocols/yahoo/yahoo_friend.h	Sun Oct 11 17:36:59 2009 +0900
+++ b/libpurple/protocols/yahoo/yahoo_friend.h	Wed Oct 14 13:26:08 2009 +0900
@@ -41,6 +41,7 @@
 	YAHOO_P2PSTATUS_WE_ARE_CLIENT
 } YahooP2PStatus;
 
+
 /* these are called friends instead of buddies mainly so I can use variables
  * named f and not confuse them with variables named b
  */
@@ -54,7 +55,7 @@
 	gchar *ip;
 	gboolean bicon_sent_request;
 	YahooPresenceVisibility presence;
-	int protocol; /* 1=LCS, 2=MSN*/
+	YahooFederation fed; 
 	long int version_id;
 	YahooPersonalDetails ypd;
 	YahooP2PStatus p2p_status;
--- a/pidgin/gtkutils.c	Sun Oct 11 17:36:59 2009 +0900
+++ b/pidgin/gtkutils.c	Wed Oct 14 13:26:08 2009 +0900
@@ -3901,6 +3901,40 @@
 	return (gnome_url_handlers != NULL);
 }
 
+#ifdef _WIN32
+static void
+winpidgin_register_win32_url_handlers(void)
+{
+	int idx = 0;
+	LONG ret = ERROR_SUCCESS;
+
+	do {
+		DWORD nameSize = 256;
+		char protocol[256];
+		/* I don't think we need to worry about non-ASCII protocol names */
+		ret = RegEnumKeyExA(HKEY_CLASSES_ROOT, idx++, protocol, &nameSize,
+							NULL, NULL, NULL, NULL);
+		if (ret == ERROR_SUCCESS) {
+			HKEY reg_key = NULL;
+			ret = RegOpenKeyExA(HKEY_CLASSES_ROOT, protocol, 0, KEY_READ, &reg_key);
+			if (ret == ERROR_SUCCESS) {
+				ret = RegQueryValueExA(reg_key, "URL Protocol", NULL, NULL, NULL, NULL);
+				if (ret == ERROR_SUCCESS) {
+					/* We still pass everything to the "http" "open" handler for security reasons */
+					gtk_imhtml_class_register_protocol(protocol, url_clicked_cb, link_context_menu);
+				}
+				RegCloseKey(reg_key);
+			}
+			ret = ERROR_SUCCESS;
+		}
+	} while (ret == ERROR_SUCCESS);
+
+	if (ret != ERROR_NO_MORE_ITEMS)
+		purple_debug_error("winpidgin", "Error iterating HKEY_CLASSES_ROOT subkeys: %ld\n",
+						   ret);
+}
+#endif
+
 void pidgin_utils_init(void)
 {
 	gtk_imhtml_class_register_protocol("http://", url_clicked_cb, link_context_menu);
@@ -3918,6 +3952,11 @@
 	/* If we're under GNOME, try registering the system URL handlers. */
 	if (purple_running_gnome())
 		register_gnome_url_handlers();
+
+#ifdef _WIN32
+	winpidgin_register_win32_url_handlers();
+#endif
+
 }
 
 void pidgin_utils_uninit(void)