changeset 24712:25e6f323b68f

propagate from branch 'im.pidgin.pidgin' (head 6a13176e096cf5e7cefc5716496a0a7afd2e2621) to branch 'im.pidgin.cpw.malu.xmpp.attention' (head d027e41df4b69f14cba595d2e9f20efc47496887)
author Marcus Lundblad <ml@update.uu.se>
date Tue, 09 Dec 2008 20:31:43 +0000
parents c0226e400836 (diff) 2ad34edb3c80 (current diff)
children 9b1aea1c8df2
files libpurple/protocols/jabber/jabber.c
diffstat 20 files changed, 257 insertions(+), 153 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Dec 07 01:40:48 2008 +0000
+++ b/ChangeLog	Tue Dec 09 20:31:43 2008 +0000
@@ -13,6 +13,9 @@
 	* The Buddy State Notification plugin no longer turns JID's, MSN Passport
 	  ID's, etc. into links (Florian Quèze)
 	* Fix a crash in SIMPLE when a malformed message is received.
+	* On MSN, the Games and Office media can now be set and displayed (in
+	  addition to the previous Music media). The Media status text now shows
+	  the album, if possible. 
 	* purple-remote now has a "getstatusmessage" command to retrieve the text
 	  of the current status message.
 	* Various fixes to the nullprpl (Paul Aurich)
--- a/libpurple/protocols/jabber/jabber.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Tue Dec 09 20:31:43 2008 +0000
@@ -200,7 +200,7 @@
 		if (requested_resource != NULL) {
 			resource = xmlnode_new_child(bind, "resource");
 			xmlnode_insert_data(resource, requested_resource, -1);
-			free(requested_resource);
+			g_free(requested_resource);
 		}
 
 		jabber_iq_set_callback(iq, jabber_bind_result_cb, NULL);
--- a/libpurple/protocols/msn/contact.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/msn/contact.c	Tue Dec 09 20:31:43 2008 +0000
@@ -176,28 +176,6 @@
 	return 0;
 }
 
-/* get Network */
-/* QuLogic: These names don't really refer to the MsnNetwork,
- *          but I haven't yet written the code to properly use them.
- */
-static MsnNetwork
-msn_get_network(char *type)
-{
-	g_return_val_if_fail(type != NULL, 0);
-
-	if (!strcmp(type,"Regular")) {
-		return MSN_NETWORK_PASSPORT;
-	}
-	if (!strcmp(type,"Live")) {
-		return MSN_NETWORK_PASSPORT;
-	}
-	if (!strcmp(type,"LivePending")) {
-		return MSN_NETWORK_PASSPORT;
-	}
-
-	return MSN_NETWORK_UNKNOWN;
-}
-
 /* Create the AddressBook in the server, if we don't have one */
 static void
 msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
@@ -245,9 +223,26 @@
 	char *type = xmlnode_get_data(xmlnode_get_child(member, "Type"));
 	char *member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId"));
 	MsnUser *user = msn_userlist_find_add_user(session->userlist, passport, NULL);
+	xmlnode *annotation;
+	guint nid = MSN_NETWORK_PASSPORT;
 
-	purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s\n",
-		node, passport, type, member_id == NULL ? "(null)" : member_id);
+	for (annotation = xmlnode_get_child(member, "Annotations/Annotation");
+	     annotation;
+	     annotation = xmlnode_get_next_twin(annotation)) {
+		char *name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
+		if (name && !strcmp(name, "MSN.IM.BuddyType")) {
+			char *value = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+			if (value != NULL)
+				nid = strtoul(value, NULL, 10);
+			g_free(value);
+		}
+		g_free(name);
+	}
+ 
+	purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s, NetworkID: %u\n",
+		node, passport, type, member_id == NULL ? "(null)" : member_id, nid);
+
+	msn_user_set_network(user, nid);
 
 	if (member_id) {
 		user->membership_id[list] = atoi(member_id);
@@ -445,16 +440,16 @@
 		if ((groupInfo = xmlnode_get_child(group, "groupInfo")) && (groupname = xmlnode_get_child(groupInfo, "name")))
 			group_name = xmlnode_get_data(groupname);
 
-		msn_group_new(session->userlist, group_id, group_name);
-
-		if (group_id == NULL){
+		if (group_id == NULL) {
 			/* Group of ungroupped buddies */
 			g_free(group_name);
 			continue;
 		}
 
+		msn_group_new(session->userlist, group_id, group_name);
+
 		purple_debug_info("msn", "AB group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)");
-		if ((purple_find_group(group_name)) == NULL){
+		if ((purple_find_group(group_name)) == NULL) {
 			PurpleGroup *g = purple_group_new(group_name);
 			purple_blist_add_group(g, NULL);
 		}
@@ -528,7 +523,6 @@
 		xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user;
 		xmlnode *annotation;
 		MsnUser *user;
-		MsnNetwork networkId;
 
 		if (!(contactId = xmlnode_get_child(contactNode,"contactId"))
 				|| !(contactInfo = xmlnode_get_child(contactNode, "contactInfo"))
@@ -569,7 +563,6 @@
 			g_free(is_messenger_user);
 		}
 
-		networkId = msn_get_network(type);
 		passportName = xmlnode_get_child(contactInfo, "passportName");
 		if (passportName == NULL) {
 			xmlnode *emailsNode, *contactEmailNode, *emailNode;
@@ -600,7 +593,6 @@
 				if (msnEnabled && !strcmp(msnEnabled, "true")) {
 					/*Messenger enabled, Get the Passport*/
 					purple_debug_info("msn", "AB Yahoo User %s\n", passport ? passport : "(null)");
-					networkId = MSN_NETWORK_YAHOO;
 					g_free(msnEnabled);
 					break;
 				} else {
@@ -628,6 +620,15 @@
 			name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
 			if (!strcmp(name, "AB.NickName"))
 				alias = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+			else if (!strcmp(name, "MSN.IM.HasSharedFolder"))
+				; /* Do nothing yet... */
+			else if (!strcmp(name, "AB.Spouse"))
+				; /* Do nothing yet... */
+			else if (!strcmp(name, "MSN.Mobile.ContactId"))
+				; /* Do nothing yet... */
+			else
+				purple_debug_info("msn",
+				                  "Unknown AB contact annotation: %s\n", name);
 			g_free(name);
 		}
 
@@ -639,7 +640,6 @@
 
 		user = msn_userlist_find_add_user(session->userlist, passport, Name);
 		msn_user_set_uid(user, uid);
-		msn_user_set_network(user, networkId);
 		msn_user_set_mobile_phone(user, mobile_number);
 
 		groupIds = xmlnode_get_child(contactInfo, "groupIds");
--- a/libpurple/protocols/msn/msn.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Tue Dec 09 20:31:43 2008 +0000
@@ -647,25 +647,41 @@
 	presence = purple_buddy_get_presence(buddy);
 	status = purple_presence_get_active_status(presence);
 
-	/* I think status message should take precedence over media */
-	msg = purple_status_get_attr_string(status, "message");
-	if (msg && *msg)
-		return g_markup_escape_text(msg, -1);
-
 	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
-		const char *title, *artist;
+		const char *title, *game, *office;
 		char *media, *esc;
 		status = purple_presence_get_status(presence, "tune");
 		title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
-		artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
-
-		media = g_strdup_printf("%s%s%s", title, artist ? " - " : "",
-				artist ? artist : "");
+
+		game = purple_status_get_attr_string(status, "game");
+		office = purple_status_get_attr_string(status, "office");
+
+		if (title && *title) {
+			const char *artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
+			const char *album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
+			media = g_strdup_printf("%s%s%s%s%s%s", title,
+			                        (artist && *artist) ? " - " : "",
+			                        (artist && *artist) ? artist : "",
+			                        (album && *album) ? " (" : "",
+			                        (album && *album) ? album : "",
+			                        (album && *album) ? ")" : "");
+		}
+		else if (game && *game)
+			media = g_strdup_printf("Playing %s", game);
+		else if (office && *office)
+			media = g_strdup_printf("Editing %s", office);
+		else
+			return NULL;
 		esc = g_markup_escape_text(media, -1);
 		g_free(media);
 		return esc;
 	}
 
+	/* Official client says media takes precedence over message */
+	msg = purple_status_get_attr_string(status, "message");
+	if (msg && *msg)
+		return g_markup_escape_text(msg, -1);
+
 	return NULL;
 }
 
@@ -681,6 +697,7 @@
 	if (purple_presence_is_online(presence))
 	{
 		const char *psm, *name;
+		const char *mediatype = NULL;
 		char *currentmedia = NULL;
 		char *tmp;
 
@@ -688,10 +705,20 @@
 		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
 			PurpleStatus *tune = purple_presence_get_status(presence, "tune");
 			const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
-			const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
-			const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
-			currentmedia = purple_util_format_song_info(title, artist, album, NULL);
-			/* We could probably just use user->media.title etc. here */
+			const char *game = purple_status_get_attr_string(tune, "game");
+			const char *office = purple_status_get_attr_string(tune, "office");
+			if (title && *title) {
+				const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
+				const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
+				mediatype = _("Now Listening");
+				currentmedia = purple_util_format_song_info(title, artist, album, NULL);
+			} else if (game && *game) {
+				mediatype = _("Playing a game");
+				currentmedia = g_strdup(game);
+			} else if (office && *office) {
+				mediatype = _("Working");
+				currentmedia = g_strdup(office);
+			}
 		}
 
 		if (!purple_status_is_available(status)) {
@@ -745,7 +772,7 @@
 		}
 
 		if (currentmedia) {
-			purple_notify_user_info_add_pair(user_info, _("Now Listening"), currentmedia);
+			purple_notify_user_info_add_pair(user_info, mediatype, currentmedia);
 			g_free(currentmedia);
 		}
 	}
@@ -840,6 +867,8 @@
 			PURPLE_TUNE_ARTIST, _("Artist"), purple_value_new(PURPLE_TYPE_STRING),
 			PURPLE_TUNE_ALBUM, _("Album"), purple_value_new(PURPLE_TYPE_STRING),
 			PURPLE_TUNE_TITLE, _("Title"), purple_value_new(PURPLE_TYPE_STRING),
+			"game", _("Game Title"), purple_value_new(PURPLE_TYPE_STRING),
+			"office", _("Office Title"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 
@@ -1811,7 +1840,7 @@
 		if (b->server_alias)
 		{
 			char *nicktext = g_markup_escape_text(b->server_alias, -1);
-			tmp = g_strdup_printf("<font sml=\"msn\">%s</font><br>", nicktext);
+			tmp = g_strdup_printf("<font sml=\"msn\">%s</font>", nicktext);
 			purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
 			g_free(tmp);
 			g_free(nicktext);
@@ -1917,9 +1946,8 @@
 
 	if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0)
 	{
-		tmp = g_strdup_printf("<b>%s</b>", _("Error retrieving profile"));
-		purple_notify_user_info_add_pair(user_info, NULL, tmp);
-		g_free(tmp);
+		purple_notify_user_info_add_pair(user_info,
+				_("Error retrieving profile"), NULL);
 
 		purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
 		purple_notify_user_info_destroy(user_info);
@@ -2260,21 +2288,24 @@
 		char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http&#58;&#47;&#47;spaces.live.com&#47;profile.aspx&#63;cid&#61;0\"");
 		PurpleBuddy *b = purple_find_buddy
 				(purple_connection_get_account(info_data->gc), info_data->name);
-		purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"),
-									   ((p && b) ? _("The user has not created a public profile.") :
-										(p ? _("MSN reported not being able to find the user's profile. "
-											   "This either means that the user does not exist, "
-											   "or that the user exists "
-											   "but has not created a public profile.") :
-										 _("Could not find "	/* This should never happen */
-										   "any information in the user's profile. "
-										   "The user most likely does not exist."))));
+		purple_notify_user_info_add_pair(user_info,
+				_("Error retrieving profile"), NULL);
+		purple_notify_user_info_add_pair(user_info, NULL,
+				((p && b) ? _("The user has not created a public profile.") :
+					(p ? _("MSN reported not being able to find the user's profile. "
+							"This either means that the user does not exist, "
+							"or that the user exists "
+							"but has not created a public profile.") :
+						_("Could not find "	/* This should never happen */
+							"any information in the user's profile. "
+							"The user most likely does not exist."))));
 	}
 
 	/* put a link to the actual profile URL */
-	tmp = g_strdup_printf("<a href=\"%s%s\">%s%s</a>",
-					PROFILE_URL, info_data->name, PROFILE_URL, info_data->name);
-	purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp);
+	purple_notify_user_info_add_section_break(user_info);
+	tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>",
+			PROFILE_URL, info_data->name, _("View web profile"));
+	purple_notify_user_info_add_pair(user_info, NULL, tmp);
 	g_free(tmp);
 
 #if PHOTO_SUPPORT
--- a/libpurple/protocols/msn/notification.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/msn/notification.c	Tue Dec 09 20:31:43 2008 +0000
@@ -1578,7 +1578,7 @@
 	MsnUser *user;
 	const char *passport;
 	char *psm_str, *str;
-	CurrentMedia media = {NULL, NULL, NULL};
+	CurrentMedia media = {CURRENT_MEDIA_UNKNOWN, NULL, NULL, NULL};
 
 	session = cmdproc->session;
 	account = session->account;
--- a/libpurple/protocols/msn/state.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/msn/state.c	Tue Dec 09 20:31:43 2008 +0000
@@ -100,14 +100,15 @@
 	cmedia_array = g_strsplit(cmedia, "\\0", 0);
 
 	/*
-	 * 0: Media Player
-	 * 1: 'Music'
+	 * 0: Application
+	 * 1: 'Music'/'Games'/'Office'
 	 * 2: '1' if enabled, '0' if not
 	 * 3: Format (eg. {0} by {1})
 	 * 4: Title
-	 * 5: Artist
-	 * 6: Album
-	 * 7: ?
+	 * If 'Music':
+	 *  5: Artist
+	 *  6: Album
+	 *  7: ?
 	 */
 #if GLIB_CHECK_VERSION(2,6,0)
 	strings  = g_strv_length(cmedia_array);
@@ -118,6 +119,15 @@
 	if (strings >= 4 && !strcmp(cmedia_array[2], "1")) {
 		parsed = TRUE;
 
+		if (!strcmp(cmedia_array[1], "Music"))
+			media->type = CURRENT_MEDIA_MUSIC;
+		else if (!strcmp(cmedia_array[1], "Games"))
+			media->type = CURRENT_MEDIA_GAMES;
+		else if (!strcmp(cmedia_array[1], "Office"))
+			media->type = CURRENT_MEDIA_OFFICE;
+		else
+			media->type = CURRENT_MEDIA_UNKNOWN;
+
 		g_free(media->title);
 		if (strings == 4) {
 			media->title = g_strdup(cmedia_array[3]);
@@ -199,21 +209,33 @@
 static char *
 create_media_string(PurplePresence *presence)
 {
-	const char *artist, *title, *album;
+	const char *title, *game, *office;
 	char *ret;
 	PurpleStatus *status = purple_presence_get_status(presence, "tune");
 	if (!status || !purple_status_is_active(status))
-		return g_strdup_printf("WMP\\0Music\\00\\0{0} - {1}\\0\\0\\0\\0\\0");
+		return g_strdup_printf("\\0Music\\00\\0\\0");
 
-	artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
 	title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
-	album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
+	game = purple_status_get_attr_string(status, "game");
+	office = purple_status_get_attr_string(status, "office");
 
-	ret = g_strdup_printf("WMP\\0Music\\0%c\\0{0} - {1}\\0%s\\0%s\\0%s\\0\\0",
-			(title && *title) ? '1' : '0',
-			title ? title : "",
-			artist ? artist : "",
-			album ? album : "");
+	if (title && *title) {
+		const char *artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
+		const char *album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
+		ret = g_strdup_printf("WMP\\0Music\\01\\0{0}%s%s\\0%s\\0%s\\0%s\\0",
+		                      artist ? " - {1}" : "",
+		                      album ? " ({2})" : "",
+		                      title,
+		                      artist ? artist : "",
+		                      album ? album : "");
+	}
+	else if (game && *game)
+		ret = g_strdup_printf("\\0Games\\01\\0Playing {0}\\0%s\\0", game);
+	else if (office && *office)
+		ret = g_strdup_printf("\\0Office\\01\\0Editing {0}\\0%s\\0", office);
+	else
+		ret = g_strdup_printf("\\0Music\\00\\0\\0");
+
 	return ret;
 }
 
--- a/libpurple/protocols/msn/user.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/msn/user.c	Tue Dec 09 20:31:43 2008 +0000
@@ -106,12 +106,25 @@
 		purple_prpl_got_user_status_deactive(account, user->passport, "mobile");
 	}
 
-	if (!offline && user->media.title) {
-		purple_prpl_got_user_status(account, user->passport, "tune",
-				PURPLE_TUNE_ARTIST, user->media.artist,
-				PURPLE_TUNE_ALBUM, user->media.album,
-				PURPLE_TUNE_TITLE, user->media.title,
-				NULL);
+	if (!offline && user->media.type != CURRENT_MEDIA_UNKNOWN) {
+		if (user->media.type == CURRENT_MEDIA_MUSIC) {
+			purple_prpl_got_user_status(account, user->passport, "tune",
+			                            PURPLE_TUNE_ARTIST, user->media.artist,
+			                            PURPLE_TUNE_ALBUM, user->media.album,
+			                            PURPLE_TUNE_TITLE, user->media.title,
+			                            NULL);
+		} else if (user->media.type == CURRENT_MEDIA_GAMES) {
+			purple_prpl_got_user_status(account, user->passport, "tune",
+			                            "game", user->media.title,
+			                            NULL);
+		} else if (user->media.type == CURRENT_MEDIA_OFFICE) {
+			purple_prpl_got_user_status(account, user->passport, "tune",
+			                            "office", user->media.title,
+			                            NULL);
+		} else {
+			purple_debug_warning("msn", "Got CurrentMedia with unknown type %d.\n",
+			                     user->media.type);
+		}
 	} else {
 		purple_prpl_got_user_status_deactive(account, user->passport, "tune");
 	}
@@ -191,6 +204,7 @@
 	g_free(user->media.album);
 	g_free(user->media.artist);
 
+	user->media.type   = media ? media->type : CURRENT_MEDIA_UNKNOWN;
 	user->media.title  = media ? g_strdup(media->title) : NULL;
 	user->media.artist = media ? g_strdup(media->artist) : NULL;
 	user->media.album  = media ? g_strdup(media->album) : NULL;
--- a/libpurple/protocols/msn/user.h	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/msn/user.h	Tue Dec 09 20:31:43 2008 +0000
@@ -45,11 +45,20 @@
 /**
  * Current media.
  */
+typedef enum
+{
+	CURRENT_MEDIA_UNKNOWN,
+	CURRENT_MEDIA_MUSIC,
+	CURRENT_MEDIA_GAMES,
+	CURRENT_MEDIA_OFFICE
+} CurrentMediaType;
+
 typedef struct _CurrentMedia
 {
+	CurrentMediaType type;     /**< Type.   */
+	char *title;    /**< Title.  */
 	char *artist;   /**< Artist. */
 	char *album;    /**< Album.  */
-	char *title;    /**< Title.  */
 } CurrentMedia;
 
 /**
--- a/libpurple/protocols/myspace/user.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/myspace/user.c	Tue Dec 09 20:31:43 2008 +0000
@@ -107,17 +107,6 @@
 
 	uid = purple_blist_node_get_int(&user->buddy->node, "UserID");
 
-	if (full) {
-		/* TODO: link to username, if available */
-		if (uid) {
-			char *profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">http://myspace.com/%d</a>",
-											uid, uid);
-			purple_notify_user_info_add_pair(user_info, _("Profile"), profile);
-			g_free(profile);
-		}
-	}
-
-
 	/* a/s/l...the vitals */
 	if (user->age) {
 		char age[16];
@@ -180,6 +169,16 @@
 			purple_notify_user_info_add_pair(user_info, _("Client Version"), client);
 		g_free(client);
 	}
+
+	if (full && uid) {
+		/* TODO: link to username, if available */
+		char *profile;
+		purple_notify_user_info_add_section_break(user_info);
+		profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">%s</a>",
+				uid, _("View web profile"));
+		purple_notify_user_info_add_pair(user_info, NULL, profile);
+		g_free(profile);
+	}
 }
 
 /** Set the currently playing song artist and or title.
--- a/libpurple/protocols/oscar/bstream.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/oscar/bstream.c	Tue Dec 09 20:31:43 2008 +0000
@@ -302,3 +302,12 @@
 
 	return len;
 }
+
+int byte_stream_putuid(ByteStream *bs, OscarData *od)
+{
+	PurpleAccount *account;
+
+	account = purple_connection_get_account(od->gc);
+
+	return byte_stream_putle32(bs, atoi(purple_account_get_username(account)));
+}
--- a/libpurple/protocols/oscar/family_auth.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Tue Dec 09 20:31:43 2008 +0000
@@ -295,10 +295,9 @@
 	/*
 	 * No matter what, we should have a screen name.
 	 */
-	memset(od->sn, 0, sizeof(od->sn));
 	if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) {
 		info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1);
-		strncpy(od->sn, info->sn, sizeof(od->sn));
+		purple_connection_set_display_name(od->gc, info->sn);
 	}
 
 	/*
--- a/libpurple/protocols/oscar/family_icbm.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Tue Dec 09 20:31:43 2008 +0000
@@ -1214,7 +1214,7 @@
 	/*
 	 * Your UIN
 	 */
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 
 	/*
 	 * TLV t(type) l(strlen(message)+1) v(message+NULL)
--- a/libpurple/protocols/oscar/family_icq.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Tue Dec 09 20:31:43 2008 +0000
@@ -36,7 +36,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
-	purple_debug_info("oscar", "Requesting offline messages from %s", od->sn);
+	purple_debug_info("oscar", "Requesting offline messages\n");
 
 	bslen = 2 + 4 + 2 + 2;
 
@@ -49,7 +49,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x003c); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 
@@ -70,7 +70,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
-	purple_debug_info("oscar", "Acknowledged receipt of offline messages from %s", od->sn);
+	purple_debug_info("oscar", "Acknowledged receipt of offline messages\n");
 
 	bslen = 2 + 4 + 2 + 2;
 
@@ -83,7 +83,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x003e); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 
@@ -117,7 +117,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x0c3a); /* shrug. */
@@ -172,7 +172,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x042e); /* shrug. */
@@ -212,7 +212,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x04b2); /* shrug. */
@@ -259,7 +259,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x04ba); /* shrug. */
@@ -303,7 +303,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x051f); /* shrug. */
@@ -341,7 +341,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x0998); /* shrug. */
@@ -377,11 +377,12 @@
 int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias)
 {
 	FlapConnection *conn;
+	PurpleAccount *account;
 	ByteStream bs;
 	aim_snacid_t snacid;
 	int bslen, xmllen;
 	char *xml;
-	const char *timestr;
+	const char *timestr, *username;
 	time_t t;
 	struct tm *tm;
 	gchar *stripped;
@@ -392,6 +393,9 @@
 	if (!name || !msg || !alias)
 		return -EINVAL;
 
+	account = purple_connection_get_account(od->gc);
+	username = purple_account_get_username(account);
+
 	time(&t);
 	tm = gmtime(&t);
 	timestr = purple_utf8_strftime("%a, %d %b %Y %T %Z", tm);
@@ -399,7 +403,7 @@
 	stripped = purple_markup_strip_html(msg);
 
 	/* The length of xml included the null terminating character */
-	xmllen = 209 + strlen(name) + strlen(stripped) + strlen(od->sn) + strlen(alias) + strlen(timestr) + 1;
+	xmllen = 209 + strlen(name) + strlen(stripped) + strlen(username) + strlen(alias) + strlen(timestr) + 1;
 
 	xml = g_new(char, xmllen);
 	snprintf(xml, xmllen, "<icq_sms_message>"
@@ -411,7 +415,7 @@
 		"<delivery_receipt>Yes</delivery_receipt>"
 		"<time>%s</time>"
 		"</icq_sms_message>",
-		name, stripped, od->sn, alias, timestr);
+		name, stripped, username, alias, timestr);
 
 	bslen = 36 + xmllen;
 
@@ -424,7 +428,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 
@@ -481,7 +485,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x0fa0); /* shrug. */
--- a/libpurple/protocols/oscar/oscar.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Tue Dec 09 20:31:43 2008 +0000
@@ -3187,6 +3187,12 @@
 		}
 	}
 
+	purple_notify_user_info_add_section_break(user_info);
+	tmp = g_strdup_printf("<a href=\"http://profiles.aim.com/%s\">%s</a>",
+			purple_normalize(account, userinfo->sn), _("View web profile"));
+	purple_notify_user_info_add_pair(user_info, NULL, tmp);
+	g_free(tmp);
+
 	purple_notify_userinfo(gc, userinfo->sn, user_info, NULL, NULL);
 	purple_notify_user_info_destroy(user_info);
 
--- a/libpurple/protocols/oscar/oscar.h	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Tue Dec 09 20:31:43 2008 +0000
@@ -488,16 +488,8 @@
 		guint maxawaymsglen; /* max size (bytes) of posted away message */
 	} rights;
 
-	/* ---- Client Accessible ------------------------ */
-
-	/* Our screen name. */
-	/* TODO: Get rid of this and use purple_account_get_username() everywhere? */
-	char sn[MAXSNLEN+1];
-
 	PurpleConnection *gc;
 
-	/* ---- Internal Use Only ------------------------ */
-
 	void *modlistv;
 
 	/*
@@ -1587,6 +1579,7 @@
 int byte_stream_putraw(ByteStream *bs, const guint8 *v, int len);
 int byte_stream_putstr(ByteStream *bs, const char *str);
 int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, int len);
+int byte_stream_putuid(ByteStream *bs, OscarData *od);
 int byte_stream_putcaps(ByteStream *bs, guint32 caps);
 
 /*
--- a/libpurple/protocols/qq/qq.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Tue Dec 09 20:31:43 2008 +0000
@@ -100,10 +100,13 @@
 	qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE);
 
 	custom_server = purple_account_get_string(account, "server", NULL);
-	purple_debug_info("QQ", "Select server '%s'\n", custom_server);
-	if ( (custom_server != NULL && strlen(custom_server) > 0) && strcasecmp(custom_server, "auto") != 0) {
-		qd->servers = g_list_append(qd->servers, g_strdup(custom_server));
-		return;
+
+	if (custom_server != NULL) {
+		purple_debug_info("QQ", "Select server '%s'\n", custom_server);
+		if (*custom_server != '\0' && g_ascii_strcasecmp(custom_server, "auto") != 0) {
+			qd->servers = g_list_append(qd->servers, g_strdup(custom_server));
+			return;
+		}
 	}
 
 	if (qd->use_tcp) {
--- a/libpurple/protocols/yahoo/yahoo_aliases.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Tue Dec 09 20:31:43 2008 +0000
@@ -301,7 +301,7 @@
 						  "<ct a=\"1\" yi='%s' nn='%s' />\n</ab>\r\n",
 						  purple_account_get_username(gc->account),
 						  who, converted_alias_jp);
-			free(converted_alias_jp);
+			g_free(converted_alias_jp);
 			g_free(alias_jp);
 		} else {
 			gchar *escaped_alias = g_markup_escape_text(alias, -1);
@@ -321,7 +321,7 @@
 						  "<ct e=\"1\"  yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n",
 						  purple_account_get_username(gc->account),
 						  who, cb->id, converted_alias_jp);
-			free(converted_alias_jp);
+			g_free(converted_alias_jp);
 			g_free(alias_jp);
 		} else {
 			gchar *escaped_alias = g_markup_escape_text(alias, -1);
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Tue Dec 09 20:31:43 2008 +0000
@@ -807,7 +807,7 @@
 	 */
 	if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0) {
 		purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"), NULL);
-		purple_notify_userinfo(info_data->gc, info_data->name, 
+		purple_notify_userinfo(info_data->gc, info_data->name,
 			user_info, NULL, NULL);
 		purple_notify_user_info_destroy(user_info);
 		g_free(profile_url_text);
@@ -841,10 +841,10 @@
 						 _("If you wish to view this profile, "
 						"you will need to visit this link in your web browser:"),
 						 profile_url_text, profile_url_text);
-		purple_notify_user_info_add_pair(user_info, NULL, tmp);		
+		purple_notify_user_info_add_pair(user_info, NULL, tmp);
 		g_free(tmp);
 
-		purple_notify_userinfo(info_data->gc, info_data->name, 
+		purple_notify_userinfo(info_data->gc, info_data->name,
 				user_info, NULL, NULL);
 
 		g_free(profile_url_text);
@@ -1193,17 +1193,15 @@
 
 	if(!found)
 	{
-		GString *str = g_string_new("");
+		const gchar *str;
 
-		g_string_append_printf(str, "<br><b>");
-		g_string_append_printf(str, _("User information for %s unavailable"),
-				info_data->name);
-		g_string_append_printf(str, "</b><br>");
+		purple_notify_user_info_add_section_break(user_info);
+		purple_notify_user_info_add_pair(user_info,
+				_("Error retrieving profile"), NULL);
 
 		if (profile_state == PROFILE_STATE_UNKNOWN_LANGUAGE) {
-			g_string_append_printf(str, "%s<br><br>",
-					_("Sorry, this profile seems to be in a language "
-					  "or format that is not supported at this time."));
+			str = _("This profile is in a language "
+					  "or format that is not supported at this time.");
 
 		} else if (profile_state == PROFILE_STATE_NOT_FOUND) {
 			PurpleBuddy *b = purple_find_buddy
@@ -1217,27 +1215,26 @@
 				 */
 				f = yahoo_friend_find(b->account->gc, b->name);
 			}
-			g_string_append_printf(str, "%s<br><br>",
-				f?  _("Could not retrieve the user's profile. "
+			str = f ? _("Could not retrieve the user's profile. "
 					  "This most likely is a temporary server-side problem. "
-					  "Please try again later."):
+					  "Please try again later.") :
 					_("Could not retrieve the user's profile. "
 					  "This most likely means that the user does not exist; "
 					  "however, Yahoo! sometimes does fail to find a user's "
 					  "profile. If you know that the user exists, "
-					  "please try again later."));
+					  "please try again later.");
 		} else {
-			g_string_append_printf(str, "%s<br><br>",
-					_("The user's profile is empty."));
+			str = _("The user's profile is empty.");
 		}
-		
-		purple_notify_user_info_add_pair(user_info, NULL, str->str);
-		g_string_free(str, TRUE);
+
+		purple_notify_user_info_add_pair(user_info, NULL, str);
 	}
 
 	/* put a link to the actual profile URL */
-	tmp = g_strdup_printf("<a href=\"%s\">%s</a>", profile_url_text, profile_url_text);
-	purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp);
+	purple_notify_user_info_add_section_break(user_info);
+	tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
+			profile_url_text, _("View web profile"));
+	purple_notify_user_info_add_pair(user_info, NULL, tmp);
 	g_free(tmp);
 
 	g_free(stripped);
--- a/libpurple/protocols/zephyr/zephyr.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Tue Dec 09 20:31:43 2008 +0000
@@ -1673,7 +1673,7 @@
 
 			purple_debug_info("zephyr", "about to read from tzc\n");
 
-			if (waitpid(pid, NULL, WNOHANG) == 0) { // Only select if tzc is still running
+			if (waitpid(pid, NULL, WNOHANG) == 0) { /* Only select if tzc is still running */
 				purple_debug_info("zephyr", "about to read from tzc\n");
 				select_status = select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, NULL);
 			}
--- a/pidgin/gtkblist.c	Sun Dec 07 01:40:48 2008 +0000
+++ b/pidgin/gtkblist.c	Tue Dec 09 20:31:43 2008 +0000
@@ -3630,6 +3630,7 @@
 	const char *name = NULL;
 	char *filename, *path;
 	PurplePresence *p;
+	PurpleStatus *tune;
 
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		if(!gtknode->contact_expanded) {
@@ -3668,7 +3669,21 @@
 		return _pidgin_blist_get_cached_emblem(path);
 	}
 
-	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) {
+	tune = purple_presence_get_status(p, "tune");
+	if (tune && purple_status_is_active(tune)) {
+		/* Only in MSN.
+		 * TODO: Replace "Tune" with generalized "Media" in 3.0. */
+		if (purple_status_get_attr_string(tune, "game") != NULL) {
+			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "game.png", NULL);
+			return _pidgin_blist_get_cached_emblem(path);
+		}
+		/* Only in MSN.
+		 * TODO: Replace "Tune" with generalized "Media" in 3.0. */
+		if (purple_status_get_attr_string(tune, "office") != NULL) {
+			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "office.png", NULL);
+			return _pidgin_blist_get_cached_emblem(path);
+		}
+		/* Regular old "tune" is the only one in all protocols. */
 		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
 		return _pidgin_blist_get_cached_emblem(path);
 	}