changeset 25772:a34d6975c239

propagate from branch 'im.pidgin.pidgin' (head 3da4a61feea56ff40dc5fdba355a1057c675a32d) to branch 'im.pidgin.cpw.malu.xmpp.ibb_ft' (head ad767a71b308c8f96d42a30bc59997f9bd395375)
author Marcus Lundblad <ml@update.uu.se>
date Fri, 06 Feb 2009 22:48:37 +0000
parents 8f5a63aebdb6 (diff) f4b9d56dba80 (current diff)
children da4e18e318e7
files libpurple/protocols/jabber/iq.c libpurple/protocols/jabber/si.c libpurple/protocols/jabber/si.h
diffstat 40 files changed, 636 insertions(+), 520 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Thu Jan 29 20:35:42 2009 +0000
+++ b/COPYRIGHT	Fri Feb 06 22:48:37 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	Thu Jan 29 20:35:42 2009 +0000
+++ b/ChangeLog	Fri Feb 06 22:48:37 2009 +0000
@@ -2,17 +2,29 @@
 
 version 2.5.5 (??/??/????):
 	libpurple:
+	* Fix a crash when removing an account with an unknown protocol id.
+	* 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 a memory leak in SILC. (Luke Petre)
+
+	ICQ:
+	* Fix retrieval of status messages from users of ICQ 6.x, Miranda, and
+	  other libpurple clients. (Daniel Ljungborg)
+	* Change client ID to match ICQ Basic 14.34.3096.  This fixes publishing
+	  of buddy icons and available messages.
+	* Properly publish status messages for statuses other than Available.
+	  ICQ 6.x users can now see these status messages. (Daniel Ljungborg)
+
+	MSN:
 	* 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)
+	* 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.API	Thu Jan 29 20:35:42 2009 +0000
+++ b/ChangeLog.API	Fri Feb 06 22:48:37 2009 +0000
@@ -6,6 +6,8 @@
 		* purple_status_type_new now defaults "saveable" to TRUE.
 		  This was necessary in order to maintain the current behavior
 		  while fixing non-saveable statuses not to be saved.
+		* xmlnode_get_prefix, xmlnode_to_str and xmlnode_to_formatted_str
+		  now all take a const xmlnode* instead of an xmlnode*
 
 version 2.5.4 (01/12/2009):
 	perl:
--- a/ChangeLog.win32	Thu Jan 29 20:35:42 2009 +0000
+++ b/ChangeLog.win32	Fri Feb 06 22:48:37 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/finch/finch.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/finch/finch.c	Fri Feb 06 22:48:37 2009 +0000
@@ -210,7 +210,7 @@
 		text = g_strdup_printf(_("%s\n"
 		       "Usage: %s [OPTION]...\n\n"
 		       "  -c, --config=DIR    use DIR for config files\n"
-		       "  -d, --debug         print debugging messages to stdout\n"
+		       "  -d, --debug         print debugging messages to stderr\n"
 		       "  -h, --help          display this help and exit\n"
 		       "  -n, --nologin       don't automatically login\n"
 		       "  -v, --version       display the current version and exit\n"), DISPLAY_VERSION, name);
--- a/finch/libgnt/gntkeys.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/finch/libgnt/gntkeys.c	Fri Feb 06 22:48:37 2009 +0000
@@ -163,7 +163,8 @@
 			(*(text + 2) >= 'A' && *(text + 2) <= 'D')) {
 		/* Apparently this is necessary for urxvt and screen and xterm */
 		if (strstr(term, "screen") == term || strcmp(term, "rxvt-unicode") == 0 ||
-				strstr(term, "xterm") == term)
+				strstr(term, "xterm") == term ||
+				strstr(term, "vt100") == term)
 			*(text + 1) = 'O';
 	} else if (*(unsigned char*)text == 195) {
 		if (*(text + 2) == 0 && strstr(term, "xterm") == term) {
--- a/libpurple/dbus-analyze-functions.py	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/dbus-analyze-functions.py	Fri Feb 06 22:48:37 2009 +0000
@@ -497,7 +497,7 @@
         # This is a total hack, but self.call is set up before the parameters
         #  are processed, so we can't tell it to pass a parameter by reference.
         self.call = "%s(%s)" % (self.function.name,
-                                ", ".join(param.name if param.name != "len" else "&len" for param in self.params))
+                                ", ".join([(param.name, "&len")[param.name == "len"] for param in self.params]))
 
         self.cdecls.append("\tgconstpointer %s;" % name)
         self.ccode.append("\t%s = %s;" % (name, self.call))
--- a/libpurple/network.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/network.c	Fri Feb 06 22:48:37 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/jabber/data.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/data.c	Fri Feb 06 22:48:37 2009 +0000
@@ -200,11 +200,9 @@
 }
 
 void
-jabber_data_parse(JabberStream *js, xmlnode *packet)
+jabber_data_parse(JabberStream *js, xmlnode *data_node, const char *who, const char *id)
 {
 	JabberIq *result = NULL;
-	const char *who = xmlnode_get_attrib(packet, "from");
-	xmlnode *data_node = xmlnode_get_child(packet, "data");
 	const JabberData *data =
 		jabber_data_find_local_by_cid(xmlnode_get_attrib(data_node, "cid"));
 
@@ -213,12 +211,12 @@
 
 		result = jabber_iq_new(js, JABBER_IQ_ERROR);
 		xmlnode_set_attrib(result->node, "to", who);
-		xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+		xmlnode_set_attrib(result->node, "id", id);
 		xmlnode_insert_child(result->node, item_not_found);
 	} else {
 		result = jabber_iq_new(js, JABBER_IQ_RESULT);
 		xmlnode_set_attrib(result->node, "to", who);
-		xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+		xmlnode_set_attrib(result->node, "id", id);
 		xmlnode_insert_child(result->node,
 							 jabber_data_get_xml_definition(data));
 	}
--- a/libpurple/protocols/jabber/data.h	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/data.h	Fri Feb 06 22:48:37 2009 +0000
@@ -65,7 +65,8 @@
 void jabber_data_associate_remote(JabberData *data);
 
 /* handles iq requests */
-void jabber_data_parse(JabberStream *js, xmlnode *packet);
+void jabber_data_parse(JabberStream *js, xmlnode *data_node,
+                       const char *who, const char *id);
 
 void jabber_data_init(void);
 void jabber_data_uninit(void);
--- a/libpurple/protocols/jabber/google.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Fri Feb 06 22:48:37 2009 +0000
@@ -144,9 +144,8 @@
 }
 
 void
-jabber_gmail_poke(JabberStream *js, xmlnode *packet)
+jabber_gmail_poke(JabberStream *js, const char *type)
 {
-	const char *type;
 	xmlnode *query;
 	JabberIq *iq;
 
@@ -154,11 +153,8 @@
 	if (!purple_account_get_check_mail(js->gc->account))
 		return;
 
-	type = xmlnode_get_attrib(packet, "type");
-
-
 	/* Is this an initial incoming mail notification? If so, send a request for more info */
-	if (strcmp(type, "set") || !xmlnode_get_child(packet, "new-mail"))
+	if (strcmp(type, "set"))
 		return;
 
 	purple_debug(PURPLE_DEBUG_MISC, "jabber",
--- a/libpurple/protocols/jabber/google.h	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/google.h	Fri Feb 06 22:48:37 2009 +0000
@@ -27,7 +27,7 @@
 #include "jabber.h"
 
 void jabber_gmail_init(JabberStream *js);
-void jabber_gmail_poke(JabberStream *js, xmlnode *node);
+void jabber_gmail_poke(JabberStream *js, const char *type);
 
 void jabber_google_roster_init(JabberStream *js);
 void jabber_google_roster_outgoing(JabberStream *js, xmlnode *query, xmlnode *item);
--- a/libpurple/protocols/jabber/iq.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/iq.c	Fri Feb 06 22:48:37 2009 +0000
@@ -310,7 +310,7 @@
 void jabber_iq_parse(JabberStream *js, xmlnode *packet)
 {
 	JabberCallbackData *jcd;
-	xmlnode *query, *error, *x;
+	xmlnode *child, *query, *error, *x;
 	const char *xmlns;
 	const char *type, *id, *from;
 	JabberIqHandler *jih;
@@ -372,25 +372,25 @@
 		}
 	}
 
-	if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) {
-		jabber_si_parse(js, packet);
+	if ((child = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
+		jabber_si_parse(js, child, from, id);
 		return;
 	}
 
-	if(xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) {
-		jabber_gmail_poke(js, packet);
+	if (xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) {
+		jabber_gmail_poke(js, type);
 		return;
 	}
 
 	purple_debug_info("jabber", "jabber_iq_parse\n");
 
-	if(xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) {
-		jabber_ping_parse(js, packet);
+	if (xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) {
+		jabber_ping_parse(js, from, id);
 		return;
 	}
 
-	if (xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE)) {
-		jabber_data_parse(js, packet);
+	if ((child = xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE))) {
+		jabber_data_parse(js, child, from, id);
 		return;
 	}
 
--- a/libpurple/protocols/jabber/jabber.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Fri Feb 06 22:48:37 2009 +0000
@@ -1864,7 +1864,7 @@
 
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
 			jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE),
-			NULL, FALSE, TRUE, FALSE,
+			NULL, TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, type);
--- a/libpurple/protocols/jabber/ping.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/ping.c	Fri Feb 06 22:48:37 2009 +0000
@@ -30,7 +30,7 @@
 #include "iq.h"
 
 void
-jabber_ping_parse(JabberStream *js, xmlnode *packet)
+jabber_ping_parse(JabberStream *js, const char *from, const char *id)
 {
 	JabberIq *iq;
 
@@ -38,9 +38,9 @@
 
 	iq = jabber_iq_new(js, JABBER_IQ_RESULT);
 
-	xmlnode_set_attrib(iq->node, "to", xmlnode_get_attrib(packet, "from") );
+	xmlnode_set_attrib(iq->node, "to", from);
 
-	jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id"));
+	jabber_iq_set_id(iq, id);
 
 	jabber_iq_send(iq);
 }
--- a/libpurple/protocols/jabber/ping.h	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/ping.h	Fri Feb 06 22:48:37 2009 +0000
@@ -26,7 +26,8 @@
 #include "conversation.h"
 
 void jabber_ping_parse(JabberStream *js,
-						xmlnode *packet);
+                       const char *from,
+                       const char *id);
 
 
 gboolean jabber_ping_jid(PurpleConversation *conv, const char *jid);
--- a/libpurple/protocols/jabber/si.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/si.c	Fri Feb 06 22:48:37 2009 +0000
@@ -1584,17 +1584,14 @@
 		purple_xfer_request(xfer);
 }
 
-void jabber_si_parse(JabberStream *js, xmlnode *packet)
+void jabber_si_parse(JabberStream *js, xmlnode *si, const char *from, const char *id)
 {
 	JabberSIXfer *jsx;
 	PurpleXfer *xfer;
-	xmlnode *si, *file, *feature, *x, *field, *option, *value;
-	const char *stream_id, *filename, *filesize_c, *profile, *from;
+	xmlnode *file, *feature, *x, *field, *option, *value;
+	const char *stream_id, *filename, *filesize_c, *profile;
 	size_t filesize = 0;
 
-	if(!(si = xmlnode_get_child(packet, "si")))
-		return;
-
 	if(!(profile = xmlnode_get_attrib(si, "profile")) ||
 			strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer"))
 		return;
@@ -1617,7 +1614,7 @@
 	if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
 		return;
 
-	if(!(from = xmlnode_get_attrib(packet, "from")))
+	if(!from)
 		return;
 
 	/* if they've already sent us this file transfer with the same damn id
@@ -1658,7 +1655,7 @@
 
 	jsx->js = js;
 	jsx->stream_id = g_strdup(stream_id);
-	jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));
+	jsx->iq_id = g_strdup(id);
 
 	xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from);
 	if (xfer)
--- a/libpurple/protocols/jabber/si.h	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/jabber/si.h	Fri Feb 06 22:48:37 2009 +0000
@@ -27,7 +27,7 @@
 #include "jabber.h"
 
 void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet);
-void jabber_si_parse(JabberStream *js, xmlnode *packet);
+void jabber_si_parse(JabberStream *js, xmlnode *si, const char *from, const char *id);
 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who);
 void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file);
 void jabber_si_init(void);
--- a/libpurple/protocols/msn/contact.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Fri Feb 06 22:48:37 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	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/contact.h	Fri Feb 06 22:48:37 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	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/msg.c	Fri Feb 06 22:48:37 2009 +0000
@@ -23,6 +23,7 @@
  */
 #include "msn.h"
 #include "msg.h"
+#include "msnutils.h"
 
 MsnMessage *
 msn_message_new(MsnMsgType type)
@@ -79,7 +80,7 @@
 	msg->ref_count++;
 
 #ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message ref (%p)[%d]\n", msg, msg->ref_count);
+	purple_debug_info("msn", "message ref (%p)[%" G_GSIZE_FORMAT "]\n", msg, msg->ref_count);
 #endif
 
 	return msg;
@@ -94,7 +95,7 @@
 	msg->ref_count--;
 
 #ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message unref (%p)[%d]\n", msg, msg->ref_count);
+	purple_debug_info("msn", "message unref (%p)[%" G_GSIZE_FORMAT "]\n", msg, msg->ref_count);
 #endif
 
 	if (msg->ref_count == 0)
@@ -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	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/msg.h	Fri Feb 06 22:48:37 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/msn.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/msn.c	Fri Feb 06 22:48:37 2009 +0000
@@ -853,11 +853,11 @@
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
-			NULL, NULL, FALSE, TRUE, FALSE);
+			NULL, NULL, TRUE, TRUE, FALSE);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
-			NULL, NULL, FALSE, TRUE, FALSE);
+			NULL, NULL, TRUE, TRUE, FALSE);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
@@ -1176,6 +1176,7 @@
 	MsnMessage *msg;
 	char *msgformat;
 	char *msgtext;
+	size_t msglen;
 	const char *username;
 
 	purple_debug_info("msn", "send IM {%s} to %s\n", message, who);
@@ -1203,13 +1204,23 @@
 	}
 
 	msn_import_html(message, &msgformat, &msgtext);
+	msglen = strlen(msgtext);
+	if (msglen == 0) {
+		/* Stuff like <hr> will be ignored. Don't send an empty message
+		   if that's all there is. */
+		g_free(msgtext);
+		g_free(msgformat);
+
+		return 0;
+	}
+
 	if (msn_user_is_online(account, who) ||
 		msn_user_is_yahoo(account, who) ||
 		swboard != NULL) {
 		/*User online or have a swboard open because it's invisible
 		 * and sent us a message,then send Online Instant Message*/
  
-		if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564)
+		if (msglen + strlen(msgformat) + strlen(VERSION) > 1564)
 		{
 			g_free(msgformat);
 			g_free(msgtext);
--- a/libpurple/protocols/msn/notification.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Fri Feb 06 22:48:37 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/soap.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/soap.c	Fri Feb 06 22:48:37 2009 +0000
@@ -342,12 +342,14 @@
 	}
 
 	if (fault || body) {
-		MsnSoapRequest *request = conn->current_request;
-		conn->current_request = NULL;
-		request->cb(request->message, response,
-			request->cb_data);
+		if (conn->current_request) {
+			MsnSoapRequest *request = conn->current_request;
+			conn->current_request = NULL;
+			request->cb(request->message, response,
+				request->cb_data);
+			msn_soap_request_destroy(request, FALSE);
+		}
 		msn_soap_message_destroy(response);
-		msn_soap_request_destroy(request, FALSE);
 	}
 
 	return TRUE;
--- a/libpurple/protocols/msn/switchboard.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Fri Feb 06 22:48:37 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	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/user.c	Fri Feb 06 22:48:37 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);
+	g_return_val_if_fail(user != NULL, FALSE);
+
+	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	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/msn/user.h	Fri Feb 06 22:48:37 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/myspace/myspace.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Fri Feb 06 22:48:37 2009 +0000
@@ -1146,7 +1146,11 @@
 			break;
 
 		case MSIM_CONTACT_LIST_INITIAL_FRIENDS:
-			/* Nothing */
+			/* The session is now set up, ready to be connected. This emits the
+			 * signedOn signal, so clients can now do anything with msimprpl, and
+			 * we're ready for it (session key, userid, username all setup). */
+			purple_connection_update_progress(session->gc, _("Connected"), 3, 4);
+			purple_connection_set_state(session->gc, PURPLE_CONNECTED);
 			break;
 	}
 
@@ -1185,12 +1189,6 @@
 	/* Set display name to username (otherwise will show email address) */
 	purple_connection_set_display_name(session->gc, session->username);
 
-	/* The session is now set up, ready to be connected. This emits the
-	 * signedOn signal, so clients can now do anything with msimprpl, and
-	 * we're ready for it (session key, userid, username all setup). */
-	purple_connection_update_progress(session->gc, _("Connected"), 3, 4);
-	purple_connection_set_state(session->gc, PURPLE_CONNECTED);
-
 	body = msim_msg_new(
 			"UserID", MSIM_TYPE_INTEGER, session->userid,
 			NULL);
--- a/libpurple/protocols/novell/novell.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/novell/novell.c	Fri Feb 06 22:48:37 2009 +0000
@@ -2976,7 +2976,7 @@
 										   NULL, TRUE, TRUE, FALSE);
 	status_types = g_list_append(status_types, type);
 
-	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
 	status_types = g_list_append(status_types, type);
 
 	return status_types;
--- a/libpurple/protocols/oscar/oscar.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri Feb 06 22:48:37 2009 +0000
@@ -4782,7 +4782,7 @@
 		if (status_html != NULL)
 		{
 			status_text = purple_markup_strip_html(status_html);
-			/* If the status_text is longer than 60 character then truncate it */
+			/* If the status_text is longer than 251 characters then truncate it */
 			if (strlen(status_text) > MAXAVAILMSGLEN)
 			{
 				char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]);
@@ -4799,9 +4799,28 @@
 	}
 	else
 	{
+		char *status_text = NULL;
+		
 		htmlaway = purple_status_get_attr_string(status, "message");
 		if ((htmlaway == NULL) || (*htmlaway == '\0'))
 			htmlaway = purple_status_type_get_name(status_type);
+		
+		/* ICQ 6.x seems to use an available message for all statuses so set one */
+		if (od->icq) 
+		{
+			status_text = purple_markup_strip_html(htmlaway);
+			/* If the status_text is longer than 251 characters then truncate it */
+			if (strlen(status_text) > MAXAVAILMSGLEN)
+			{
+				char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]);
+				strcpy(tmp, "...");
+			}
+			aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, NULL);
+		}
+		g_free(status_text);
+
+		/* Set a proper away message for icq too so that they work for old third party clients */
+		
 		away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding);
 
 		if (awaylen > od->rights.maxawaymsglen)
--- a/libpurple/protocols/oscar/oscar.h	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Fri Feb 06 22:48:37 2009 +0000
@@ -286,6 +286,15 @@
 	"us", "en", \
 }
 
+#define CLIENTINFO_ICQBASIC_14_34_3096 { \
+	"ICQBasic", \
+	0x010a, \
+	0x0014, 0x0034, \
+	0x0000, 0x0c18, \
+	0x0000043d, \
+	"us", "en", \
+}
+
 #define CLIENTINFO_NETSCAPE_7_0_1 { \
 	"Netscape 2000 an approved user of AOL Instant Messenger (SM)", \
 	0x1d0d, \
@@ -312,14 +321,14 @@
 #define CLIENTINFO_PURPLE_ICQ { \
 	"Purple/" VERSION, \
 	0x010a, \
-	0x0006, 0x0000, \
-	0x0000, 0x17ab, \
-	0x00007535, \
+	0x0014, 0x0034, \
+	0x0000, 0x0c18, \
+	0x0000043d, \
 	"us", "en", \
 }
 
 #define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036
-#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3000
+#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3096
 
 typedef enum
 {
--- a/libpurple/protocols/qq/qq.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/qq/qq.c	Fri Feb 06 22:48:37 2009 +0000
@@ -406,15 +406,15 @@
 	GList *types = NULL;
 
 	status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
-			"available", _("Available"), FALSE, TRUE, FALSE);
+			"available", _("Available"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
-			"away", _("Away"), FALSE, TRUE, FALSE);
+			"away", _("Away"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
-			"invisible", _("Invisible"), FALSE, TRUE, FALSE);
+			"invisible", _("Invisible"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
@@ -422,7 +422,7 @@
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
-			"offline", _("Offline"), FALSE, TRUE, FALSE);
+			"offline", _("Offline"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
--- a/libpurple/protocols/silc/silc.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/silc/silc.c	Fri Feb 06 22:48:37 2009 +0000
@@ -48,19 +48,19 @@
 	PurpleStatusType *type;
 	GList *types = NULL;
 
-	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
 
 	return types;
@@ -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/libpurple/protocols/silc10/silc.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/protocols/silc10/silc.c	Fri Feb 06 22:48:37 2009 +0000
@@ -39,19 +39,19 @@
 	PurpleStatusType *type;
 	GList *types = NULL;
 
-	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
-	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, FALSE, TRUE, FALSE);
+	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE);
 	types = g_list_append(types, type);
 
 	return types;
--- a/libpurple/xmlnode.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/xmlnode.c	Fri Feb 06 22:48:37 2009 +0000
@@ -303,7 +303,7 @@
 	node->prefix = g_strdup(prefix);
 }
 
-const char *xmlnode_get_prefix(xmlnode *node)
+const char *xmlnode_get_prefix(const xmlnode *node)
 {
 	g_return_val_if_fail(node != NULL, NULL);
 	return node->prefix;
@@ -443,11 +443,11 @@
 }
 
 static char *
-xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth)
+xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int depth)
 {
 	GString *text = g_string_new("");
 	const char *prefix;
-	xmlnode *c;
+	const xmlnode *c;
 	char *node_name, *esc, *esc2, *tab = NULL;
 	gboolean need_end = FALSE, pretty = formatting;
 
@@ -537,13 +537,13 @@
 }
 
 char *
-xmlnode_to_str(xmlnode *node, int *len)
+xmlnode_to_str(const xmlnode *node, int *len)
 {
 	return xmlnode_to_str_helper(node, len, FALSE, 0);
 }
 
 char *
-xmlnode_to_formatted_str(xmlnode *node, int *len)
+xmlnode_to_formatted_str(const xmlnode *node, int *len)
 {
 	char *xml, *xml_with_declaration;
 
--- a/libpurple/xmlnode.h	Thu Jan 29 20:35:42 2009 +0000
+++ b/libpurple/xmlnode.h	Fri Feb 06 22:48:37 2009 +0000
@@ -243,7 +243,7 @@
  * @param node The node to get the prefix from
  * @return The prefix of this node
  */
-const char *xmlnode_get_prefix(xmlnode *node);
+const char *xmlnode_get_prefix(const xmlnode *node);
 
 /**
  * Returns the node in a string of xml.
@@ -254,7 +254,7 @@
  * @return The node represented as a string.  You must
  *         g_free this string when finished using it.
  */
-char *xmlnode_to_str(xmlnode *node, int *len);
+char *xmlnode_to_str(const xmlnode *node, int *len);
 
 /**
  * Returns the node in a string of human readable xml.
@@ -266,7 +266,7 @@
  *         tab and new line characters.  You must
  *         g_free this string when finished using it.
  */
-char *xmlnode_to_formatted_str(xmlnode *node, int *len);
+char *xmlnode_to_formatted_str(const xmlnode *node, int *len);
 
 /**
  * Creates a node from a string of XML.  Calling this on the
--- a/pidgin/gtkdialogs.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/pidgin/gtkdialogs.c	Fri Feb 06 22:48:37 2009 +0000
@@ -198,7 +198,7 @@
 	{N_("Macedonian"),          "mk", "Arangel Angov ", "arangel@linux.net.mk"},
 	{N_("Macedonian"),          "mk", "Ivana Kirkovska", "ivana.kirkovska@gmail.com"},
 	{N_("Macedonian"),          "mk", "Jovan Naumovski", "jovan@lugola.net"},
-	{"Mongolian",               "mn", "gooyo", NULL},
+	{N_("Mongolian"),           "mn", "gooyo", NULL},
 	{N_("Bokmål Norwegian"),    "nb", "Hans Fredrik Nordhaug", "hans@nordhaug.priv.no"},
 	{N_("Nepali"),              "ne", "Shyam Krishna Bal", "shyamkrishna_bal@yahoo.com"},
 	{N_("Dutch, Flemish"),      "nl", "Vincent van Adrighem", "V.vanAdrighem@dirck.mine.nu"},
--- a/pidgin/plugins/win32/winprefs/winprefs.c	Thu Jan 29 20:35:42 2009 +0000
+++ b/pidgin/plugins/win32/winprefs/winprefs.c	Fri Feb 06 22:48:37 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)
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Thu Jan 29 20:35:42 2009 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Fri Feb 06 22:48:37 2009 +0000
@@ -1247,6 +1247,9 @@
   Push $R0
   Push $R1
   Push $R2
+  Push $R3 ; This is only used for the Parameters throughout the function
+
+  ${GetParameters} $R3
 
   IntOp $R1 0 + 0
   retry_runcheck:
@@ -1258,7 +1261,14 @@
   IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume
     MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck
     Abort
+
+  ; Allow installer to run even if pidgin is running via "/NOPIDGINRUNCHECK=1"
+  ; This is useful for testing
+  ClearErrors
+  ${GetOptions} "$R3" "/NOPIDGINRUNCHECK=" $R1
+  IfErrors 0 +2
   Call RunCheck
+
   StrCpy $name "Pidgin ${PIDGIN_VERSION}"
   StrCpy $SPELLCHECK_SEL ""
 
@@ -1312,16 +1322,13 @@
   SetShellVarContext "current"
 
   StrCpy $ISSILENT "/S"
-
-  ; GTK installer has two silent states.. one with Message boxes, one without
+  ; GTK installer has two silent states - one with Message boxes, one without
   ; If pidgin installer was run silently, we want to supress gtk installer msg boxes.
-  IfSilent 0 set_gtk_normal
-      StrCpy $ISSILENT "/NOUI"
-  set_gtk_normal:
+  IfSilent 0 +2
+    StrCpy $ISSILENT "/NOUI"
 
-  ${GetParameters} $R0
   ClearErrors
-  ${GetOptions} "$R0" "/L=" $R1
+  ${GetOptions} "$R3" "/L=" $R1
   IfErrors +3
   StrCpy $LANGUAGE $R1
   Goto skip_lang
@@ -1332,7 +1339,7 @@
     skip_lang:
 
   ClearErrors
-  ${GetOptions} "$R0" "/DS=" $R1
+  ${GetOptions} "$R3" "/DS=" $R1
   IfErrors +8
   SectionGetFlags ${SecDesktopShortcut} $R2
   StrCmp "1" $R1 0 +2
@@ -1343,7 +1350,7 @@
   SectionSetFlags ${SecDesktopShortcut} $R2
 
   ClearErrors
-  ${GetOptions} "$R0" "/SMS=" $R1
+  ${GetOptions} "$R3" "/SMS=" $R1
   IfErrors +8
   SectionGetFlags ${SecStartMenuShortcut} $R2
   StrCmp "1" $R1 0 +2
@@ -1380,6 +1387,7 @@
 
   instdir_done:
 ;LogSet on
+  Pop $R3
   Pop $R2
   Pop $R1
   Pop $R0
@@ -1694,6 +1702,7 @@
   Push $R1
   Push $R2
   Push $R3
+  Push $R4
 
   check:
   ClearErrors
@@ -1714,7 +1723,12 @@
   StrCmp $R3 "success" +3
     StrCpy $R0 $R3
     Goto done
+  ; Use a specific temporary $OUTDIR for each dictionary because the installer doesn't clean up after itself
+  StrCpy $R4 "$OUTDIR"
+  SetOutPath "$TEMP\aspell_dict-$R0"
   ExecWait '"$R1"'
+  SetOutPath "$R4"
+  RMDir /r "$TEMP\aspell_dict-$R0"
   Delete $R1
   Goto check ; Check that it is now installed correctly
 
@@ -1723,6 +1737,7 @@
     StrCpy $R0 ''
 
   done:
+  Pop $R4
   Pop $R3
   Pop $R2
   Pop $R1