changeset 25964:03541b3adb38

propagate from branch 'im.pidgin.pidgin' (head f3cf3ba10ebc911a27833496dcc4d4dca3de66c5) to branch 'im.pidgin.cpw.malu.xmpp.idle' (head 8a78a27a5123f05441fd5df8c423b57929973128)
author Marcus Lundblad <ml@update.uu.se>
date Sun, 01 Feb 2009 18:04:38 +0000
parents feea711ac242 (current diff) e6683ec9d310 (diff)
children c5d2559d6e0d
files
diffstat 14 files changed, 478 insertions(+), 417 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun Feb 01 18:00:05 2009 +0000
+++ b/COPYRIGHT	Sun Feb 01 18:04:38 2009 +0000
@@ -314,6 +314,7 @@
 Ted Percival
 Eduardo Pérez
 Matt Perry
+Luke Petre
 Diego Petten
 Nathan Peterson
 Sebastián E. Peyrott
--- a/ChangeLog	Sun Feb 01 18:00:05 2009 +0000
+++ b/ChangeLog	Sun Feb 01 18:04:38 2009 +0000
@@ -2,17 +2,24 @@
 
 version 2.5.5 (??/??/????):
 	libpurple:
-	* Fix transfer of buddy icons, custom smileys, and files from the
-	  latest Windows Live Messenger 9 official client. (Thomas
-	  Gibson-Robinson)
 	* Fix a crash when removing an account with an unknown protocol id.
-	* Large (multi-part) messages on MSN are now correctly re-combined.
 	* Beta support for SSL connections for AIM and ICQ accounts.  To
 	  enable, check the "Use SSL" option from the Advanced tab when
 	  editing your AIM or ICQ account. (Paul Aurich)
 	* Fix retrieval of ICQ status messages from users of ICQ 6.x, Miranda,
 	  and other libpurple clients (fixes with libpurple users only on
 	  statuses other than Available). (Daniel Ljungborg)
+	* Fix a memory leak in SILC. (Luke Petre)
+
+	MSN:
+	* Fix transfer of buddy icons, custom smileys, and files from the
+	  latest Windows Live Messenger 9 official client. (Thomas
+	  Gibson-Robinson)
+	* Large (multi-part) messages are now correctly re-combined.
+	* Federated/Yahoo! buddies should now stop creating sync issues at
+	  every signin.  You may need to remove duplicates in the Address
+	  Book.  See the FAQ for more information.
+	* Messages from Yahoo! buddies are no longer silently dropped.
 
 	Finch:
 	* Allow rebinding keys to change the focused widget (details in the
--- a/ChangeLog.win32	Sun Feb 01 18:00:05 2009 +0000
+++ b/ChangeLog.win32	Sun Feb 01 18:04:38 2009 +0000
@@ -1,3 +1,8 @@
+version 2.5.5 (??/??/2009):
+	* Remove the "Flash window when chat messages are received" pref from
+	  the Windows Pidgin Options plugin - the Message Notification plugin
+	  does this (and much more).
+
 version 2.5.4 (01/12/2009):
 	* Fix the "Hang on Exit" issue that a number of users encountered.
 	* Updated GTK+ to 2.14.6
--- a/libpurple/network.c	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/network.c	Sun Feb 01 18:04:38 2009 +0000
@@ -74,8 +74,12 @@
 
 /* Mutex for the other global vars */
 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
-static gboolean network_initialized;
-static HANDLE network_change_handle;
+static gboolean network_initialized = FALSE;
+static HANDLE network_change_handle = NULL;
+static int (WSAAPI *MyWSANSPIoctl) (
+		HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer,
+		DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
+		LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL;
 #endif
 
 struct _PurpleNetworkListenData {
@@ -538,27 +542,28 @@
 	return FALSE;
 }
 
+static gboolean _print_debug_msg(gpointer data) {
+	gchar *msg = data;
+	purple_debug_warning("network", msg);
+	g_free(msg);
+	return FALSE;
+}
+
 static gpointer wpurple_network_change_thread(gpointer data)
 {
 	WSAQUERYSET qs;
 	WSAEVENT *nla_event;
-	time_t last_trigger = time(NULL);
-
-	int (WSAAPI *MyWSANSPIoctl) (
-		HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer,
-		DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
-		LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL;
-
-	if (!(MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) {
-		g_thread_exit(NULL);
-		return NULL;
-	}
+	time_t last_trigger = time(NULL) - 31;
+	char buf[4096];
+	WSAQUERYSET *res = (LPWSAQUERYSET) buf;
+	DWORD size;
 
 	if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) {
 		int errorid = WSAGetLastError();
 		gchar *msg = g_win32_error_message(errorid);
-		purple_debug_warning("network", "Couldn't create WSA event. "
-			"Message: %s (%d).\n", msg, errorid);
+		purple_timeout_add(0, _print_debug_msg,
+						   g_strdup_printf("Couldn't create WSA event. "
+										   "Message: %s (%d).\n", msg, errorid));
 		g_free(msg);
 		g_thread_exit(NULL);
 		return NULL;
@@ -579,30 +584,26 @@
 			return NULL;
 		}
 
-		memset(&qs, 0, sizeof(WSAQUERYSET));
-		qs.dwSize = sizeof(WSAQUERYSET);
-		qs.dwNameSpace = NS_NLA;
-		if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) {
-			int errorid = WSAGetLastError();
-			gchar *msg = g_win32_error_message(errorid);
-			purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. "
-				"NLA service is probably not running. Message: %s (%d).\n",
-				msg, errorid);
-			g_free(msg);
-			WSACloseEvent(nla_event);
-			g_static_mutex_unlock(&mutex);
-			g_thread_exit(NULL);
-			return NULL;
+		if (network_change_handle == NULL) {
+			memset(&qs, 0, sizeof(WSAQUERYSET));
+			qs.dwSize = sizeof(WSAQUERYSET);
+			qs.dwNameSpace = NS_NLA;
+			if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) {
+				int errorid = WSAGetLastError();
+				gchar *msg = g_win32_error_message(errorid);
+				purple_timeout_add(0, _print_debug_msg,
+								   g_strdup_printf("Couldn't retrieve NLA SP lookup handle. "
+												   "NLA service is probably not running. Message: %s (%d).\n",
+													msg, errorid));
+				g_free(msg);
+				WSACloseEvent(nla_event);
+				g_static_mutex_unlock(&mutex);
+				g_thread_exit(NULL);
+				return NULL;
+			}
 		}
 		g_static_mutex_unlock(&mutex);
 
-		/* Make sure at least 30 seconds have elapsed since the last
-		 * notification so we don't peg the cpu if this keeps changing. */
-		if ((time(NULL) - last_trigger) < 30)
-			Sleep(30000);
-
-		last_trigger = time(NULL);
-
 		memset(&completion, 0, sizeof(WSACOMPLETION));
 		completion.Type = NSP_NOTIFY_EVENT;
 		overlapped.hEvent = nla_event;
@@ -610,18 +611,34 @@
 
 		if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) {
 			int errorid = WSAGetLastError();
+			if (errorid == WSA_INVALID_HANDLE) {
+				purple_timeout_add(0, _print_debug_msg,
+								   g_strdup("Invalid NLA handle; resetting.\n"));
+				g_static_mutex_lock(&mutex);
+				retval = WSALookupServiceEnd(network_change_handle);
+				network_change_handle = NULL;
+				g_static_mutex_unlock(&mutex);
+				continue;
 			/* WSA_IO_PENDING indicates successful async notification will happen */
-			if (errorid != WSA_IO_PENDING) {
+			} else if (errorid != WSA_IO_PENDING) {
 				gchar *msg = g_win32_error_message(errorid);
-				purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n",
-					msg, errorid);
+				purple_timeout_add(0, _print_debug_msg,
+								   g_strdup_printf("Unable to wait for changes. Message: %s (%d).\n",
+												   msg, errorid));
 				g_free(msg);
 			}
 		}
 
+		/* Make sure at least 30 seconds have elapsed since the last
+		 * notification so we don't peg the cpu if this keeps changing. */
+		if ((time(NULL) - last_trigger) < 30)
+			Sleep(30000);
+
 		/* This will block until NLA notifies us */
 		retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE);
 
+		last_trigger = time(NULL);
+
 		g_static_mutex_lock(&mutex);
 		if (network_initialized == FALSE) {
 			/* Time to die */
@@ -631,8 +648,14 @@
 			return NULL;
 		}
 
-		retval = WSALookupServiceEnd(network_change_handle);
-		network_change_handle = NULL;
+		size = sizeof(buf);
+		while ((retval = WSALookupServiceNext(network_change_handle, 0, &size, res)) == ERROR_SUCCESS) {
+			/*purple_timeout_add(0, _print_debug_msg,
+							   g_strdup_printf("thread found network '%s'\n",
+											   res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)"));*/
+			size = sizeof(buf);
+		}
+
 		WSAResetEvent(nla_event);
 		g_static_mutex_unlock(&mutex);
 
@@ -768,11 +791,12 @@
 	if (cnt < 0) /* Assume there is a network */
 		current_network_count = 1;
 	/* Don't listen for network changes if we can't tell anyway */
-	else
-	{
+	else {
 		current_network_count = cnt;
-		if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err))
-			purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : "");
+		if ((MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) {
+			if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err))
+				purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : "");
+		}
 	}
 #endif
 
@@ -848,10 +872,12 @@
 				msg, errorid);
 			g_free(msg);
 		}
+		network_change_handle = NULL;
+
 	}
 	g_static_mutex_unlock(&mutex);
 
 #endif
 	purple_signal_unregister(purple_network_get_handle(),
-	                         "network-configuration-changed");
+							 "network-configuration-changed");
 }
--- a/libpurple/protocols/msn/contact.c	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Sun Feb 01 18:04:38 2009 +0000
@@ -158,6 +158,113 @@
 	state->action |= action;
 }
 
+/***************************************************************
+ * General SOAP handling
+ ***************************************************************/
+
+static const char *
+msn_contact_operation_str(MsnCallbackAction action)
+{
+	/* Make sure this is large enough when adding more */
+	static char buf[BUF_LEN];
+	buf[0] = '\0';
+
+	if (action & MSN_ADD_BUDDY)
+		strcat(buf, "Adding Buddy,");
+	if (action & MSN_MOVE_BUDDY)
+		strcat(buf, "Moving Buddy,");
+	if (action & MSN_ACCEPTED_BUDDY)
+		strcat(buf, "Accepted Buddy,");
+	if (action & MSN_DENIED_BUDDY)
+		strcat(buf, "Denied Buddy,");
+	if (action & MSN_ADD_GROUP)
+		strcat(buf, "Adding Group,");
+	if (action & MSN_DEL_GROUP)
+		strcat(buf, "Deleting Group,");
+	if (action & MSN_RENAME_GROUP)
+		strcat(buf, "Renaming Group,");
+	if (action & MSN_UPDATE_INFO)
+		strcat(buf, "Updating Contact Info,");
+
+	return buf;
+}
+
+static gboolean msn_contact_request(MsnCallbackState *state);
+
+static void
+msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
+{
+	MsnCallbackState *state = data;
+	xmlnode *fault;
+	char *faultcode_str;
+
+	if (resp == NULL) {
+		purple_debug_error("msn",
+		                   "Operation {%s} failed. No response received from server.\n",
+		                   msn_contact_operation_str(state->action));
+		return;
+	}
+
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+
+	if (fault == NULL) {
+		/* No errors */
+		if (state->cb)
+			((MsnSoapCallback)state->cb)(req, resp, data);
+		msn_callback_state_free(state);
+		return;
+	}
+
+	faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode"));
+
+	if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
+		purple_debug_info("msn",
+		                  "Contact Operation {%s} failed because of bad token."
+		                  " Updating token now and retrying operation.\n",
+		                  msn_contact_operation_str(state->action));
+		/* Token has expired, so renew it, and try again later */
+		msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS,
+		                       (GSourceFunc)msn_contact_request, data);
+	}
+	else
+	{
+		if (state->cb) {
+			((MsnSoapCallback)state->cb)(req, resp, data);
+		} else {
+			/* We don't know how to respond to this faultcode, so log it */
+			char *str = xmlnode_to_str(fault, NULL);
+			purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+			                   msn_contact_operation_str(state->action), str);
+			g_free(str);
+		}
+		msn_callback_state_free(state);
+	}
+
+	g_free(faultcode_str);
+}
+
+static gboolean
+msn_contact_request(MsnCallbackState *state)
+{
+	if (state->token == NULL)
+		state->token = xmlnode_get_child(state->body,
+			"Header/ABAuthHeader/TicketToken");
+	/* delete old & replace with new token */
+	xmlnode_free(state->token->child);
+	xmlnode_insert_data(state->token,
+		msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1);
+	msn_soap_message_send(state->session,
+		msn_soap_message_new(state->post_action, xmlnode_copy(state->body)),
+		MSN_CONTACT_SERVER, state->post_url, FALSE,
+		msn_contact_request_cb, state);
+	return FALSE;
+}
+
+/***************************************************************
+ * Address Book and Membership List Operations
+ ***************************************************************/
+
 /*get MSN member role utility*/
 static MsnListId
 msn_get_memberrole(const char *role)
@@ -180,9 +287,10 @@
 static void
 msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
+	MsnCallbackState *state = data;
 	if (resp && xmlnode_get_child(resp->xml, "Body/Fault") == NULL) {
 		purple_debug_info("msn", "Address Book successfully created!\n");
-		msn_get_address_book((MsnSession *)data, MSN_PS_INITIAL, NULL, NULL);
+		msn_get_address_book(state->session, MSN_PS_INITIAL, NULL, NULL);
 	} else {
 		purple_debug_info("msn", "Address Book creation failed!\n");
 	}
@@ -192,7 +300,7 @@
 msn_create_address_book(MsnSession *session)
 {
 	gchar *body;
-	gchar *token_str;
+	MsnCallbackState *state;
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->user != NULL);
@@ -200,17 +308,15 @@
 	
 	purple_debug_info("msn", "Creating an Address Book.\n");
 
-	token_str = g_markup_escape_text(
-		msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1);
 	body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE,
-		token_str, session->user->passport);
-	g_free(token_str);
+	                       session->user->passport);
 
-	msn_soap_message_send(session,
-		msn_soap_message_new(MSN_ADD_ADDRESSBOOK_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE,
-		msn_create_address_cb, session);
+	state = msn_callback_state_new(session);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_ADD_ADDRESSBOOK_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_create_address_cb;
+	msn_contact_request(state);
 
 	g_free(body);
 }
@@ -243,7 +349,7 @@
 			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);
 
@@ -362,8 +468,8 @@
 msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
 	gpointer data)
 {
-	GetContactListCbData *cb_data = data;
-	MsnSession *session = cb_data->session;
+	MsnCallbackState *state = data;
+	MsnSession *session = state->session;
 
 	g_return_if_fail(session != NULL);
 
@@ -379,7 +485,7 @@
 		dynamicItemLastChange = purple_account_get_string(session->account,
 			"dynamicItemLastChange", NULL);
 
-		if (cb_data->which == MSN_PS_INITIAL) {
+		if (state->partner_scenario == MSN_PS_INITIAL) {
 #ifdef MSN_PARTIAL_LISTS
 			/* XXX: this should be enabled when we can correctly do partial
 			   syncs with the server. Currently we need to retrieve the whole
@@ -390,8 +496,6 @@
 #endif
 		}
 	}
-
-	g_free(cb_data);
 }
 
 /*SOAP  get contact list*/
@@ -401,8 +505,7 @@
 {
 	gchar *body = NULL;
 	gchar *update_str = NULL;
-	gchar *token_str;
-	GetContactListCbData cb_data = { session, partner_scenario };
+	MsnCallbackState *state;
 	const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario];
 
 	purple_debug_misc("msn", "Getting Contact List.\n");
@@ -412,17 +515,16 @@
 		update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML, update_time);
 	}
 
-	token_str = g_markup_escape_text(
-		msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1);
 	body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str,
-		token_str, update_str ? update_str : "");
-	g_free(token_str);
+	                       update_str ? update_str : "");
 
-	msn_soap_message_send(session,
-		msn_soap_message_new(MSN_GET_CONTACT_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_GET_CONTACT_POST_URL, FALSE,
-		msn_get_contact_list_cb, g_memdup(&cb_data, sizeof(cb_data)));
+	state = msn_callback_state_new(session);
+	state->partner_scenario = partner_scenario;
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_GET_CONTACT_SOAP_ACTION;
+	state->post_url = MSN_GET_CONTACT_POST_URL;
+	state->cb = msn_get_contact_list_cb;
+	msn_contact_request(state);
 
 	g_free(update_str);
 	g_free(body);
@@ -525,7 +627,7 @@
 
 	for(contactNode = xmlnode_get_child(node, "Contact"); contactNode;
 				contactNode = xmlnode_get_next_twin(contactNode)) {
-		xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user;
+		xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds;
 		xmlnode *annotation;
 		MsnUser *user;
 
@@ -556,58 +658,52 @@
 			continue; /* Not adding own account as buddy to buddylist */
 		}
 
-		/* ignore non-messenger contacts */
-		if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) {
-			char *is_messenger_user = xmlnode_get_data(messenger_user);
-
-			if(is_messenger_user && !strcmp(is_messenger_user, "false")) {
-				g_free(is_messenger_user);
-				continue;
-			}
-
-			g_free(is_messenger_user);
-		}
-
 		passportName = xmlnode_get_child(contactInfo, "passportName");
 		if (passportName == NULL) {
 			xmlnode *emailsNode, *contactEmailNode, *emailNode;
 			xmlnode *messengerEnabledNode;
 			char *msnEnabled;
 
-			/*TODO: add it to the none-instant Messenger group and recognize as email Membership*/
-			/*Yahoo User?*/
+			/*TODO: add it to the non-instant Messenger group and recognize as email Membership*/
+			/* Yahoo/Federated User? */
 			emailsNode = xmlnode_get_child(contactInfo, "emails");
 			if (emailsNode == NULL) {
 				/*TODO:  need to support the Mobile type*/
 				continue;
 			}
 			for (contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail"); contactEmailNode;
-					contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){
-				if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) {
-					/* XXX: Should this be a continue instead of a break? It seems like it'd cause unpredictable results otherwise. */
-					break;
-				}
+					contactEmailNode = xmlnode_get_next_twin(contactEmailNode)) {
+				if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled")))
+					continue;
 
 				msnEnabled = xmlnode_get_data(messengerEnabledNode);
 
-				if ((emailNode = xmlnode_get_child(contactEmailNode, "email"))) {
-					g_free(passport);
-					passport = xmlnode_get_data(emailNode);
-				}
+				if (msnEnabled && !strcmp(msnEnabled, "true")) {
+					if ((emailNode = xmlnode_get_child(contactEmailNode, "email")))
+						passport = xmlnode_get_data(emailNode);
 
-				if (msnEnabled && !strcmp(msnEnabled, "true")) {
 					/*Messenger enabled, Get the Passport*/
-					purple_debug_info("msn", "AB Yahoo User %s\n", passport ? passport : "(null)");
+					purple_debug_info("msn", "AB Yahoo/Federated User %s\n", passport ? passport : "(null)");
 					g_free(msnEnabled);
 					break;
-				} else {
-					/*TODO maybe we can just ignore it in Purple?*/
-					purple_debug_info("msn", "AB Other type user\n");
 				}
 
 				g_free(msnEnabled);
 			}
 		} else {
+			xmlnode *messenger_user;
+			/* ignore non-messenger contacts */
+			if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) {
+				char *is_messenger_user = xmlnode_get_data(messenger_user);
+
+				if (is_messenger_user && !strcmp(is_messenger_user, "false")) {
+					g_free(is_messenger_user);
+					continue;
+				}
+
+				g_free(is_messenger_user);
+			}
+
 			passport = xmlnode_get_data(passportName);
 		}
 
@@ -777,10 +873,8 @@
 static void
 msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	MsnSession *session = data;
-
-	if (resp == NULL)
-		return;
+	MsnCallbackState *state = data;
+	MsnSession *session = state->session;
 
 	g_return_if_fail(session != NULL);
 
@@ -809,7 +903,7 @@
 	const char *dynamicItemLastChange)
 {
 	char *body, *update_str = NULL;
-	gchar *token_str;
+	MsnCallbackState *state;
 
 	purple_debug_misc("msn", "Getting Address Book\n");
 
@@ -819,19 +913,16 @@
 	else if (LastChanged != NULL)
 		update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, LastChanged);
 
-	token_str = g_markup_escape_text(
-		msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1);
 	body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE,
 		MsnSoapPartnerScenarioText[partner_scenario],
-		token_str,
 		update_str ? update_str : "");
-	g_free(token_str);
 
-	msn_soap_message_send(session,
-		msn_soap_message_new(MSN_GET_ADDRESS_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE,
-		msn_get_address_cb, session);
+	state = msn_callback_state_new(session);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_GET_ADDRESS_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_get_address_cb;
+	msn_contact_request(state);
 
 	g_free(update_str);
 	g_free(body);
@@ -841,105 +932,6 @@
  * Contact Operations
  ***************************************************************/
 
-static const char *
-msn_contact_operation_str(MsnCallbackAction action)
-{
-	/* Make sure this is large enough when adding more */
-	static char buf[BUF_LEN];
-	buf[0] = '\0';
-
-	if (action & MSN_ADD_BUDDY)
-		strcat(buf, "Adding Buddy,");
-	if (action & MSN_MOVE_BUDDY)
-		strcat(buf, "Moving Buddy,");
-	if (action & MSN_ACCEPTED_BUDDY)
-		strcat(buf, "Accepted Buddy,");
-	if (action & MSN_DENIED_BUDDY)
-		strcat(buf, "Denied Buddy,");
-	if (action & MSN_ADD_GROUP)
-		strcat(buf, "Adding Group,");
-	if (action & MSN_DEL_GROUP)
-		strcat(buf, "Deleting Group,");
-	if (action & MSN_RENAME_GROUP)
-		strcat(buf, "Renaming Group,");
-	if (action & MSN_UPDATE_INFO)
-		strcat(buf, "Updating Contact Info,");
-
-	return buf;
-}
-
-static gboolean msn_contact_request(MsnCallbackState *state);
-
-static void
-msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
-	gpointer data)
-{
-	MsnCallbackState *state = data;
-	xmlnode *fault;
-	char *faultcode_str;
-
-	if (resp == NULL) {
-		purple_debug_error("msn",
-		                   "Operation {%s} failed. No response received from server.\n",
-		                   msn_contact_operation_str(state->action));
-		return;
-	}
-
-	fault = xmlnode_get_child(resp->xml, "Body/Fault");
-
-	if (fault == NULL) {
-		/* No errors */
-		if (state->cb)
-			((MsnSoapCallback)state->cb)(req, resp, data);
-		msn_callback_state_free(state);
-		return;
-	}
-
-	faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode"));
-
-	if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
-		purple_debug_info("msn",
-		                  "Contact Operation {%s} failed because of bad token."
-		                  " Updating token now and retrying operation.\n",
-		                  msn_contact_operation_str(state->action));
-		/* Token has expired, so renew it, and try again later */
-		msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS,
-		                       (GSourceFunc)msn_contact_request, data);
-	}
-	else
-	{
-		if (state->cb) {
-			((MsnSoapCallback)state->cb)(req, resp, data);
-		} else {
-			/* We don't know how to respond to this faultcode, so log it */
-			char *str = xmlnode_to_str(fault, NULL);
-			purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
-			                   msn_contact_operation_str(state->action), str);
-			g_free(str);
-		}
-		msn_callback_state_free(state);
-	}
-
-	g_free(faultcode_str);
-}
-
-static gboolean
-msn_contact_request(MsnCallbackState *state)
-{
-	if (state->token == NULL)
-		state->token = xmlnode_get_child(state->body,
-			"Header/ABAuthHeader/TicketToken");
-	/* delete old & replace with new token */
-	xmlnode_free(state->token->child);
-	xmlnode_insert_data(state->token,
-		msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1);
-	msn_soap_message_send(state->session,
-		msn_soap_message_new(state->post_action, xmlnode_copy(state->body)),
-		MSN_CONTACT_SERVER, state->post_url, FALSE,
-		msn_contact_request_cb, state);
-	return FALSE;
-}
-
 static void
 msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
 	gpointer data)
--- a/libpurple/protocols/msn/contact.h	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/msn/contact.h	Sun Feb 01 18:04:38 2009 +0000
@@ -52,7 +52,7 @@
 		 "</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>"\
-			"<TicketToken>%s</TicketToken>"\
+			"<TicketToken>EMPTY</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
@@ -94,7 +94,7 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
-			"<TicketToken>%s</TicketToken>"\
+			"<TicketToken>EMPTY</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -135,7 +135,7 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
-			"<TicketToken>%s</TicketToken>"\
+			"<TicketToken>EMPTY</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -619,6 +619,15 @@
 	MSN_UPDATE_INFO     = 0x80
 } MsnCallbackAction;
 
+typedef enum
+{
+	MSN_PS_INITIAL,
+	MSN_PS_SAVE_CONTACT,
+	MSN_PS_PENDING_LIST,
+	MSN_PS_CONTACT_API,
+	MSN_PS_BLOCK_UNBLOCK
+} MsnSoapPartnerScenario;
+
 typedef struct _MsnCallbackState MsnCallbackState;
 
 struct _MsnCallbackState
@@ -636,19 +645,12 @@
 	const gchar *post_action;
 	const gchar *post_url;
 	MsnSoapCallback cb;
+	/* For msn_get_contact_list only */
+	MsnSoapPartnerScenario partner_scenario;
 };
 
 typedef enum
 {
-	MSN_PS_INITIAL,
-	MSN_PS_SAVE_CONTACT,
-	MSN_PS_PENDING_LIST,
-	MSN_PS_CONTACT_API,
-	MSN_PS_BLOCK_UNBLOCK
-} MsnSoapPartnerScenario;
-
-typedef enum
-{
 	MSN_UPDATE_DISPLAY,	/* Real display name */
 	MSN_UPDATE_ALIAS,	/* Aliased display name */
 	MSN_UPDATE_COMMENT
--- a/libpurple/protocols/msn/msg.c	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/msn/msg.c	Sun Feb 01 18:04:38 2009 +0000
@@ -23,6 +23,7 @@
  */
 #include "msn.h"
 #include "msg.h"
+#include "msnutils.h"
 
 MsnMessage *
 msn_message_new(MsnMsgType type)
@@ -804,3 +805,174 @@
 
 	g_string_free(str, TRUE);
 }
+
+/**************************************************************************
+ * Message Handlers
+ **************************************************************************/
+void
+msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+	PurpleConnection *gc;
+	const char *body;
+	char *body_str;
+	char *body_enc;
+	char *body_final;
+	size_t body_len;
+	const char *passport;
+	const char *value;
+
+	gc = cmdproc->session->account->gc;
+
+	body = msn_message_get_bin_data(msg, &body_len);
+	body_str = g_strndup(body, body_len);
+	body_enc = g_markup_escape_text(body_str, -1);
+	g_free(body_str);
+
+	passport = msg->remote_user;
+
+	if (!strcmp(passport, "messenger@microsoft.com") &&
+		strstr(body, "immediate security update"))
+	{
+		return;
+	}
+
+#if 0
+	if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL)
+	{
+		purple_debug_misc("msn", "User-Agent = '%s'\n", value);
+	}
+#endif
+
+	if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
+	{
+		char *pre, *post;
+
+		msn_parse_format(value, &pre, &post);
+
+		body_final = g_strdup_printf("%s%s%s", pre ? pre : "",
+									 body_enc ? body_enc : "", post ? post : "");
+
+		g_free(pre);
+		g_free(post);
+		g_free(body_enc);
+	}
+	else
+	{
+		body_final = body_enc;
+	}
+
+	if (cmdproc->servconn->type == MSN_SERVCONN_SB) {
+		MsnSwitchBoard *swboard = cmdproc->data;
+
+		swboard->flag |= MSN_SB_FLAG_IM;
+
+		if (swboard->current_users > 1 ||
+			((swboard->conv != NULL) &&
+			 purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
+		{
+			/* If current_users is always ok as it should then there is no need to
+			 * check if this is a chat. */
+			if (swboard->current_users <= 1)
+				purple_debug_misc("msn", "plain_msg: current_users(%d)\n",
+								swboard->current_users);
+
+			serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final,
+							 time(NULL));
+			if (swboard->conv == NULL)
+			{
+				swboard->conv = purple_find_chat(gc, swboard->chat_id);
+				swboard->flag |= MSN_SB_FLAG_IM;
+			}
+		}
+		else
+		{
+			serv_got_im(gc, passport, body_final, 0, time(NULL));
+			if (swboard->conv == NULL)
+			{
+				swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+										passport, purple_connection_get_account(gc));
+				swboard->flag |= MSN_SB_FLAG_IM;
+			}
+		}
+
+	} else {
+		serv_got_im(gc, passport, body_final, 0, time(NULL));
+	}
+
+	g_free(body_final);
+}
+
+void
+msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+	PurpleConnection *gc;
+	char *passport;
+
+	gc = cmdproc->session->account->gc;
+	passport = msg->remote_user;
+
+	if (msn_message_get_attr(msg, "TypingUser") == NULL)
+		return;
+
+	if (cmdproc->servconn->type == MSN_SERVCONN_SB) {
+		MsnSwitchBoard *swboard = cmdproc->data;
+
+		if (swboard->current_users == 1)
+		{
+			serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
+							PURPLE_TYPING);
+		}
+
+	} else {
+		serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
+						PURPLE_TYPING);
+	}
+}
+
+void
+msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+	GHashTable *body;
+	const char *id;
+	body = msn_message_get_hashtable_from_body(msg);
+
+	id = g_hash_table_lookup(body, "ID");
+
+	if (!strcmp(id, "1")) {
+		/* Nudge */
+		PurpleAccount *account;
+		const char *user;
+
+		account = cmdproc->session->account;
+		user = msg->remote_user;
+
+		if (cmdproc->servconn->type == MSN_SERVCONN_SB) {
+			MsnSwitchBoard *swboard = cmdproc->data;
+			if (swboard->current_users > 1 ||
+				((swboard->conv != NULL) &&
+				 purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
+				purple_prpl_got_attention_in_chat(account->gc, swboard->chat_id, user, MSN_NUDGE);
+
+			else
+				purple_prpl_got_attention(account->gc, user, MSN_NUDGE);
+
+		} else {
+			purple_prpl_got_attention(account->gc, user, MSN_NUDGE);
+		}
+
+	} else if (!strcmp(id, "2")) {
+		/* Wink */
+
+	} else if (!strcmp(id, "3")) {
+		/* Voiceclip */
+
+	} else if (!strcmp(id, "4")) {
+		/* Action */
+
+	} else {
+		purple_debug_warning("msn", "Got unknown datacast with ID %s.\n", id);
+	}
+
+	g_hash_table_destroy(body);
+}
+
--- a/libpurple/protocols/msn/msg.h	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/msn/msg.h	Sun Feb 01 18:04:38 2009 +0000
@@ -339,4 +339,10 @@
 
 char *msn_message_to_string(MsnMessage *msg);
 
+void msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
+void msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
+void msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
 #endif /* _MSN_MSG_H_ */
--- a/libpurple/protocols/msn/notification.c	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Sun Feb 01 18:04:38 2009 +0000
@@ -1094,8 +1094,10 @@
 		return;
 	}
 
-	serv_got_alias(gc, passport, friendly);
-	msn_user_set_friendly_name(user, friendly);
+	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);
 
 	msn_user_set_object(user, msnobj);
@@ -1220,7 +1222,7 @@
 	MsnObject *msnobj;
 	unsigned long clientid;
 	int networkid;
-	const char *state, *passport, *friendly, *old_friendly;
+	const char *state, *passport, *friendly;
 
 	session = cmdproc->session;
 	account = session->account;
@@ -1234,11 +1236,10 @@
 	user = msn_userlist_find_user(session->userlist, passport);
 	if (user == NULL) return;
 
-	old_friendly = msn_user_get_friendly_name(user);
-	if (!old_friendly || (old_friendly && (!friendly || strcmp(old_friendly, friendly))))
+	if (msn_user_set_friendly_name(user, friendly))
 	{
 		serv_got_alias(gc, passport, friendly);
-		msn_user_set_friendly_name(user, friendly);
+		msn_update_contact(session, passport, MSN_UPDATE_DISPLAY, friendly);
 	}
 
 	if (cmd->param_count == 6)
@@ -2095,6 +2096,13 @@
 	msn_table_add_msg_type(cbs_table,
 						   "application/x-msmsgssystemmessage",
 						   system_msg);
+	/* generic message handlers */
+	msn_table_add_msg_type(cbs_table, "text/plain",
+						   msn_plain_msg);
+	msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol",
+						   msn_control_msg);
+	msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast",
+						   msn_datacast_msg);
 }
 
 void
--- a/libpurple/protocols/msn/switchboard.c	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Sun Feb 01 18:04:38 2009 +0000
@@ -887,113 +887,6 @@
  * Message Handlers
  **************************************************************************/
 static void
-plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	PurpleConnection *gc;
-	MsnSwitchBoard *swboard;
-	const char *body;
-	char *body_str;
-	char *body_enc;
-	char *body_final;
-	size_t body_len;
-	const char *passport;
-	const char *value;
-
-	gc = cmdproc->session->account->gc;
-	swboard = cmdproc->data;
-
-	body = msn_message_get_bin_data(msg, &body_len);
-	body_str = g_strndup(body, body_len);
-	body_enc = g_markup_escape_text(body_str, -1);
-	g_free(body_str);
-
-	passport = msg->remote_user;
-
-	if (!strcmp(passport, "messenger@microsoft.com") &&
-		strstr(body, "immediate security update"))
-	{
-		return;
-	}
-
-#if 0
-	if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL)
-	{
-		purple_debug_misc("msn", "User-Agent = '%s'\n", value);
-	}
-#endif
-
-	if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
-	{
-		char *pre, *post;
-
-		msn_parse_format(value, &pre, &post);
-
-		body_final = g_strdup_printf("%s%s%s", pre ? pre : "",
-									 body_enc ? body_enc : "", post ? post : "");
-
-		g_free(pre);
-		g_free(post);
-		g_free(body_enc);
-	}
-	else
-	{
-		body_final = body_enc;
-	}
-
-	swboard->flag |= MSN_SB_FLAG_IM;
-
-	if (swboard->current_users > 1 ||
-		((swboard->conv != NULL) &&
-		 purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
-	{
-		/* If current_users is always ok as it should then there is no need to
-		 * check if this is a chat. */
-		if (swboard->current_users <= 1)
-			purple_debug_misc("msn", "plain_msg: current_users(%d)\n",
-							swboard->current_users);
-
-		serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final,
-						 time(NULL));
-		if (swboard->conv == NULL)
-		{
-			swboard->conv = purple_find_chat(gc, swboard->chat_id);
-			swboard->flag |= MSN_SB_FLAG_IM;
-		}
-	}
-	else
-	{
-		serv_got_im(gc, passport, body_final, 0, time(NULL));
-		if (swboard->conv == NULL)
-		{
-			swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-									passport, purple_connection_get_account(gc));
-			swboard->flag |= MSN_SB_FLAG_IM;
-		}
-	}
-
-	g_free(body_final);
-}
-
-static void
-control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	PurpleConnection *gc;
-	MsnSwitchBoard *swboard;
-	char *passport;
-
-	gc = cmdproc->session->account->gc;
-	swboard = cmdproc->data;
-	passport = msg->remote_user;
-
-	if (swboard->current_users == 1 &&
-		msn_message_get_attr(msg, "TypingUser") != NULL)
-	{
-		serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
-						PURPLE_TYPING);
-	}
-}
-
-static void
 clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
 #if 0
@@ -1012,49 +905,6 @@
 #endif
 }
 
-static void
-datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	GHashTable *body;
-	const char *id;
-	body = msn_message_get_hashtable_from_body(msg);
-
-	id = g_hash_table_lookup(body, "ID");
-
-	if (!strcmp(id, "1")) {
-		/* Nudge */
-		MsnSwitchBoard *swboard;
-		PurpleAccount *account;
-		const char *user;
-
-		swboard = cmdproc->data;
-		account = cmdproc->session->account;
-		user = msg->remote_user;
-
-		if (swboard->current_users > 1 ||
-			((swboard->conv != NULL) &&
-			 purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
-			purple_prpl_got_attention_in_chat(account->gc, swboard->chat_id, user, MSN_NUDGE);
-
-		else
-			purple_prpl_got_attention(account->gc, user, MSN_NUDGE);
-
-	} else if (!strcmp(id, "2")) {
-		/* Wink */
-
-	} else if (!strcmp(id, "3")) {
-		/* Voiceclip */
-
-	} else if (!strcmp(id, "4")) {
-		/* Action */
-
-	} else {
-		purple_debug_warning("msn", "Got unknown datacast with ID %s.\n", id);
-	}
-
-	g_hash_table_destroy(body);
-}
-
 /**************************************************************************
  * Connect stuff
  **************************************************************************/
@@ -1372,9 +1222,9 @@
 
 	/* Register the message type callbacks. */
 	msn_table_add_msg_type(cbs_table, "text/plain",
-						   plain_msg);
+						   msn_plain_msg);
 	msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol",
-						   control_msg);
+						   msn_control_msg);
 	msn_table_add_msg_type(cbs_table, "text/x-clientcaps",
 						   clientcaps_msg);
 	msn_table_add_msg_type(cbs_table, "text/x-clientinfo",
@@ -1384,9 +1234,9 @@
 	msn_table_add_msg_type(cbs_table, "text/x-mms-emoticon",
 						   msn_emoticon_msg);
 	msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon",
-	                                           msn_emoticon_msg);
+	                       msn_emoticon_msg);
 	msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast",
-						   datacast_msg);
+						   msn_datacast_msg);
 #if 0
 	msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite",
 						   msn_invite_msg);
--- a/libpurple/protocols/msn/user.c	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/msn/user.c	Sun Feb 01 18:04:38 2009 +0000
@@ -177,13 +177,18 @@
 	user->passport = g_strdup(passport);
 }
 
-void
+gboolean
 msn_user_set_friendly_name(MsnUser *user, const char *name)
 {
 	g_return_if_fail(user != NULL);
 
+	if (user->friendly_name && name && !strcmp(user->friendly_name, name))
+		return FALSE;
+
 	g_free(user->friendly_name);
 	user->friendly_name = g_strdup(name);
+
+	return TRUE;
 }
 
 void
--- a/libpurple/protocols/msn/user.h	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/msn/user.h	Sun Feb 01 18:04:38 2009 +0000
@@ -178,8 +178,10 @@
  *
  * @param user The user.
  * @param name The friendly name.
+ *
+ * @returns TRUE is name actually changed, FALSE otherwise.
  */
-void msn_user_set_friendly_name(MsnUser *user, const char *name);
+gboolean msn_user_set_friendly_name(MsnUser *user, const char *name);
 
 /**
  * Sets the buddy icon for a local user.
--- a/libpurple/protocols/silc/silc.c	Sun Feb 01 18:00:05 2009 +0000
+++ b/libpurple/protocols/silc/silc.c	Sun Feb 01 18:04:38 2009 +0000
@@ -1357,6 +1357,7 @@
 	char tmp[256];
 	SilcClientEntry client_entry;
 	SilcDList list;
+	gboolean free_list = FALSE;
 
 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick,
 						      sg->account);
@@ -1373,6 +1374,8 @@
 							im->nick, FALSE);
 		if (!clients)
 			goto err;
+
+		free_list = TRUE;
 	}
 
 	silc_dlist_start(clients);
@@ -1413,6 +1416,9 @@
 	purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
 
  out:
+	if (free_list) {
+		silc_client_list_free(client, conn, clients);
+	}
 	g_free(im->nick);
 	g_free(im->message);
 	silc_free(im);
--- a/pidgin/plugins/win32/winprefs/winprefs.c	Sun Feb 01 18:00:05 2009 +0000
+++ b/pidgin/plugins/win32/winprefs/winprefs.c	Sun Feb 01 18:04:38 2009 +0000
@@ -55,9 +55,8 @@
 static const char *PREF_DBLIST_HEIGHT = "/plugins/gtk/win32/winprefs/dblist_height";
 static const char *PREF_DBLIST_SIDE = "/plugins/gtk/win32/winprefs/dblist_side";
 static const char *PREF_BLIST_ON_TOP = "/plugins/gtk/win32/winprefs/blist_on_top";
+/* Deprecated */
 static const char *PREF_CHAT_BLINK = "/plugins/gtk/win32/winprefs/chat_blink";
-
-/* Deprecated */
 static const char *PREF_DBLIST_ON_TOP = "/plugins/gtk/win32/winprefs/dblist_on_top";
 
 static PurplePlugin *handle = NULL;
@@ -229,17 +228,6 @@
 		blist_set_ontop(FALSE);
 }
 
-static gboolean
-winpidgin_conv_chat_blink(PurpleAccount *account, const char *who, char **message,
-		PurpleConversation *conv, PurpleMessageFlags flags, void *data)
-{
-	if(purple_prefs_get_bool(PREF_CHAT_BLINK))
-		winpidgin_conv_blink(conv, flags);
-
-	return FALSE;
-}
-
-
 /*
  *  EXPORTED FUNCTIONS
  */
@@ -258,10 +246,6 @@
 	purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-created",
 		plugin, PURPLE_CALLBACK(blist_create_cb), NULL);
 
-	purple_signal_connect(pidgin_conversations_get_handle(),
-		"displaying-chat-msg", plugin, PURPLE_CALLBACK(winpidgin_conv_chat_blink),
-		NULL);
-
 	purple_signal_connect((void*)purple_get_core(), "quitting", plugin,
 		PURPLE_CALLBACK(purple_quit_cb), NULL);
 
@@ -336,11 +320,6 @@
 		_("Only when docked"), BLIST_TOP_DOCKED,
 		NULL);
 
-	/* Conversations */
-	vbox = pidgin_make_frame(ret, _("Conversations"));
-	pidgin_prefs_checkbox(_("_Flash window when chat messages are received"),
-							PREF_CHAT_BLINK, vbox);
-
 	gtk_widget_show_all(ret);
 	return ret;
 }
@@ -399,7 +378,6 @@
 	purple_prefs_add_bool(PREF_DBLIST_DOCKED, FALSE);
 	purple_prefs_add_int(PREF_DBLIST_HEIGHT, 0);
 	purple_prefs_add_int(PREF_DBLIST_SIDE, 0);
-	purple_prefs_add_bool(PREF_CHAT_BLINK, FALSE);
 
 	/* Convert old preferences */
 	if(purple_prefs_exists(PREF_DBLIST_ON_TOP)) {
@@ -413,6 +391,7 @@
 		purple_prefs_add_int(PREF_BLIST_ON_TOP, blist_top);
 	} else
 		purple_prefs_add_int(PREF_BLIST_ON_TOP, BLIST_TOP_NEVER);
+	purple_prefs_remove(PREF_CHAT_BLINK);
 }
 
 PURPLE_INIT_PLUGIN(winprefs, init_plugin, info)