changeset 25961:b018c91fb90b

propagate from branch 'im.pidgin.pidgin' (head 6a51501ec3aff341c8cd763ffc2076a9bbc07d23) to branch 'im.pidgin.cpw.malu.xmpp.idle' (head 0f53d2b4991ccab13787b974415c2bf7bb16b08f)
author Marcus Lundblad <ml@update.uu.se>
date Thu, 29 Jan 2009 19:15:09 +0000
parents 43da17b9a27e (current diff) c434094cf298 (diff)
children a92df60855f3
files
diffstat 40 files changed, 653 insertions(+), 368 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Thu Jan 22 20:39:12 2009 +0000
+++ b/AUTHORS	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/COPYRIGHT	Thu Jan 29 19:15:09 2009 +0000
@@ -444,6 +444,7 @@
 Todd Troxell
 Brad Turcotte
 Kyle Turman
+Jon Turney
 Junichi Uekawa
 Igor Vlasenko
 István Váradi
@@ -480,6 +481,7 @@
 Dan Winship
 Michal Witkowski
 Scott Wolchok
+Rogier Wolff
 The Written Word, Inc.
 Kevin Wu Won
 Pui Lam Wong
--- a/ChangeLog	Thu Jan 22 20:39:12 2009 +0000
+++ b/ChangeLog	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/doc/finch.1.in	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/doc/pidgin.1.in	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/finch/libgnt/gntcolors.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/certificate.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/connection.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/core.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/dbus-analyze-functions.py	Thu Jan 29 19:15:09 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/protocols/msn/cmdproc.c	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/msn/cmdproc.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/msn/cmdproc.h	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/msn/msg.h	Thu Jan 29 19:15:09 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/slpcall.c	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Thu Jan 29 19:15:09 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/oscar/family_chat.c	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/family_chat.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/oscar_data.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/oscarcommon.h	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/protocols/oscar/peer.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/purple-url-handler	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/libpurple/status.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/gtkaccount.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/gtkblist.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/gtkgaim-compat.h	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/gtkimhtml.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/gtkmain.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/gtkpluginpref.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/gtkprefs.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/gtkrequest.c	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/pidgin.h	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/po/fi.po	Thu Jan 29 19:15:09 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	Thu Jan 22 20:39:12 2009 +0000
+++ b/share/ca-certs/Makefile.am	Thu Jan 29 19:15:09 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	Thu Jan 29 19:15:09 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-----