changeset 25822:ca8934dafd28

propagate from branch 'im.pidgin.pidgin' (head dbdaecf659b7d74cd50fad7c3d9bb621d9e8220e) to branch 'im.pidgin.cpw.darkrain42.xmpp.avatars' (head 640e5a85cacdc9f93a93761279c3afecd3986f1f)
author Paul Aurich <paul@darkrain42.org>
date Fri, 30 Jan 2009 18:21:16 +0000
parents 5313cf16e1e2 (current diff) 75f72178e361 (diff)
children 591846d6a16c
files libpurple/protocols/jabber/buddy.c
diffstat 46 files changed, 745 insertions(+), 425 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Mon Jan 26 06:04:01 2009 +0000
+++ b/AUTHORS	Fri Jan 30 18:21:16 2009 +0000
@@ -36,7 +36,6 @@
 Crazy Patch Writers:
 -------------------
 Paul Aurich
-Felipe 'shx' Contreras
 Marcus 'malu' Lundblad
 Dennis 'EvilDennisR' Ristuccia
 Peter 'Fmoo' Ruibal
@@ -57,6 +56,7 @@
 
 Retired Crazy Patch Writers:
 ---------------------------
+Felipe 'shx' Contreras
 Decklin Foster
 Peter 'Bleeter' Lawler
 Robert 'Robot101' McQueen
--- a/COPYRIGHT	Mon Jan 26 06:04:01 2009 +0000
+++ b/COPYRIGHT	Fri Jan 30 18:21:16 2009 +0000
@@ -211,6 +211,7 @@
 Jaromír Karmazín
 John Kelm
 Jochen Kemnade
+Yann Kerherve
 Akuke Kok
 Kir Kolyshkin
 Konstantin Korikov
@@ -443,6 +444,7 @@
 Todd Troxell
 Brad Turcotte
 Kyle Turman
+Jon Turney
 Junichi Uekawa
 Igor Vlasenko
 István Váradi
@@ -479,6 +481,7 @@
 Dan Winship
 Michal Witkowski
 Scott Wolchok
+Rogier Wolff
 The Written Word, Inc.
 Kevin Wu Won
 Pui Lam Wong
--- a/ChangeLog	Mon Jan 26 06:04:01 2009 +0000
+++ b/ChangeLog	Fri Jan 30 18:21:16 2009 +0000
@@ -2,9 +2,17 @@
 
 version 2.5.5 (??/??/????):
 	libpurple:
-	* Fix transfer of buddy icons, custom smileys and files from the
-	latest WLM 9 official client. (Thomas Gibson-Robinson)
+	* 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)
 
 	Finch:
 	* Allow rebinding keys to change the focused widget (details in the
--- a/doc/finch.1.in	Mon Jan 26 06:04:01 2009 +0000
+++ b/doc/finch.1.in	Fri Jan 30 18:21:16 2009 +0000
@@ -543,23 +543,26 @@
 \fIhttp://developer.pidgin.im/query?status=new&status=assigned&status=reopened&component=finch+%28gnt%2Fncurses%29&order=priority\fR
 
 Before sending a bug report, please verify that you have the latest
-version of \fBfinch\fR and \fBlibpurple\fR.  Many bugs (major and minor) are
+version of \fBfinch\fR and libpurple.  Many bugs (major and minor) are
 fixed at each release, and if yours is out of date, the problem may already have
 been solved.
 
-
 .SH PATCHES
 If you fix a bug in \fBfinch\fR (or otherwise enhance it), please submit a
-patch (using \fImtn diff > my.diff\fR against the latest version from the
-Monotone repository) at
-.br
-\fIhttp://developer.pidgin.im/newticket\fR
+patch (using \fBmtn diff > my.diff\fR against the latest version from the
+Monotone repository) at \fIhttp://developer.pidgin.im/simpleticket\fR
 
-You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR to
-discuss development.
+You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR
+to discuss development.
 
 .SH SEE ALSO
 \fIhttp://pidgin.im/\fR
+.br
+\fIhttp://developer.pidgin.im/\fR
+.br
+\fBpurple-remote\fR(1)
+.br
+\fBpidgin\fR(1)
 
 .SH LICENSE
 This program is free software; you can redistribute it and/or modify
--- a/doc/pidgin.1.in	Mon Jan 26 06:04:01 2009 +0000
+++ b/doc/pidgin.1.in	Fri Jan 30 18:21:16 2009 +0000
@@ -34,6 +34,9 @@
 many common features found in other clients, as well as many unique features.
 Pidgin is not endorsed by or affiliated with America Online, ICQ, Microsoft, or
 Yahoo.
+.PP
+Pidgin can be extended by plugins written in multiple programming languages and
+controlled through DBus or \fBpurple-remote\fR.
 
 .SH OPTIONS
 The following options are provided by Pidgin using the standard GNU
@@ -556,20 +559,28 @@
 .SH BUGS
 The bug tracker can be reached by visiting \fIhttp://developer.pidgin.im/query\fR
 
-.SH PATCHES
-If you fix a bug in Pidgin (or otherwise enhance it), please submit a
-patch (using \fImtn diff > my.diff\fR against the latest version from the
-Monotone repository) at \fIhttp://developer.pidgin.im/simpleticket\fR
-
 Before sending a bug report, please verify that you have the latest
 version of Pidgin.  Many bugs (major and minor) are fixed
 at each release, and if yours is out of date, the problem may already
 have been solved.
 
+.SH PATCHES
+If you fix a bug in Pidgin (or otherwise enhance it), please submit a
+patch (using \fBmtn diff > my.diff\fR against the latest version from the
+Monotone repository) at \fIhttp://developer.pidgin.im/simpleticket\fR
+
+You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR
+to discuss development.
+
+
 .SH SEE ALSO
 \fIhttp://pidgin.im/\fR
 .br
 \fIhttp://developer.pidgin.im/\fR
+.br
+\fBpurple-remote\fR(1)
+.br
+\fBfinch\fR(1)
 
 .SH LICENSE
 This program is free software; you can redistribute it and/or modify
@@ -643,8 +654,6 @@
 .br
   Paul Aurich
 .br
-  Felipe 'shx' Contreras
-.br
   Marcus 'malu' Lundblad
 .br
   Dennis 'EvilDennisR' Ristuccia
@@ -687,6 +696,8 @@
 
 Our retired crazy patch writers include:
 .br
+  Felipe 'shx' Contreras
+.br
   Decklin Foster
 .br
   Peter 'Bleeter' Lawler
--- a/finch/libgnt/gntcolors.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/finch/libgnt/gntcolors.c	Fri Jan 30 18:21:16 2009 +0000
@@ -208,7 +208,7 @@
 				key = g_ascii_strdown(key, -1);
 				color = gnt_colors_get_color(key);
 				g_free(key);
-				if (color == -1)
+				if (color == -EINVAL)
 					continue;
 
 				init_color(color, r, g, b);
@@ -251,7 +251,7 @@
 			int bg = gnt_colors_get_color(bgc);
 			g_free(fgc);
 			g_free(bgc);
-			if (fg == -1 || bg == -1)
+			if (fg == -EINVAL || bg == -EINVAL)
 				continue;
 
 			key = g_ascii_strdown(key, -1);
--- a/libpurple/certificate.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/certificate.c	Fri Jan 30 18:21:16 2009 +0000
@@ -1546,31 +1546,11 @@
 void
 purple_certificate_uninit(void)
 {
-	GList *full_list, *l;
-
-	/* Unregister all Schemes */
-	full_list = g_list_copy(cert_schemes); /* Make a working copy */
-	for (l = full_list; l; l = l->next) {
-		purple_certificate_unregister_scheme(
-			(PurpleCertificateScheme *) l->data );
-	}
-	g_list_free(full_list);
-
 	/* Unregister all Verifiers */
-	full_list = g_list_copy(cert_verifiers); /* Make a working copy */
-	for (l = full_list; l; l = l->next) {
-		purple_certificate_unregister_verifier(
-			(PurpleCertificateVerifier *) l->data );
-	}
-	g_list_free(full_list);
+	g_list_foreach(cert_verifiers, (GFunc)purple_certificate_unregister_verifier, NULL);
 
 	/* Unregister all Pools */
-	full_list = g_list_copy(cert_pools); /* Make a working copy */
-	for (l = full_list; l; l = l->next) {
-		purple_certificate_unregister_pool(
-			(PurpleCertificatePool *) l->data );
-	}
-	g_list_free(full_list);
+	g_list_foreach(cert_pools, (GFunc)purple_certificate_unregister_pool, NULL);
 }
 
 gpointer
--- a/libpurple/connection.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/connection.c	Fri Jan 30 18:21:16 2009 +0000
@@ -545,7 +545,7 @@
 	}
 
 	if (description == NULL) {
-		purple_debug_error("connection", "purple_connection_error_reason: check `description != NULL' failed\n");
+		purple_debug_error("connection", "purple_connection_error_reason called with NULL description\n");
 		description = _("Unknown error");
 	}
 
--- a/libpurple/core.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/core.c	Fri Jan 30 18:21:16 2009 +0000
@@ -198,6 +198,14 @@
 	/* Transmission ends */
 	purple_connections_disconnect_all();
 
+	/*
+	 * Certificates must be destroyed before the SSL plugins, because
+	 * PurpleCertificates contain pointers to PurpleCertificateSchemes,
+	 * and the PurpleCertificateSchemes will be unregistered when the
+	 * SSL plugin is uninit.
+	 */
+	purple_certificate_uninit();
+
 	/* The SSL plugins must be uninit before they're unloaded */
 	purple_ssl_uninit();
 
@@ -220,7 +228,6 @@
 	purple_notify_uninit();
 	purple_conversations_uninit();
 	purple_connections_uninit();
-	purple_certificate_uninit();
 	purple_buddy_icons_uninit();
 	purple_accounts_uninit();
 	purple_savedstatuses_uninit();
--- a/libpurple/dbus-analyze-functions.py	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/dbus-analyze-functions.py	Fri Jan 30 18:21:16 2009 +0000
@@ -117,7 +117,7 @@
             self.params.append(Parameter.fromtokens(paramtexts[i].split(), i))
 
         self.call = "%s(%s)" % (self.function.name,
-                                ", ".join([param.name for param in self.params]))
+                                ", ".join(param.name for param in self.params))
         
     
     def process(self):
@@ -160,6 +160,10 @@
             elif type[0].startswith("Purple") or type[0] == "xmlnode":
                 return self.inputpurplestructure(type, name)
 
+            # special case for *_get_data functions, be careful here...
+            elif (type[0] == "size_t") and (name == "len"):
+                return self.inputgetdata(type, name)
+            
             # unknown pointers are always replaced with NULL
             else:
                 return self.inputpointer(type, name)
@@ -196,6 +200,10 @@
             if type[0] in ["GList", "GSList"]:
                 return self.outputlist(type, name)
 
+        # Special case for *_get_data functions
+        if type[0] == "gconstpointer":
+            return self.outputgetdata(type, name)
+
         raise myexception
     
 
@@ -309,7 +317,13 @@
         self.returncode.append("return garray_int_to_%s(%s);" %
                                (type[0].lower(), name));
 
- 
+    # Special case for *_get_data functions, don't need client bindings,
+    #  but do need the name so it doesn't crash
+    def inputgetdata(self, type, name):
+        raise myexception
+    def outputgetdata(self, type, name):
+        raise myexception
+
 class ServerBinding (Binding):
     def __init__(self, functiontext, paramtexts):
         Binding.__init__(self, functiontext, paramtexts)
@@ -475,6 +489,21 @@
                               % (name, name))
             self.addouttype("ai", name)
 
+    # Special case for *_get_data functions
+    def inputgetdata(self, type, name):
+        self.cdecls.append("\tsize_t %s = 0;" % name)
+        return True
+    def outputgetdata(self, type, name):
+        # 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))
+
+        self.cdecls.append("\tgconstpointer %s;" % name)
+        self.ccode.append("\t%s = %s;" % (name, self.call))
+        self.cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &%s, %s" \
+                               % (name, "len"))
+        self.addouttype("ay", name)
 
 class BindingSet:
     regexp = r"^(\w[^()]*)\(([^()]*)\)\s*;\s*$";
--- a/libpurple/network.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/network.c	Fri Jan 30 18:21:16 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/auth.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/jabber/auth.c	Fri Jan 30 18:21:16 2009 +0000
@@ -749,8 +749,8 @@
 
 				val_end = cur;
 				while (val_end != val_start && (*val_end == ' ' || *val_end == ',' || *val_end == '\t'
-						|| *val_end == '\r' || *val_start == '\n'
-						|| *val_end == '"'))
+						|| *val_end == '\r' || *val_end == '\n'
+						|| *val_end == '"'  || *val_end == '\0'))
 					val_end--;
 
 				if (val_start != val_end)
--- a/libpurple/protocols/jabber/buddy.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Fri Jan 30 18:21:16 2009 +0000
@@ -2305,7 +2305,7 @@
 	JabberBuddyResource *jbr = jabber_buddy_find_resource((JabberBuddy*)jb, NULL);
 
 	if (!jbr) {
-		purple_debug_error("jabber",
+		purple_debug_info("jabber",
 			"Unable to find caps: buddy might be offline\n");
 		return FALSE;
 	}
--- a/libpurple/protocols/msn/cmdproc.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/cmdproc.c	Fri Jan 30 18:21:16 2009 +0000
@@ -35,6 +35,9 @@
 	cmdproc->txqueue = g_queue_new();
 	cmdproc->history = msn_history_new();
 
+	cmdproc->multiparts = g_hash_table_new_full(g_str_hash, g_str_equal,
+	                                            NULL, (GDestroyNotify)msn_message_unref);
+
 	return cmdproc;
 }
 
@@ -53,6 +56,8 @@
 	if (cmdproc->last_cmd != NULL)
 		msn_command_destroy(cmdproc->last_cmd);
 
+	g_hash_table_destroy(cmdproc->multiparts);
+
 	g_free(cmdproc);
 }
 
@@ -235,6 +240,61 @@
 msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
 	MsnMsgTypeCb cb;
+	const char *messageId = NULL;
+
+	/* Multi-part messages */
+	if ((messageId = msn_message_get_attr(msg, "Message-ID")) != NULL) {
+		const char *chunk_text = msn_message_get_attr(msg, "Chunks");
+		guint chunk;
+		if (chunk_text != NULL) {
+			chunk = strtol(chunk_text, NULL, 10);
+			/* 1024 chunks of ~1300 bytes is ~1MB, which seems OK to prevent 
+			   some random client causing pidgin to hog a ton of memory.
+			   Probably should figure out the maximum that the official client
+			   actually supports, though. */
+			if (chunk > 0 && chunk < 1024) {
+				msg->total_chunks = chunk;
+				msg->received_chunks = 1;
+				g_hash_table_insert(cmdproc->multiparts, (gpointer)messageId, msn_message_ref(msg));
+				purple_debug_info("msn", "Received chunked message, messageId: '%s', total chunks: %d\n",
+				                  messageId, chunk);
+			} else {
+				purple_debug_error("msn", "MessageId '%s' has too many chunks: %d\n", messageId, chunk);
+			}
+			return;
+		} else {
+			chunk_text = msn_message_get_attr(msg, "Chunk");
+			if (chunk_text != NULL) {
+				MsnMessage *first = g_hash_table_lookup(cmdproc->multiparts, messageId);
+				chunk = strtol(chunk_text, NULL, 10);
+				if (first == NULL) {
+					purple_debug_error("msn",
+					                   "Unable to find first chunk of messageId '%s' to correspond with chunk %d.\n",
+					                   messageId, chunk+1);
+				} else if (first->received_chunks == chunk) {
+					/* Chunk is from 1 to total-1 (doesn't count first one) */
+					purple_debug_info("msn", "Received chunk %d of %d, messageId: '%s'\n",
+					                  chunk+1, first->total_chunks, messageId);
+					first->body = g_realloc(first->body, first->body_len + msg->body_len);
+					memcpy(first->body + first->body_len, msg->body, msg->body_len);
+					first->body_len += msg->body_len;
+					first->received_chunks++;
+					if (first->received_chunks != first->total_chunks)
+						return;
+					else
+						/* We're done! Send it along... The caller takes care of
+						   freeing the old one. */
+						msg = first;
+				} else {
+					/* TODO: Can you legitimately receive chunks out of order? */
+					g_hash_table_remove(cmdproc->multiparts, messageId);
+					return;
+				}
+			} else {
+				purple_debug_error("msn", "Received MessageId '%s' with no chunk number!\n", messageId);
+			}
+		}
+	}
 
 	if (msn_message_get_content_type(msg) == NULL)
 	{
@@ -245,15 +305,14 @@
 	cb = g_hash_table_lookup(cmdproc->cbs_table->msgs,
 							 msn_message_get_content_type(msg));
 
-	if (cb == NULL)
-	{
+	if (cb != NULL)
+		cb(cmdproc, msg);
+	else
 		purple_debug_warning("msn", "Unhandled content-type '%s'\n",
 						   msn_message_get_content_type(msg));
 
-		return;
-	}
-
-	cb(cmdproc, msg);
+	if (messageId != NULL)
+		g_hash_table_remove(cmdproc->multiparts, messageId);
 }
 
 void
--- a/libpurple/protocols/msn/cmdproc.h	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/cmdproc.h	Fri Jan 30 18:21:16 2009 +0000
@@ -46,6 +46,8 @@
 
 	MsnHistory *history;
 
+	GHashTable *multiparts; /**< Multi-part message ID's */
+
 	void *data; /**< Extra data, like the switchboard. */
 };
 
--- a/libpurple/protocols/msn/contact.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Fri Jan 30 18:21:16 2009 +0000
@@ -1362,7 +1362,8 @@
 	xmlnode *changes;
 
 	purple_debug_info("msn", "Update contact information with new %s: %s\n",
-		type==MSN_UPDATE_DISPLAY ? "display name" : "alias", value);
+		type == MSN_UPDATE_DISPLAY ? "display name" : "alias",
+		value ? value : "(null)");
 	purple_debug_info("msn", "passport=%s\n", passport);
 	g_return_if_fail(passport != NULL);
 	contact_info = xmlnode_new("contactInfo");
--- a/libpurple/protocols/msn/msg.h	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/msg.h	Fri Jan 30 18:21:16 2009 +0000
@@ -109,6 +109,8 @@
 	char *charset;
 	char *body;
 	gsize body_len;
+	guint total_chunks;   /**< How many chunks in this multi-part message */
+	guint received_chunks; /**< How many chunks we've received so far */
 
 	MsnSlpHeader msnslp_header;
 	MsnSlpFooter msnslp_footer;
--- a/libpurple/protocols/msn/notification.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Fri Jan 30 18:21:16 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)
--- a/libpurple/protocols/msn/slpcall.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Fri Jan 30 18:21:16 2009 +0000
@@ -239,6 +239,10 @@
 			msn_slpcall_session_init(slpcall);
 	}
 #endif
+	else if (slpmsg->flags == 0x2)
+	{
+		/* Acknowledgement of previous message. Don't do anything currently. */
+	}
 	else
 		purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%08lx\n",
 		                     slpmsg->flags);
--- a/libpurple/protocols/msn/switchboard.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Fri Jan 30 18:21:16 2009 +0000
@@ -799,7 +799,7 @@
 
 	msn_cmdproc_process_msg(cmdproc, msg);
 
-	msn_message_destroy(msg);
+	msn_message_unref(msg);
 }
 
 static void
--- a/libpurple/protocols/msn/user.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/user.c	Fri Jan 30 18:21:16 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	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/msn/user.h	Fri Jan 30 18:21:16 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/oscar/family_chat.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/family_chat.c	Fri Jan 30 18:21:16 2009 +0000
@@ -79,13 +79,15 @@
 
 		if (conn->type != SNAC_FAMILY_CHAT)
 			continue;
-		if (!conn->internal) {
-			purple_debug_misc("oscar", "faim: chat: chat connection with no name! (fd = %d)\n", conn->fd);
+		if (!conn->internal)
+		{
+			purple_debug_misc("oscar", "%sfaim: chat: chat connection with no name! (fd = %d)\n",
+					conn->gsc ? "(ssl) " : "", conn->gsc ? conn->gsc->fd : conn->fd);
 			continue;
 		}
 
 		if (strcmp(ccp->name, name) == 0)
-			return conn;;
+			return conn;
 	}
 
 	return NULL;
--- a/libpurple/protocols/oscar/family_icq.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Fri Jan 30 18:21:16 2009 +0000
@@ -456,64 +456,6 @@
 	return 0;
 }
 
-/*
- * getstatusnote may be a misleading name because the response
- * contains a lot of different information but currently it's only
- * used to get that.
- */
-int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len)
-{
-	FlapConnection *conn;
-	ByteStream bs;
-	aim_snacid_t snacid;
-	int bslen;
-
-	purple_debug_misc("oscar", "aim_icq_getstatusnote: requesting status note for %s.\n", uin);
-
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
-	{
-		purple_debug_misc("oscar", "aim_icq_getstatusnote: no connection.\n");
-		return -EINVAL;
-	}
-
-	bslen = 2 + 4 + 2 + 2 + 2 + 2 + 58 + strlen(uin);
-	byte_stream_new(&bs, 4 + bslen);
-
-	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
-
-	byte_stream_put16(&bs, 0x0001);
-	byte_stream_put16(&bs, bslen);
-
-	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putuid(&bs, od);
-	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
-	byte_stream_putle16(&bs, snacid); /* eh. */
-	byte_stream_putle16(&bs, 0x0fa0); /* shrug. */
-	byte_stream_putle16(&bs, 58 + strlen(uin));
-
-	byte_stream_put32(&bs, 0x05b90002);    /* don't ask */
-	byte_stream_put32(&bs, 0x80000000);
-	byte_stream_put32(&bs, 0x00000006);
-	byte_stream_put32(&bs, 0x00010002);
-	byte_stream_put32(&bs, 0x00020000);
-	byte_stream_put32(&bs, 0x04e30000);
-	byte_stream_put32(&bs, 0x00020002);
-	byte_stream_put32(&bs, 0x00000001);
-
-	byte_stream_put16(&bs, 24 + strlen(uin));
-	byte_stream_put32(&bs, 0x003c0010);
-	byte_stream_putraw(&bs, note_hash, 16); /* status note hash */
-	byte_stream_put16(&bs, 0x0032);        /* buddy uin */
-	byte_stream_put16(&bs, strlen(uin));
-	byte_stream_putstr(&bs, uin);
-
-	flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x000, snacid, &bs, FALSE);
-
-	byte_stream_destroy(&bs);
-
-	return 0;
-}
-
 static void aim_icq_freeinfo(struct aim_icq_info *info) {
 	int i;
 
--- a/libpurple/protocols/oscar/family_oservice.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Fri Jan 30 18:21:16 2009 +0000
@@ -103,12 +103,29 @@
 aim_srv_requestnew(OscarData *od, guint16 serviceid)
 {
 	FlapConnection *conn;
+	ByteStream bs;
+	aim_snacid_t snacid;
+	GSList *tlvlist = NULL;
 
 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
 	if(!conn)
 		return;
 
-	aim_genericreq_s(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, &serviceid);
+	byte_stream_new(&bs, 6);
+
+	byte_stream_put16(&bs, serviceid);
+
+	if (od->use_ssl)
+		/* Request SSL Connection */
+		aim_tlvlist_add_noval(&tlvlist, 0x008c);
+
+	aim_tlvlist_write(&bs, &tlvlist);
+	aim_tlvlist_free(tlvlist);
+
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, snacid, &bs);
+
+	byte_stream_destroy(&bs);
 }
 
 /*
@@ -127,10 +144,10 @@
 	struct chatsnacinfo csi;
 
 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
-	if (!conn || !roomname || !strlen(roomname))
+	if (!conn || !roomname || roomname[0] == '\0')
 		return -EINVAL;
 
-	byte_stream_new(&bs, 502);
+	byte_stream_new(&bs, 506);
 
 	memset(&csi, 0, sizeof(csi));
 	csi.exchange = exchange;
@@ -143,6 +160,11 @@
 	byte_stream_put16(&bs, 0x000e);
 
 	aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance);
+
+	if (od->use_ssl)
+		/* Request SSL Connection */
+		aim_tlvlist_add_noval(&tlvlist, 0x008c);
+
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
@@ -179,6 +201,8 @@
 	redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
 	redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
 	redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
+	redir.ssl_cert_cn = aim_tlv_getstr(tlvlist, 0x008d, 1);
+	redir.use_ssl = aim_tlv_get8(tlvlist, 0x008e, 1);
 
 	/* Fetch original SNAC so we can get csi if needed */
 	origsnac = aim_remsnac(od, snac->id);
@@ -196,6 +220,7 @@
 
 	g_free((void *)redir.ip);
 	g_free((void *)redir.cookie);
+	g_free((void *)redir.ssl_cert_cn);
 
 	if (origsnac)
 		g_free(origsnac->data);
--- a/libpurple/protocols/oscar/flap_connection.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Fri Jan 30 18:21:16 2009 +0000
@@ -364,6 +364,15 @@
 		conn->fd = -1;
 	}
 
+	if (conn->gsc != NULL)
+	{
+		if (conn->type == SNAC_FAMILY_LOCATE)
+			flap_connection_send_close(od, conn);
+
+		purple_ssl_close(conn->gsc);
+		conn->gsc = NULL;
+	}
+
 	if (conn->watcher_incoming != 0)
 	{
 		purple_input_remove(conn->watcher_incoming);
@@ -467,6 +476,7 @@
 
 	g_free(conn->error_message);
 	g_free(conn->cookie);
+	g_free(conn->ssl_cert_cn);
 
 	/*
 	 * Free conn->internal, if necessary
@@ -844,24 +854,31 @@
  * All complete FLAPs handled immedate after they're received.
  * Incomplete FLAP data is stored locally and appended to the next
  * time this callback is triggered.
+ *
+ * This is called by flap_connection_recv_cb and
+ * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
  */
-void
-flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
+static void
+flap_connection_recv(FlapConnection *conn)
 {
-	FlapConnection *conn;
+	gpointer buf;
+	gsize buflen;
 	gssize read;
 
-	conn = data;
-
 	/* Read data until we run out of data and break out of the loop */
 	while (TRUE)
 	{
 		/* Start reading a new FLAP */
 		if (conn->buffer_incoming.data.data == NULL)
 		{
+			buf = conn->header + conn->header_received;
+			buflen = 6 - conn->header_received;
+
 			/* Read the first 6 bytes (the FLAP header) */
-			read = recv(conn->fd, conn->header + conn->header_received,
-					6 - conn->header_received, 0);
+			if (conn->gsc)
+				read = purple_ssl_read(conn->gsc, buf, buflen);
+			else
+				read = recv(conn->fd, buf, buflen, 0);
 
 			/* Check if the FLAP server closed the connection */
 			if (read == 0)
@@ -918,13 +935,15 @@
 			conn->buffer_incoming.data.offset = 0;
 		}
 
-		if (conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset)
+		buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset;
+		if (buflen)
 		{
+			buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset];
 			/* Read data into the temporary FlapFrame until it is complete */
-			read = recv(conn->fd,
-						&conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset],
-						conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset,
-						0);
+			if (conn->gsc)
+				read = purple_ssl_read(conn->gsc, buf, buflen);
+			else
+				read = recv(conn->fd, buf, buflen, 0);
 
 			/* Check if the FLAP server closed the connection */
 			if (read == 0)
@@ -964,6 +983,29 @@
 	}
 }
 
+void
+flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	FlapConnection *conn = data;
+
+	flap_connection_recv(conn);
+}
+
+void
+flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
+{
+	FlapConnection *conn = data;
+
+	flap_connection_recv(conn);
+}
+
+/**
+ * @param source When this function is called as a callback source is
+ *        set to the fd that triggered the callback.  But this function
+ *        is also called directly from flap_connection_send_byte_stream(),
+ *        in which case source will be -1.  So don't use source--use
+ *        conn->gsc or conn->fd instead.
+ */
 static void
 send_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
@@ -980,7 +1022,11 @@
 		return;
 	}
 
-	ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
+	if (conn->gsc)
+		ret = purple_ssl_write(conn->gsc, conn->buffer_outgoing->outptr,
+				writelen);
+	else
+		ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
 	if (ret <= 0)
 	{
 		if (ret < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
@@ -990,8 +1036,13 @@
 		/* Error! */
 		purple_input_remove(conn->watcher_outgoing);
 		conn->watcher_outgoing = 0;
-		close(conn->fd);
-		conn->fd = -1;
+		if (conn->gsc) {
+			purple_ssl_close(conn->gsc);
+			conn->gsc = NULL;
+		} else {
+			close(conn->fd);
+			conn->fd = -1;
+		}
 		flap_connection_schedule_destroy(conn,
 				OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
 		return;
@@ -1017,11 +1068,17 @@
 	purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count);
 
 	/* If we haven't already started writing stuff, then start the cycle */
-	if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
+	if (conn->watcher_outgoing == 0)
 	{
-		conn->watcher_outgoing = purple_input_add(conn->fd,
-				PURPLE_INPUT_WRITE, send_cb, conn);
-		send_cb(conn, conn->fd, 0);
+		if (conn->gsc) {
+			conn->watcher_outgoing = purple_input_add(conn->gsc->fd,
+					PURPLE_INPUT_WRITE, send_cb, conn);
+			send_cb(conn, -1, 0);
+		} else if (conn->fd >= 0) {
+			conn->watcher_outgoing = purple_input_add(conn->fd,
+					PURPLE_INPUT_WRITE, send_cb, conn);
+			send_cb(conn, -1, 0);
+		}
 	}
 }
 
--- a/libpurple/protocols/oscar/oscar.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri Jan 30 18:21:16 2009 +0000
@@ -1088,59 +1088,64 @@
 }
 
 /**
- * This is the callback function anytime purple_proxy_connect()
- * establishes a new TCP connection with an oscar host.  Depending
- * on the type of host, we do a few different things here.
+ * This is called from the callback functions for establishing
+ * a TCP connection with an oscar host if an error occurred.
  */
 static void
-connection_established_cb(gpointer data, gint source, const gchar *error_message)
+connection_common_error_cb(FlapConnection *conn, const gchar *error_message)
 {
-	PurpleConnection *gc;
 	OscarData *od;
+	PurpleConnection *gc;
+
+	od = conn->od;
+	gc = od->gc;
+
+	purple_debug_error("oscar", "unable to connect to FLAP "
+			"server of type 0x%04hx\n", conn->type);
+
+	if (conn->type == SNAC_FAMILY_AUTH)
+	{
+		gchar *msg;
+		msg = g_strdup_printf(_("Could not connect to authentication server:\n%s"),
+				error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
+		g_free(msg);
+	}
+	else if (conn->type == SNAC_FAMILY_LOCATE)
+	{
+		gchar *msg;
+		msg = g_strdup_printf(_("Could not connect to BOS server:\n%s"),
+				error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
+		g_free(msg);
+	}
+	else
+	{
+		/* Maybe we should call this for BOS connections, too? */
+		flap_connection_schedule_destroy(conn,
+				OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message);
+	}
+}
+
+/**
+ * This is called from the callback functions for establishing
+ * a TCP connection with an oscar host. Depending on the type
+ * of host, we do a few different things here.
+ */
+static void
+connection_common_established_cb(FlapConnection *conn)
+{
+	OscarData *od;
+	PurpleConnection *gc;
 	PurpleAccount *account;
-	FlapConnection *conn;
-
-	conn = data;
+
 	od = conn->od;
 	gc = od->gc;
 	account = purple_connection_get_account(gc);
 
-	conn->connect_data = NULL;
-	conn->fd = source;
-
-	if (source < 0)
-	{
-		purple_debug_error("oscar", "unable to connect to FLAP "
-				"server of type 0x%04hx\n", conn->type);
-		if (conn->type == SNAC_FAMILY_AUTH)
-		{
-			gchar *msg;
-			msg = g_strdup_printf(_("Could not connect to authentication server:\n%s"),
-					error_message);
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
-			g_free(msg);
-		}
-		else if (conn->type == SNAC_FAMILY_LOCATE)
-		{
-			gchar *msg;
-			msg = g_strdup_printf(_("Could not connect to BOS server:\n%s"),
-					error_message);
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
-			g_free(msg);
-		}
-		else
-		{
-			/* Maybe we should call this for BOS connections, too? */
-			flap_connection_schedule_destroy(conn,
-					OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message);
-		}
-		return;
-	}
-
 	purple_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n",
 			conn->type);
-	conn->watcher_incoming = purple_input_add(conn->fd,
-			PURPLE_INPUT_READ, flap_connection_recv_cb, conn);
+
 	if (conn->cookie == NULL)
 		flap_connection_send_version(od, conn);
 	else
@@ -1171,6 +1176,85 @@
 }
 
 static void
+connection_established_cb(gpointer data, gint source, const gchar *error_message)
+{
+	FlapConnection *conn;
+
+	conn = data;
+
+	conn->connect_data = NULL;
+	conn->fd = source;
+
+	if (source < 0)
+	{
+		connection_common_error_cb(conn, error_message);
+		return;
+	}
+
+	conn->watcher_incoming = purple_input_add(conn->fd,
+			PURPLE_INPUT_READ, flap_connection_recv_cb, conn);
+	connection_common_established_cb(conn);
+}
+
+static void
+ssl_connection_established_cb(gpointer data, PurpleSslConnection *gsc,
+		PurpleInputCondition cond)
+{
+	FlapConnection *conn;
+
+	conn = data;
+
+	purple_ssl_input_add(gsc, flap_connection_recv_cb_ssl, conn);
+	connection_common_established_cb(conn);
+}
+
+static void
+ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error,
+		gpointer data)
+{
+	FlapConnection *conn;
+
+	conn = data;
+
+	if (conn->watcher_outgoing)
+	{
+		purple_input_remove(conn->watcher_outgoing);
+		conn->watcher_outgoing = 0;
+	}
+
+	/* sslconn frees the connection on error */
+	conn->gsc = NULL;
+
+	connection_common_error_cb(conn, purple_ssl_strerror(error));
+}
+
+static void
+ssl_proxy_conn_established_cb(gpointer data, gint source, const gchar *error_message)
+{
+	OscarData *od;
+	PurpleConnection *gc;
+	PurpleAccount *account;
+	FlapConnection *conn;
+
+	conn = data;
+	od = conn->od;
+	gc = od->gc;
+	account = purple_connection_get_account(gc);
+
+	conn->connect_data = NULL;
+
+	if (source < 0)
+	{
+		connection_common_error_cb(conn, error_message);
+		return;
+	}
+
+	conn->gsc = purple_ssl_connect_with_host_fd(account, source,
+			ssl_connection_established_cb, ssl_connection_error_cb,
+			conn->ssl_cert_cn, conn);
+}
+
+static void
 flap_connection_established_bos(OscarData *od, FlapConnection *conn)
 {
 	PurpleConnection *gc = od->gc;
@@ -1430,17 +1514,56 @@
 		gc->flags |= PURPLE_CONNECTION_AUTO_RESP;
 	}
 
+	od->use_ssl = purple_account_get_bool(account, "use_ssl", OSCAR_DEFAULT_USE_SSL);
+
 	/* Connect to core Purple signals */
 	purple_prefs_connect_callback(gc, "/purple/away/idle_reporting", idle_reporting_pref_cb, gc);
 	purple_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc);
 
 	newconn = flap_connection_new(od, SNAC_FAMILY_AUTH);
-	newconn->connect_data = purple_proxy_connect(NULL, account,
-			purple_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER),
-			purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
-			connection_established_cb, newconn);
-	if (newconn->connect_data == NULL)
-	{
+	if (od->use_ssl) {
+		if (purple_ssl_is_supported()) {
+			const char *server = purple_account_get_string(account, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER);
+			/*
+			 * If the account's server is what the oscar prpl has offered as
+			 * the default login server through the vast eons (all two of
+			 * said default options, AFAIK) and the user wants SSL, we'll
+			 * do what we know is best for them and change the setting out
+			 * from under them to the SSL login server.
+			 */
+			if (!strcmp(server, OSCAR_DEFAULT_LOGIN_SERVER) || !strcmp(server, OSCAR_OLD_LOGIN_SERVER)) {
+				purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n");
+				purple_account_set_string(account, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER);
+				server = OSCAR_DEFAULT_SSL_LOGIN_SERVER;
+			}
+
+			newconn->gsc = purple_ssl_connect(account, server,
+					purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
+					ssl_connection_established_cb, ssl_connection_error_cb, newconn);
+		} else {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
+					_("SSL support unavailable"));
+		}
+	} else {
+		const char *server = purple_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER);
+
+		/*
+		 * See the comment above. We do the reverse here. If they don't want
+		 * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER,
+		 * set it back to the default.
+		 */
+		if (!strcmp(server, OSCAR_DEFAULT_SSL_LOGIN_SERVER)) {
+			purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n");
+			purple_account_set_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER);
+			server = OSCAR_DEFAULT_LOGIN_SERVER;
+		}
+
+		newconn->connect_data = purple_proxy_connect(NULL, account, server,
+				purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
+				connection_established_cb, newconn);
+	}
+
+	if (newconn->gsc == NULL && newconn->connect_data == NULL) {
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Couldn't connect to host"));
 		return;
@@ -1565,8 +1688,23 @@
 	newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE);
 	newconn->cookielen = info->cookielen;
 	newconn->cookie = g_memdup(info->cookie, info->cookielen);
-	newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
-			connection_established_cb, newconn);
+
+	if (od->use_ssl)
+	{
+		/*
+		 * This shouldn't be hardcoded except that the server isn't sending
+		 * us a name to use for comparing the certificate common name.
+		 */
+		newconn->ssl_cert_cn = g_strdup("bos.oscar.aol.com");
+		newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
+				ssl_proxy_conn_established_cb, newconn);
+	}
+	else
+	{
+		newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
+				connection_established_cb, newconn);
+	}
+
 	g_free(host);
 	if (newconn->connect_data == NULL)
 	{
@@ -1871,8 +2009,22 @@
 	else
 		host = g_strdup(redir->ip);
 
-	purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx\n",
-					host, port, redir->group);
+	/*
+	 * These FLAP servers advertise SSL (type "0x02"), but SSL connections to these hosts
+	 * die a painful death. iChat and Miranda, when using SSL, still do these in plaintext.
+	 */
+	if (redir->use_ssl && (redir->group == SNAC_FAMILY_ADMIN ||
+	                       redir->group == SNAC_FAMILY_BART))
+	{
+		purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n",
+						redir->group);
+		redir->use_ssl = 0;
+	}
+
+	purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx%s\n",
+					host, port, redir->group,
+					od->use_ssl && !redir->use_ssl ? " without SSL, despite main stream encryption" : "");
+
 	newconn = flap_connection_new(od, redir->group);
 	newconn->cookielen = redir->cookielen;
 	newconn->cookie = g_memdup(redir->cookie, redir->cookielen);
@@ -1890,9 +2042,26 @@
 		purple_debug_info("oscar", "Connecting to chat room %s exchange %hu\n", cc->name, cc->exchange);
 	}
 
-	newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
-			connection_established_cb, newconn);
-	if (newconn->connect_data == NULL)
+
+	if (redir->use_ssl)
+	{
+		/*
+		 * TODO: It should be possible to specify a certificate common name
+		 * distinct from the host we're passing to purple_ssl_connect. The
+		 * way to work around that is to use purple_proxy_connect +
+		 * purple_ssl_connect_with_host_fd
+		 */
+		newconn->ssl_cert_cn = g_strdup(redir->ssl_cert_cn);
+		newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
+				ssl_proxy_conn_established_cb, newconn);
+	}
+	else
+	{
+		newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
+				connection_established_cb, newconn);
+	}
+
+	if (newconn->gsc == NULL && newconn->connect_data == NULL)
 	{
 		flap_connection_schedule_destroy(newconn,
 				OSCAR_DISCONNECT_COULD_NOT_CONNECT,
@@ -1905,37 +2074,6 @@
 	return 1;
 }
 
-static gboolean purple_requesticqstatusnote(gpointer data)
-{
-	PurpleConnection *gc = data;
-	OscarData *od = gc->proto_data;
-
-	while (od->statusnotes_queue != NULL)
-	{
-		char *sn;
-		struct aim_ssi_item *ssi_item;
-		aim_tlv_t *note_hash;
-
-		sn = od->statusnotes_queue->data;
-
-		ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
-											 NULL, sn, AIM_SSI_TYPE_BUDDY);
-		if (ssi_item != NULL)
-		{
-			note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1);
-			if (note_hash != NULL) {
-				aim_icq_getstatusnote(od, sn, note_hash->value, note_hash->length);
-			}
-		}
-
-		od->statusnotes_queue = g_slist_remove(od->statusnotes_queue, sn);
-		g_free(sn);
-	}
-
-	od->statusnotes_queue_timer = 0;
-	return FALSE;
-}
-
 
 static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
 {
@@ -1948,6 +2086,10 @@
 	const char *status_id;
 	va_list ap;
 	aim_userinfo_t *info;
+	char *message = NULL;
+	char *itmsurl = NULL;
+	char *tmp;
+	const char *tmp2;
 
 	gc = od->gc;
 	account = purple_connection_get_account(gc);
@@ -2002,49 +2144,36 @@
 		purple_prpl_got_user_status_deactive(account, info->sn, OSCAR_STATUS_ID_MOBILE);
 	}
 
-	if (strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) == 0)
-	{
-		char *message = NULL;
-		char *itmsurl = NULL;
-		char *tmp;
-		const char *tmp2;
-
-		if (info->status != NULL && info->status[0] != '\0')
-			/* Grab the available message */
-			message = oscar_encoding_to_utf8(account, info->status_encoding,
-					info->status, info->status_len);
-
+	if (info->status != NULL && info->status[0] != '\0')
+		/* Grab the available message */
+		message = oscar_encoding_to_utf8(account, info->status_encoding,
+										 info->status, info->status_len);
+
+	tmp2 = tmp = (message ? g_markup_escape_text(message, -1) : NULL);
+
+	if (strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) == 0) {
 		if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len)
 			/* Grab the iTunes Music Store URL */
 			itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
-					info->itmsurl, info->itmsurl_len);
-
-		tmp2 = tmp = (message ? g_markup_escape_text(message, -1) : NULL);
+											 info->itmsurl, info->itmsurl_len);
 
 		if (tmp2 == NULL && itmsurl != NULL)
+			/*
+			 * The message can't be NULL because NULL means it was the
+			 * last attribute, so the itmsurl would get ignored below.
+			 */
 			tmp2 = "";
 
 		purple_prpl_got_user_status(account, info->sn, status_id,
-				"message", tmp2, "itmsurl", itmsurl, NULL);
-		g_free(tmp);
-
-		g_free(message);
-		g_free(itmsurl);
+									"message", tmp2, "itmsurl", itmsurl, NULL);
 	}
 	else
-	{
-		PurpleBuddy *b = purple_find_buddy(account, info->sn);
-		PurplePresence *presence = purple_buddy_get_presence(b);
-		PurpleStatus *old_status = purple_presence_get_active_status(presence);
-		PurpleStatus *new_status = purple_presence_get_status(presence, status_id);
-		
-		/* If our status_id would change with this update, pass it to the core.
-		 * However, if our status_id would not change, do nothing, as we would clear out any existing
-		 * attributes on the status prematurely. purple_got_infoblock() will update the message as needed.
-		 */
-		if (old_status != new_status)
-			purple_prpl_got_user_status(account, info->sn, status_id, NULL);
-	}
+		purple_prpl_got_user_status(account, info->sn, status_id, "message", tmp2, NULL);
+
+	g_free(tmp);
+
+	g_free(message);
+	g_free(itmsurl);
 
 	/* Login time stuff */
 	if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
@@ -2099,51 +2228,6 @@
 		g_free(b16);
 	}
 
-	/*
-	 * If we didn't receive a status message with the status change,
-	 * or if the message is empty, and we have a note hash, then
-	 * query the ICQ6 status note.
-	 *
-	 * TODO: We should probably always query the status note regardless
-	 *       of whether they have a status message set, and we should
-	 *       figure out a way to display both the status note and the
-	 *       status message at the same time.
-	 */
-	if (info->status == NULL || info->status[0] == '\0')
-	{
-		struct aim_ssi_item *ssi_item;
-		aim_tlv_t *note_hash;
-
-		ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
-				NULL, info->sn, AIM_SSI_TYPE_BUDDY);
-		if (ssi_item != NULL)
-		{
-			note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1);
-			if (note_hash != NULL) {
-				/* We do automatic rate limiting at the FLAP level, so
-				 * a flood of requests won't disconnect us.  However,
-				 * it WOULD mean that we would have to wait a
-				 * potentially long time to be able to message in real
-				 * time again.  Also, since we're requesting with every
-				 * purple_parse_oncoming() call, which often come in
-				 * groups, we should coalesce to do only one lookup per
-				 * buddy.
-				 */
-				if (od->statusnotes_queue == NULL ||
-					g_slist_find_custom(od->statusnotes_queue, info->sn, (GCompareFunc)strcmp) == NULL)
-				{
-					od->statusnotes_queue = g_slist_prepend(od->statusnotes_queue,
-							g_strdup(info->sn));
-
-					if (od->statusnotes_queue_timer > 0)
-						purple_timeout_remove(od->statusnotes_queue_timer);
-					od->statusnotes_queue_timer = purple_timeout_add_seconds(3,
-							purple_requesticqstatusnote, gc);
-				}
-			}
-		}
-	}
-
 	return 1;
 }
 
@@ -5309,7 +5393,6 @@
 	PurpleBuddy *b;
 	PurpleGroup *g;
 	struct aim_ssi_item *ssi_item;
-	aim_tlv_t *note_hash;
 	va_list ap;
 	guint16 snac_subtype, type;
 	const char *name;
@@ -5379,13 +5462,7 @@
 
 	ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
 			gname, name, AIM_SSI_TYPE_BUDDY);
-	if (ssi_item != NULL)
-	{
-		note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1);
-		if (note_hash != NULL)
-			aim_icq_getstatusnote(od, name, note_hash->value, note_hash->length);
-	}
-	else
+	if (ssi_item == NULL)
 	{
 		purple_debug_error("oscar", "purple_ssi_parseaddmod: "
 				"Could not find ssi item for oncoming buddy %s, "
@@ -5823,19 +5900,8 @@
 		else
 			ret = g_strdup(_("Offline"));
 	}
-	else if (purple_status_is_available(status) && !strcmp(id, OSCAR_STATUS_ID_AVAILABLE))
+	else
 	{
-		/* Available */
-		message = purple_status_get_attr_string(status, "message");
-		if (message != NULL)
-		{
-			ret = g_strdup(message);
-			purple_util_chrreplace(ret, '\n', ' ');
-		}
-	}
-	else if (!purple_status_is_available(status) && !strcmp(id, OSCAR_STATUS_ID_AWAY))
-	{
-		/* Away */
 		message = purple_status_get_attr_string(status, "message");
 		if (message != NULL)
 		{
@@ -5847,13 +5913,15 @@
 			g_free(tmp1);
 			g_free(tmp2);
 		}
+		else if (purple_status_is_available(status))
+		{
+			/* Don't show "Available" as status message in case buddy doesn't have a status message */
+		}
 		else
 		{
-			ret = g_strdup(_("Away"));
+			ret = g_strdup(purple_status_get_name(status));
 		}
 	}
-	else
-		ret = g_strdup(purple_status_get_name(status));
 
 	return ret;
 }
@@ -6859,6 +6927,10 @@
 	option = purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT);
 	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
 
+	option = purple_account_option_bool_new(_("Use SSL"), "use_ssl",
+			OSCAR_DEFAULT_USE_SSL);
+	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+
 	option = purple_account_option_bool_new(
 		_("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy",
 		OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY);
--- a/libpurple/protocols/oscar/oscar.h	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Fri Jan 30 18:21:16 2009 +0000
@@ -34,6 +34,7 @@
 #include "eventloop.h"
 #include "internal.h"
 #include "proxy.h"
+#include "sslconn.h"
 
 #include <stdio.h>
 #include <string.h>
@@ -417,8 +418,10 @@
 	guint16 cookielen;
 	guint8 *cookie;
 	gpointer new_conn_data;
+	gchar *ssl_cert_cn;
 
 	int fd;
+	PurpleSslConnection *gsc;
 	guint8 header[6];
 	gssize header_received;
 	FlapFrame buffer_incoming;
@@ -476,6 +479,7 @@
 	GHashTable *buddyinfo;
 	GSList *requesticon;
 
+	gboolean use_ssl;
 	gboolean icq;
 	guint getblisttimer;
 
@@ -537,10 +541,6 @@
 
 	/** A linked list containing PeerConnections. */
 	GSList *peer_connections;
-
-	/** Queue of ICQ Status Notes to request. */
-	GSList *statusnotes_queue;
-	guint statusnotes_queue_timer;
 };
 
 /* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */
@@ -593,6 +593,8 @@
 	const char *ip;
 	guint16 cookielen;
 	const guint8 *cookie;
+	const char *ssl_cert_cn;
+	guint8 use_ssl;
 	struct { /* group == SNAC_FAMILY_CHAT */
 		guint16 exchange;
 		const char *room;
@@ -616,6 +618,8 @@
 FlapConnection *flap_connection_getbytype(OscarData *, int type);
 FlapConnection *flap_connection_getbytype_all(OscarData *, int type);
 void flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond);
+void flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
+
 void flap_connection_send(FlapConnection *conn, FlapFrame *frame);
 void flap_connection_send_version(OscarData *od, FlapConnection *conn);
 void flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy);
@@ -1352,7 +1356,6 @@
 int aim_icq_getalias(OscarData *od, const char *uin);
 int aim_icq_getallinfo(OscarData *od, const char *uin);
 int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias);
-int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len);
 
 
 /* 0x0017 - family_auth.c */
--- a/libpurple/protocols/oscar/oscar_data.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/oscar_data.c	Fri Jan 30 18:21:16 2009 +0000
@@ -91,14 +91,6 @@
 		g_free(od->requesticon->data);
 		od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon);
 	}
-	while (od->statusnotes_queue)
-	{
-		g_free(od->statusnotes_queue->data);
-		od->statusnotes_queue = g_slist_delete_link(od->statusnotes_queue,
-				od->statusnotes_queue);
-	}
-	if (od->statusnotes_queue_timer > 0)
-		purple_timeout_remove(od->statusnotes_queue_timer);
 	g_free(od->email);
 	g_free(od->newp);
 	g_free(od->oldp);
--- a/libpurple/protocols/oscar/oscarcommon.h	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/oscarcommon.h	Fri Jan 30 18:21:16 2009 +0000
@@ -32,6 +32,8 @@
 
 #define OSCAR_DEFAULT_LOGIN_SERVER "login.messaging.aol.com"
 #define OSCAR_DEFAULT_LOGIN_PORT 5190
+#define OSCAR_DEFAULT_SSL_LOGIN_SERVER "slogin.oscar.aol.com"
+#define OSCAR_OLD_LOGIN_SERVER "login.oscar.aol.com"
 #ifndef _WIN32
 #define OSCAR_DEFAULT_CUSTOM_ENCODING "ISO-8859-1"
 #else
@@ -42,6 +44,7 @@
 #define OSCAR_DEFAULT_WEB_AWARE FALSE
 #define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE
 #define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE
+#define OSCAR_DEFAULT_USE_SSL FALSE
 
 #ifdef _WIN32
 const char *oscar_get_locale_charset(void);
--- a/libpurple/protocols/oscar/peer.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/protocols/oscar/peer.c	Fri Jan 30 18:21:16 2009 +0000
@@ -690,7 +690,10 @@
 		return;
 	}
 
-	listener_ip = purple_network_get_my_ip(bos_conn->fd);
+	if (bos_conn->gsc)
+		listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
+	else
+		listener_ip = purple_network_get_my_ip(bos_conn->fd);
 	listener_port = purple_network_get_port_from_fd(conn->listenerfd);
 	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
 	{
--- a/libpurple/purple-url-handler	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/purple-url-handler	Fri Jan 30 18:21:16 2009 +0000
@@ -207,7 +207,11 @@
 
     def correct_server(account):
         username = cpurple.PurpleAccountGetUsername(account)
-        return (server == (username.split("@"))[1])
+        user_split = (username.split("@"))
+        # Not all accounts have a split, so append an empty string so the
+        # [1] doesn't throw an IndexError.
+        user_split.append("")
+        return (server == user_split[1])
 
     account = findaccount(protocol, matcher=correct_server)
 
--- a/libpurple/status.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/libpurple/status.c	Fri Jan 30 18:21:16 2009 +0000
@@ -816,28 +816,42 @@
 	/* Reset any unspecified attributes to their default value */
 	status_type = purple_status_get_type(status);
 	l = purple_status_type_get_attrs(status_type);
-	while (l != NULL)
-	{
+	while (l != NULL) {
 		PurpleStatusAttr *attr;
 
 		attr = l->data;
-		if (!g_list_find_custom(specified_attr_ids, attr->id, (GCompareFunc)strcmp))
-		{
+		l = l->next;
+
+		if (!g_list_find_custom(specified_attr_ids, attr->id, (GCompareFunc)strcmp)) {
 			PurpleValue *default_value;
 			default_value = purple_status_attr_get_value(attr);
-			if (default_value->type == PURPLE_TYPE_STRING)
-				purple_status_set_attr_string(status, attr->id,
-						purple_value_get_string(default_value));
-			else if (default_value->type == PURPLE_TYPE_INT)
-				purple_status_set_attr_int(status, attr->id,
-						purple_value_get_int(default_value));
-			else if (default_value->type == PURPLE_TYPE_BOOLEAN)
-				purple_status_set_attr_boolean(status, attr->id,
-						purple_value_get_boolean(default_value));
+			if (default_value->type == PURPLE_TYPE_STRING) {
+				const char *cur = purple_status_get_attr_string(status, attr->id);
+				const char *def = purple_value_get_string(default_value);
+				if ((cur == NULL && def == NULL)
+				    || (cur != NULL && def != NULL
+					&& !strcmp(cur, def))) {
+					continue;
+				}
+
+				purple_status_set_attr_string(status, attr->id, def);
+			} else if (default_value->type == PURPLE_TYPE_INT) {
+				int cur = purple_status_get_attr_int(status, attr->id);
+				int def = purple_value_get_int(default_value);
+				if (cur == def)
+					continue;
+
+				purple_status_set_attr_int(status, attr->id, def);
+			} else if (default_value->type == PURPLE_TYPE_BOOLEAN) {
+				gboolean cur = purple_status_get_attr_boolean(status, attr->id);
+				gboolean def = purple_value_get_boolean(default_value);
+				if (cur == def)
+					continue;
+
+				purple_status_set_attr_boolean(status, attr->id, def);
+			}
 			changed = TRUE;
 		}
-
-		l = l->next;
 	}
 	g_list_free(specified_attr_ids);
 
--- a/pidgin/gtkaccount.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/gtkaccount.c	Fri Jan 30 18:21:16 2009 +0000
@@ -555,8 +555,10 @@
 	/* Password */
 	dialog->password_entry = gtk_entry_new();
 	gtk_entry_set_visibility(GTK_ENTRY(dialog->password_entry), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 	if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->password_entry)) == '*')
 		gtk_entry_set_invisible_char(GTK_ENTRY(dialog->password_entry), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 	dialog->password_box = add_pref_box(dialog, vbox, _("_Password:"),
 										  dialog->password_entry);
 
@@ -860,8 +862,10 @@
 				if (purple_account_option_get_masked(option))
 				{
 					gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 					if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
 						gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 				}
 
 				if (str_value != NULL)
@@ -1089,8 +1093,10 @@
 	/* Password */
 	dialog->proxy_pass_entry = gtk_entry_new();
 	gtk_entry_set_visibility(GTK_ENTRY(dialog->proxy_pass_entry), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 	if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->proxy_pass_entry)) == '*')
 		gtk_entry_set_invisible_char(GTK_ENTRY(dialog->proxy_pass_entry), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 	add_pref_box(dialog, vbox2, _("Pa_ssword:"), dialog->proxy_pass_entry);
 
 	if (dialog->account != NULL &&
@@ -1984,9 +1990,13 @@
 		if (purple_account_get_bool(account, "use-global-buddyicon", TRUE)) {
 			if (global_buddyicon != NULL)
 				buddyicon = g_object_ref(G_OBJECT(global_buddyicon));
-			/* This is for when set_account() is called for a single account */
-			else
-				img = purple_buddy_icons_find_account_icon(account);
+			else {
+				/* This is for when set_account() is called for a single account */
+				const char *path;
+				path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
+				if (path != NULL)
+					img = purple_imgstore_new_from_file(path);
+			}
 		} else {
 			img = purple_buddy_icons_find_account_icon(account);
 		}
--- a/pidgin/gtkblist.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/gtkblist.c	Fri Jan 30 18:21:16 2009 +0000
@@ -955,8 +955,10 @@
 			if (pce->secret)
 			{
 				gtk_entry_set_visibility(GTK_ENTRY(input), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
 					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 			}
 			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL);
 			g_signal_connect(G_OBJECT(input), "changed",
@@ -6826,8 +6828,10 @@
 			if (pce->secret)
 			{
 				gtk_entry_set_visibility(GTK_ENTRY(input), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
 					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 			}
 			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL);
 			g_signal_connect(G_OBJECT(input), "changed",
--- a/pidgin/gtkgaim-compat.h	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/gtkgaim-compat.h	Fri Jan 30 18:21:16 2009 +0000
@@ -297,7 +297,9 @@
 #define GAIM_HIG_BORDER PIDGIN_HIG_BORDER
 #define GAIM_HIG_BOX_SPACE PIDGIN_HIG_BOX_SPACE
 #define GAIM_HIG_CAT_SPACE PIDGIN_HIG_CAT_SPACE
+#if !GTK_CHECK_VERSION(2,16,0)
 #define GAIM_INVISIBLE_CHAR PIDGIN_INVISIBLE_CHAR
+#endif /* Less than GTK+ 2.16 */
 #define GAIM_IS_GTK_CONVERSATION PIDGIN_IS_PIDGIN_CONVERSATION
 #define GAIM_IS_GTK_PLUGIN PIDGIN_IS_PIDGIN_PLUGIN
 #define gaim_new_check_item pidgin_new_check_item
--- a/pidgin/gtkimhtml.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/gtkimhtml.c	Fri Jan 30 18:21:16 2009 +0000
@@ -5378,12 +5378,14 @@
 						text_tag_data_destroy(tmp);
 				}
 
-				if (tmp == NULL)
-					purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n");
-				else {
+				if (tmp != NULL) {
 					g_string_append(str, tmp->end);
 					text_tag_data_destroy(tmp);
 				}
+#if 0 /* This can't be allowed to happen because it causes the iters to be invalidated in the debug window imhtml during text copying */
+				else
+					purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n");
+#endif
 
 				while ((tmp = g_queue_pop_head(r))) {
 					g_string_append(str, tmp->start);
--- a/pidgin/gtkmain.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/gtkmain.c	Fri Jan 30 18:21:16 2009 +0000
@@ -784,8 +784,8 @@
 		dbus_connection_send_with_reply_and_block(conn, message, -1, NULL);
 		dbus_message_unref(message);
 #endif
-		purple_debug_info("main", "exiting because another libpurple client is already running\n");
 		purple_core_quit();
+		g_printerr(_("Exiting because another libpurple client is already running.\n"));
 #ifdef HAVE_SIGNAL_H
 		g_free(segfault_message);
 #endif
--- a/pidgin/gtkpluginpref.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/gtkpluginpref.c	Fri Jan 30 18:21:16 2009 +0000
@@ -101,8 +101,10 @@
 				if (purple_plugin_pref_get_masked(pref))
 				{
 					gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 					if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
 						gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 				}
 				g_signal_connect(G_OBJECT(entry), "changed",
 								 G_CALLBACK(entry_cb),
--- a/pidgin/gtkprefs.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/gtkprefs.c	Fri Jan 30 18:21:16 2009 +0000
@@ -1412,8 +1412,10 @@
 		gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
 		gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 1, 2, GTK_FILL , 0, 0, 0);
 		gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 		if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
 			gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 		g_signal_connect(G_OBJECT(entry), "changed",
 				 G_CALLBACK(proxy_print_option), (void *)PROXYPASS);
 
--- a/pidgin/gtkrequest.c	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/gtkrequest.c	Fri Jan 30 18:21:16 2009 +0000
@@ -430,8 +430,10 @@
 			if (masked)
 			{
 				gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 				if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
 					gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 			}
 		}
 		gtk_widget_show_all(vbox);
@@ -791,8 +793,10 @@
 		if (purple_request_field_string_is_masked(field))
 		{
 			gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
 			if (gtk_entry_get_invisible_char(GTK_ENTRY(widget)) == '*')
 				gtk_entry_set_invisible_char(GTK_ENTRY(widget),	PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
 		}
 
 		gtk_editable_set_editable(GTK_EDITABLE(widget),
--- a/pidgin/pidgin.h	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/pidgin.h	Fri Jan 30 18:21:16 2009 +0000
@@ -91,11 +91,22 @@
 #define PIDGIN_HIG_BORDER        12
 #define PIDGIN_HIG_BOX_SPACE      6
 
+#if !GTK_CHECK_VERSION(2,16,0) || !defined(PIDGIN_DISABLE_DEPRECATED)
 /*
- * See GNOME bug #307304 for some discussion about the invisible
- * character.  0x25cf is a good choice, too.
+ * Older versions of GNOME defaulted to using an asterisk as the invisible
+ * character.  But this is ugly and we want to use something nicer.
+ *
+ * The default invisible character was changed in GNOME revision 21446
+ * (GTK+ 2.16) from an asterisk to the first available character out of
+ * 0x25cf, 0x2022, 0x2731, 0x273a.  See GNOME bugs 83935 and 307304 for
+ * discussion leading up to the change.
+ *
+ * Here's the change:
+ * http://svn.gnome.org/viewvc/gtk%2B?view=revision&revision=21446
+ *
  */
-#define PIDGIN_INVISIBLE_CHAR (gunichar)0x2022
+#define PIDGIN_INVISIBLE_CHAR (gunichar)0x25cf
+#endif /* Less than GTK+ 2.16 */
 
 #endif /* _PIDGIN_H_ */
 
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Mon Jan 26 06:04:01 2009 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Fri Jan 30 18:21:16 2009 +0000
@@ -718,6 +718,7 @@
     Delete "$INSTDIR\ca-certs\StartCom_Free_SSL_CA.pem"
     Delete "$INSTDIR\ca-certs\Verisign_Class3_Primary_CA.pem"
     Delete "$INSTDIR\ca-certs\VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.pem"
+    Delete "$INSTDIR\ca-certs\VeriSign_International_Server_Class_3_CA.pem"
     Delete "$INSTDIR\ca-certs\Verisign_RSA_Secure_Server_CA.pem"
     RMDir "$INSTDIR\ca-certs"
     RMDir /r "$INSTDIR\locale"
--- a/po/fi.po	Mon Jan 26 06:04:01 2009 +0000
+++ b/po/fi.po	Fri Jan 30 18:21:16 2009 +0000
@@ -11,7 +11,7 @@
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2009-01-07 13:30+0200\n"
-"PO-Revision-Date: 2009-01-07 13:30+0200\n"
+"PO-Revision-Date: 2009-01-23 19:58+0200\n"
 "Last-Translator: Timo Jyrinki <timo.jyrinki@iki.fi>\n"
 "Language-Team: \n"
 "MIME-Version: 1.0\n"
@@ -1169,7 +1169,7 @@
 msgstr "Keskustelut"
 
 msgid "Logging"
-msgstr "Kirjataan lokiin"
+msgstr "Lokiinkirjaus"
 
 msgid "You must fill all the required fields."
 msgstr "Täytä kaikki vaaditut kentät."
@@ -15466,7 +15466,7 @@
 #~ "keskustelun uuden keskustelun aluksi.\n"
 #~ "\n"
 #~ "Historia-liitännäinen vaatii lokiinkirjauksen käyttöä. Loki voidaan ottaa "
-#~ "käyttöön menemällä Työkalut -> Asetukset -> Kirjataan lokiin. Lokien "
+#~ "käyttöön menemällä Työkalut -> Asetukset -> Lokiinkirjaus. Lokien "
 #~ "käyttöönotto pikaviesteille ja/tai ryhmäkeskusteluille ottaa käyttöön "
 #~ "historiatoiminnon vastaaville keskustelutyypeille."
 
--- a/share/ca-certs/Makefile.am	Mon Jan 26 06:04:01 2009 +0000
+++ b/share/ca-certs/Makefile.am	Fri Jan 30 18:21:16 2009 +0000
@@ -10,7 +10,8 @@
 
 EXTRA_CERTS = \
 		Microsoft_Internet_Authority.pem \
-		Microsoft_Secure_Server_Authority.pem
+		Microsoft_Secure_Server_Authority.pem \
+		VeriSign_International_Server_Class_3_CA.pem
 
 
 cacertsdir =	$(datadir)/purple/ca-certs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/VeriSign_International_Server_Class_3_CA.pem	Fri Jan 30 18:21:16 2009 +0000
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDgzCCAuygAwIBAgIQRvzrurTQLw+SYJgjP5MHjzANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNOTcwNDE3MDAwMDAwWhcNMTYxMDI0MjM1OTU5WjCBujEfMB0GA1UEChMWVmVy
+aVNpZ24gVHJ1c3QgTmV0d29yazEXMBUGA1UECxMOVmVyaVNpZ24sIEluYy4xMzAx
+BgNVBAsTKlZlcmlTaWduIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gQ2xhc3Mg
+MzFJMEcGA1UECxNAd3d3LnZlcmlzaWduLmNvbS9DUFMgSW5jb3JwLmJ5IFJlZi4g
+TElBQklMSVRZIExURC4oYyk5NyBWZXJpU2lnbjCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEA2IKA6NYZAn0fhRg5JaJlK+G/1AXTvOY2O6rwTGxbtueqPHNFVbLx
+veqXQu2aNAoV1Klc9UAl3dkHwTKydWzEyruj/lYncUOqY/UwPpMo5frxCTvzt01O
+OfdcSVq4wR3Tsor+cDCVQsv+K1GLWjw6+SJPkLICp1OcTzTnqwSye28CAwEAAaOB
+4zCB4DAPBgNVHRMECDAGAQH/AgEAMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQEw
+KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL0NQUzA0BgNV
+HSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIGCWCGSAGG+EIEAQYKYIZIAYb4RQEI
+ATALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgEGMDEGA1UdHwQqMCgwJqAk
+oCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA0GCSqGSIb3DQEB
+BQUAA4GBAECOSZeWinPdjk3vPmG3yqBirfQOCrt1PeJu2CzHv/S5jDabyqLQnHJG
+OfamggNlEcS8vy2m9dk7CrWY+rN4uR7yK0xi1f2yeh3fM/1z+aXYLYwq6tH8sCi2
+6UlIE0uDihtIeyT3ON5vQVS4q1drBt/HotSp9vE2YoCI8ot11oBx
+-----END CERTIFICATE-----