changeset 25554:ee5551542eee

propagate from branch 'im.pidgin.pidgin' (head 6f5c855ee2435b317e7ddb8c9551fd17cd0328fe) to branch 'im.pidgin.pidgin.yaz' (head 017ee71808fe6a5473018d15f37ae1de046eb3e2)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Fri, 31 Aug 2007 16:10:18 +0000
parents 7f8cf35fc99b (current diff) c07a9c6f0263 (diff)
children af320e03fa63
files libpurple/conversation.c libpurple/protocols/yahoo/yahoo.c pidgin/gtkconv.c
diffstat 41 files changed, 1120 insertions(+), 314 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Tue Aug 28 09:20:27 2007 +0000
+++ b/ChangeLog.API	Fri Aug 31 16:10:18 2007 +0000
@@ -1,6 +1,15 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 Version 2.2.0 (??/??/????):
+	libpurple:
+		Added:
+		* PURPLE_MESSAGE_INVISIBLE flag, which can be used by
+		  purple_conv_im_send_with_flags to send a message, but not display it
+		  in the conversation
+		Changed:
+		* purple_prefs_load is now called within purple_prefs_init.
+		  The UI no longer needs to call it.
+
 	Pidgin:
 		Added:
 		* pidgin_set_accessible_relations, sets up label-for and labelled-by
--- a/ChangeLog.win32	Tue Aug 28 09:20:27 2007 +0000
+++ b/ChangeLog.win32	Fri Aug 31 16:10:18 2007 +0000
@@ -1,5 +1,9 @@
+version 2.2.0 (??/??/2007):
+	* Updated gtkspell to 2.0.11
+	* Upgrade SILC to use the 1.1.2 toolkit
+
 version 2.1.1 (08/20/2007):
-        * No changes
+	* No changes
 
 version 2.1.0 (7/28/2007):
 	* Updated launcher application (pidgin.exe) to support portable mode
--- a/finch/Makefile.am	Tue Aug 28 09:20:27 2007 +0000
+++ b/finch/Makefile.am	Fri Aug 31 16:10:18 2007 +0000
@@ -14,6 +14,7 @@
 finch_SOURCES = \
 	gntaccount.c \
 	gntblist.c \
+	gntcertmgr.c \
 	gntconn.c \
 	gntconv.c \
 	gntdebug.c \
@@ -32,6 +33,7 @@
 finch_headers = \
 	gntaccount.h \
 	gntblist.h \
+	gntcertmgr.h \
 	gntconn.h \
 	gntconv.h \
 	gntdebug.h \
--- a/finch/finch.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/finch/finch.c	Fri Aug 31 16:10:18 2007 +0000
@@ -360,9 +360,7 @@
 	purple_set_blist(purple_blist_new());
 	purple_blist_load();
 
-	/* TODO: Move prefs loading into purple_prefs_init() */
-	purple_prefs_load();
-	purple_prefs_update_old();
+	/* TODO: should this be moved into finch_prefs_init() ? */
 	finch_prefs_update_old();
 
 	/* load plugins we had when we quit */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/gntcertmgr.c	Fri Aug 31 16:10:18 2007 +0000
@@ -0,0 +1,340 @@
+/**
+ * @file gntcertmgr.c GNT Certificate Manager API
+ * @ingroup finch
+ *
+ * finch
+ *
+ * Finch is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "internal.h"
+
+#include "certificate.h"
+#include "debug.h"
+#include "notify.h"
+#include "request.h"
+
+#include "finch.h"
+#include "gntcertmgr.h"
+
+#include "gntbutton.h"
+#include "gntlabel.h"
+#include "gnttree.h"
+#include "gntutils.h"
+#include "gntwindow.h"
+
+struct {
+	GntWidget *window;
+	GntWidget *tree;
+	PurpleCertificatePool *pool;
+} certmgr;
+
+/* Pretty much Xerox of gtkcertmgr */
+
+/* Add certificate */
+static void
+tls_peers_mgmt_import_ok2_cb(gpointer data, const char *result)
+{
+	PurpleCertificate *crt = (PurpleCertificate *) data;
+	const char *id = result;
+
+	/* TODO: Perhaps prompt if you're overwriting a cert? */
+
+	purple_certificate_pool_store(purple_certificate_find_pool("x509", "tls_peers"), id, crt);
+	purple_certificate_destroy(crt);
+}
+
+static void
+tls_peers_mgmt_import_cancel2_cb(gpointer data, const char *result)
+{
+	PurpleCertificate *crt = (PurpleCertificate *) data;
+	purple_certificate_destroy(crt);
+}
+
+static void
+tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename)
+{
+	PurpleCertificateScheme *x509;
+	PurpleCertificate *crt;
+
+	x509 = purple_certificate_pool_get_scheme(purple_certificate_find_pool("x509", "tls_peers"));
+
+	crt = purple_certificate_import(x509, filename);
+
+	if (crt != NULL) {
+		gchar *default_hostname;
+		default_hostname = purple_certificate_get_subject_name(crt);
+		purple_request_input(NULL,
+				_("Certificate Import"),
+				_("Specify a hostname"),
+				_("Type the host name this certificate is for."),
+				default_hostname, FALSE, FALSE, NULL,
+				_("OK"), G_CALLBACK(tls_peers_mgmt_import_ok2_cb),
+				_("Cancel"), G_CALLBACK(tls_peers_mgmt_import_cancel2_cb),
+				NULL, NULL, NULL,
+				crt);
+		g_free(default_hostname);
+	} else {
+		gchar * secondary;
+		secondary = g_strdup_printf(_("File %s could not be imported.\nMake sure that the file is readable and in PEM format.\n"), filename);
+		purple_notify_error(NULL,
+				_("Certificate Import Error"),
+				_("X.509 certificate import failed"),
+				secondary);
+		g_free(secondary);
+	}
+}
+
+static void
+add_cert_cb(GntWidget *button, gpointer null)
+{
+	purple_request_file(NULL,
+			_("Select a PEM certificate"),
+			"certificate.pem",
+			FALSE,
+			G_CALLBACK(tls_peers_mgmt_import_ok_cb),
+			NULL,
+			NULL, NULL, NULL, NULL );
+}
+
+/* Save certs in some file */
+static void
+tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename)
+{
+	PurpleCertificate *crt = (PurpleCertificate *) data;
+
+	if (!purple_certificate_export(filename, crt)) {
+		gchar * secondary;
+
+		secondary = g_strdup_printf(_("Export to file %s failed.\nCheck that you have write permission to the target path\n"), filename);
+		purple_notify_error(NULL,
+				    _("Certificate Export Error"),
+				    _("X.509 certificate export failed"),
+				    secondary);
+		g_free(secondary);
+	}
+
+	purple_certificate_destroy(crt);
+}
+
+static void
+save_cert_cb(GntWidget *button, gpointer null)
+{
+	PurpleCertificate *crt;
+	const char *key;
+
+	if (!certmgr.window)
+		return;
+
+	key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree));
+	if (!key)
+		return;
+
+	crt = purple_certificate_pool_retrieve(certmgr.pool, key);
+	if (!crt) {
+		purple_debug_error("gntcertmgr/tls_peers_mgmt",
+				"Id %s was not in the peers cache?!\n", key);
+		return;
+	}
+
+	purple_request_file((void*)key,
+			_("PEM X.509 Certificate Export"),
+			"certificate.pem", TRUE,
+			G_CALLBACK(tls_peers_mgmt_export_ok_cb),
+			G_CALLBACK(purple_certificate_destroy),
+			NULL, NULL, NULL,
+			crt);
+}
+
+/* Show information about a cert */
+static void
+info_cert_cb(GntWidget *button, gpointer null)
+{
+	const char *key;
+	PurpleCertificate *crt;
+	gchar *subject;
+	GByteArray *fpr_sha1;
+	gchar *fpr_sha1_asc;
+	gchar *primary, *secondary;
+
+	if (!certmgr.window)
+		return;
+
+	key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree));
+	if (!key)
+		return;
+
+	crt = purple_certificate_pool_retrieve(certmgr.pool, key);
+	g_return_if_fail(crt);
+
+	primary = g_strdup_printf(_("Certificate for %s"), key);
+
+	fpr_sha1 = purple_certificate_get_fingerprint_sha1(crt);
+	fpr_sha1_asc = purple_base16_encode_chunked(fpr_sha1->data,
+						    fpr_sha1->len);
+	subject = purple_certificate_get_subject_name(crt);
+
+	secondary = g_strdup_printf(_("Common name: %s\n\nSHA1 fingerprint:\n%s"), subject, fpr_sha1_asc);
+	
+	purple_notify_info(NULL,
+			   _("SSL Host Certificate"), primary, secondary);
+
+	g_free(primary);
+	g_free(secondary);
+	g_byte_array_free(fpr_sha1, TRUE);
+	g_free(fpr_sha1_asc);
+	g_free(subject);
+	purple_certificate_destroy(crt);
+}
+
+/* Delete a cert */
+static void
+tls_peers_mgmt_delete_confirm_cb(gchar *id, gint dontcare)
+{
+	if (!purple_certificate_pool_delete(certmgr.pool, id)) {
+		purple_debug_warning("gntcertmgr/tls_peers_mgmt",
+				"Deletion failed on id %s\n", id);
+	};
+
+	g_free(id);
+}
+
+static void
+delete_cert_cb(GntWidget *button, gpointer null)
+{
+	gchar *primary;
+	const char *key;
+
+	if (!certmgr.window)
+		return;
+
+	key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree));
+	if (!key)
+		return;
+
+	primary = g_strdup_printf(_("Really delete certificate for %s?"), key);
+
+	purple_request_close_with_handle((void *)key);
+	purple_request_yes_no((void *)key, _("Confirm certificate delete"),
+			primary, NULL,
+			2,
+			NULL, NULL, NULL,
+			g_strdup(key),
+			tls_peers_mgmt_delete_confirm_cb,
+			g_free);
+
+	g_free(primary);
+}
+
+/* populate the list */
+static void
+populate_cert_list()
+{
+	GList *idlist, *l;
+
+	if (!certmgr.window)
+		return;
+
+	gnt_tree_remove_all(GNT_TREE(certmgr.tree));
+
+	idlist = purple_certificate_pool_get_idlist(purple_certificate_find_pool("x509", "tls_peers"));
+	for (l = idlist; l; l = l->next) {
+		gnt_tree_add_row_last(GNT_TREE(certmgr.tree), g_strdup(l->data),
+				gnt_tree_create_row(GNT_TREE(certmgr.tree), l->data), NULL);
+	}
+	purple_certificate_pool_destroy_idlist(idlist);
+}
+
+static void
+cert_list_added(PurpleCertificatePool *pool, const char *id, gpointer null)
+{
+	g_return_if_fail(certmgr.window);
+	gnt_tree_add_row_last(GNT_TREE(certmgr.tree), g_strdup(id),
+			gnt_tree_create_row(GNT_TREE(certmgr.tree), id), NULL);
+}
+
+static void
+cert_list_removed(PurpleCertificatePool *pool, const char *id, gpointer null)
+{
+	g_return_if_fail(certmgr.window);
+	purple_request_close_with_handle((void*)id);
+	gnt_tree_remove(GNT_TREE(certmgr.tree), (void*)id);
+}
+
+void finch_certmgr_show(void)
+{
+	GntWidget *win, *tree, *box, *button;
+	PurpleCertificatePool *pool;
+
+	if (certmgr.window) {
+		gnt_window_present(certmgr.window);
+		return;
+	}
+
+	certmgr.window = win = gnt_vwindow_new(FALSE);
+	gnt_box_set_title(GNT_BOX(win), _("Certificate Manager"));
+	gnt_box_set_pad(GNT_BOX(win), 0);
+
+	certmgr.tree = tree = gnt_tree_new();
+	gnt_tree_set_hash_fns(GNT_TREE(tree), g_str_hash, g_str_equal, g_free);
+	gnt_tree_set_column_title(GNT_TREE(tree), 0, _("Hostname"));
+	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
+
+	gnt_box_add_widget(GNT_BOX(win), tree);
+
+	box = gnt_hbox_new(FALSE);
+	gnt_box_add_widget(GNT_BOX(win), box);
+
+	button = gnt_button_new(_("Add"));
+	gnt_box_add_widget(GNT_BOX(box), button);
+	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(add_cert_cb), NULL);
+	gnt_util_set_trigger_widget(GNT_WIDGET(tree), GNT_KEY_INS, button);
+
+	button = gnt_button_new(_("Save"));
+	gnt_box_add_widget(GNT_BOX(box), button);
+	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_cert_cb), NULL);
+
+	button = gnt_button_new(_("Info"));
+	gnt_box_add_widget(GNT_BOX(box), button);
+	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(info_cert_cb), NULL);
+
+	button = gnt_button_new(_("Delete"));
+	gnt_box_add_widget(GNT_BOX(box), button);
+	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(delete_cert_cb), NULL);
+	gnt_util_set_trigger_widget(GNT_WIDGET(tree), GNT_KEY_DEL, button);
+
+	button = gnt_button_new(_("Close"));
+	gnt_box_add_widget(GNT_BOX(box), button);
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), win);
+
+	g_signal_connect_swapped(G_OBJECT(win), "destroy", G_CALLBACK(g_nullify_pointer), &certmgr.window);
+
+	populate_cert_list();
+
+	pool = certmgr.pool = purple_certificate_find_pool("x509", "tls_peers");
+	purple_signal_connect(pool, "certificate-stored",
+			      win, PURPLE_CALLBACK(cert_list_added), NULL);
+	purple_signal_connect(pool, "certificate-deleted",
+			      win, PURPLE_CALLBACK(cert_list_removed), NULL);
+	g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(purple_signals_disconnect_by_handle), NULL);
+
+	gnt_widget_show(certmgr.window);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/gntcertmgr.h	Fri Aug 31 16:10:18 2007 +0000
@@ -0,0 +1,31 @@
+/**
+ * @file gntcertmgr.h GNT Certificate Manager API
+ * @ingroup finch
+ *
+ * finch
+ *
+ * Finch is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#ifndef _GNT_CERTMGR_H
+#define _GNT_CERTMGR_H
+
+void finch_certmgr_show(void);
+
+#endif
--- a/finch/gntconv.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/finch/gntconv.c	Fri Aug 31 16:10:18 2007 +0000
@@ -1006,6 +1006,7 @@
 {
 	FinchConv *ggconv = conv->ui_data;
 	gnt_text_view_clear(GNT_TEXT_VIEW(ggconv->tv));
+	purple_conversation_clear_message_history(conv);
 	return PURPLE_CMD_STATUS_OK;
 }
 
--- a/finch/gntrequest.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/finch/gntrequest.c	Fri Aug 31 16:10:18 2007 +0000
@@ -198,7 +198,7 @@
 
 static void *
 finch_request_choice(const char *title, const char *primary,
-		const char *secondary, unsigned int default_value,
+		const char *secondary, int default_value,
 		const char *ok_text, GCallback ok_cb,
 		const char *cancel_text, GCallback cancel_cb,
 		PurpleAccount *account, const char *who, PurpleConversation *conv,
@@ -244,7 +244,7 @@
 
 static void*
 finch_request_action(const char *title, const char *primary,
-		const char *secondary, unsigned int default_value,
+		const char *secondary, int default_value,
 		PurpleAccount *account, const char *who, PurpleConversation *conv,
 		void *user_data, size_t actioncount,
 		va_list actions)
--- a/finch/gntui.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/finch/gntui.c	Fri Aug 31 16:10:18 2007 +0000
@@ -25,6 +25,7 @@
 
 #include "gntaccount.h"
 #include "gntblist.h"
+#include "gntcertmgr.h"
 #include "gntconn.h"
 #include "gntconv.h"
 #include "gntdebug.h"
@@ -81,6 +82,7 @@
 	gnt_register_action(_("Accounts"), finch_accounts_show_all);
 	gnt_register_action(_("Buddy List"), finch_blist_show);
 	gnt_register_action(_("Buddy Pounces"), finch_pounces_manager_show);
+	gnt_register_action(_("Certificates"), finch_certmgr_show);
 	gnt_register_action(_("Debug Window"), finch_debug_window_show);
 	gnt_register_action(_("File Transfers"), finch_xfer_dialog_show);
 	gnt_register_action(_("Plugins"), finch_plugins_show_all);
--- a/finch/libgnt/pygnt/dbus-gnt	Tue Aug 28 09:20:27 2007 +0000
+++ b/finch/libgnt/pygnt/dbus-gnt	Fri Aug 31 16:10:18 2007 +0000
@@ -13,7 +13,7 @@
 import gnt
 import sys
 
-from time import strftime
+import time
 
 convwins = {}
 
@@ -27,19 +27,26 @@
     # if a conv window is closed, then reopened, this thing crashes
     convwins[key] = None
 
-def wrote_msg(account, who, msg, conv, flags):
-    stuff = show_conversation(conv)
+def add_message(conv, who, msg, flags, timestamp):
+    stuff = show_conversation(conv, False)
     tv = stuff[1]
     tv.append_text_with_flags("\n", 0)
-    tv.append_text_with_flags(strftime("(%X) "), 8)
+    if timestamp:
+        tv.append_text_with_flags(time.strftime("(%X) ", time.localtime(timestamp)), 8)
+    else:
+        tv.append_text_with_flags(time.strftime("(%X) "), 8)
     if flags & 3:
         tv.append_text_with_flags(who + ": ", 1)
+        msg = purple.PurpleMarkupStripHtml(msg)
         tv.append_text_with_flags(msg, 0)
         stuff[0].set_urgent()
     else:
         tv.append_text_with_flags(msg, 8)
     tv.scroll(0)
 
+def wrote_msg(account, who, msg, conv, flags):
+    add_message(conv, who, msg, flags, None)
+
 bus = dbus.SessionBus()
 obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
 purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
@@ -79,7 +86,7 @@
 def conv_window_destroyed(win, key):
     del convwins[key]
 
-def show_conversation(conv):
+def show_conversation(conv, history):
     key = get_dict_key(conv)
     if key in convwins:
         return convwins[key]
@@ -96,9 +103,21 @@
     vbox.add_widget(entry)
     entry.connect("key_pressed", send_im_cb, conv)
     tv.clear()
+    tv.attach_scroll_widget(entry)
     win.show()
     convwins[key] = [win, tv, entry]
     win.connect("destroy", conv_window_destroyed, key)
+    
+    if history:
+        msgs = purple.PurpleConversationGetMessageHistory(conv)
+        msgs.reverse()
+        for msg in msgs:
+            who = purple.PurpleConversationMessageGetSender(msg)
+            what = purple.PurpleConversationMessageGetMessage(msg)
+            flags = purple.PurpleConversationMessageGetFlags(msg)
+            when = purple.PurpleConversationMessageGetTimestamp(msg)
+            add_message(conv, who, what, flags, when)
+
     return convwins[key]
 
 def show_buddylist():
@@ -127,7 +146,7 @@
 
 convs = purple.PurpleGetConversations()
 for conv in convs:
-    show_conversation(conv)
+    show_conversation(conv, True)
 
 gnt.gnt_main()
 
--- a/finch/libgnt/pygnt/gendef.sh	Tue Aug 28 09:20:27 2007 +0000
+++ b/finch/libgnt/pygnt/gendef.sh	Fri Aug 31 16:10:18 2007 +0000
@@ -31,7 +31,9 @@
 rm -f gnt.def
 for file in $FILES
 do
+	echo -n "Generating definitions for ${file} ... "
 	python /usr/share/pygtk/2.0/codegen/h2def.py ../$file >> gnt.def
+	echo "Done"
 done
 
 # Remove the definitions about the enums
--- a/libpurple/Makefile.am	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/Makefile.am	Fri Aug 31 16:10:18 2007 +0000
@@ -22,9 +22,9 @@
 		win32/giowin32.c \
 		win32/win32dep.h
 
-# if USE_GCONFTOOL
-# GCONF_DIR=gconf
-# endif
+if USE_GCONFTOOL
+GCONF_DIR=gconf
+endif
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = purple.pc
--- a/libpurple/certificate.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/certificate.c	Fri Aug 31 16:10:18 2007 +0000
@@ -30,6 +30,7 @@
 
 #include "internal.h"
 #include "certificate.h"
+#include "dbus-maybe.h"
 #include "debug.h"
 #include "request.h"
 #include "signals.h"
@@ -1124,6 +1125,18 @@
 	}
 }
 
+static void
+x509_tls_cached_user_auth_accept_cb(x509_tls_cached_ua_ctx *c, gint ignore)
+{
+	x509_tls_cached_user_auth_cb(c, 2);
+}
+
+static void
+x509_tls_cached_user_auth_reject_cb(x509_tls_cached_ua_ctx *c, gint ignore)
+{
+	x509_tls_cached_user_auth_cb(c, 1);
+}
+
 /** Validates a certificate by asking the user
  * @param reason    String to explain why the user needs to accept/refuse the
  *                  certificate.
@@ -1151,8 +1164,8 @@
 		NULL,         /* No associated conversation */
 		x509_tls_cached_ua_ctx_new(vrq, reason),
 		3,            /* Number of actions */
-		_("Yes"), x509_tls_cached_user_auth_cb,
-		_("No"),  x509_tls_cached_user_auth_cb,
+		_("Accept"), x509_tls_cached_user_auth_accept_cb,
+		_("Reject"),  x509_tls_cached_user_auth_reject_cb,
 		_("_View Certificate..."), x509_tls_cached_show_cert);
 	
 	/* Cleanup */
@@ -1192,7 +1205,14 @@
 	/* Load up the cached certificate */
 	cached_crt = purple_certificate_pool_retrieve(
 		tls_peers, vrq->subject_name);
-	g_assert(cached_crt);
+	if ( !cached_crt ) {
+		purple_debug_error("certificate/x509/tls_cached",
+				   "Lookup failed on cached certificate!\n"
+				   "It was here just a second ago. Forwarding "
+				   "to cert_changed.\n");
+		/* vrq now becomes the problem of cert_changed */
+		x509_tls_cached_peer_cert_changed(vrq);
+	}
 
 	/* Now get SHA1 sums for both and compare them */
 	/* TODO: This is not an elegant way to compare certs */
@@ -1325,7 +1345,14 @@
 
 	ca_crt = purple_certificate_pool_retrieve(ca, ca_id);
 	g_free(ca_id);
-	g_assert(ca_crt);
+	if (!ca_crt) {
+		purple_debug_error("certificate/x509/tls_cached",
+				   "Certificate authority disappeared out "
+				   "underneath me!\n");
+		purple_certificate_verify_complete(vrq,
+						   PURPLE_CERTIFICATE_INVALID);
+		return;
+	}
 	
 	/* Check the signature */
 	if ( !purple_certificate_signed_by(end_crt, ca_crt) ) {
@@ -1362,9 +1389,11 @@
 						 "tls_peers");
 
 	if (tls_peers) {
-		g_assert(purple_certificate_pool_store(tls_peers,
-						       vrq->subject_name,
-						       peer_crt) );
+		if (!purple_certificate_pool_store(tls_peers,vrq->subject_name,
+						   peer_crt) ) {
+			purple_debug_error("certificate/x509/tls_cached",
+					   "FAILED to cache peer certificate\n");
+		}
 	} else {
 		purple_debug_error("certificate/x509/tls_cached",
 				   "Unable to locate tls_peers certificate "
@@ -1700,6 +1729,7 @@
 
 		/* TODO: Emit a signal that the pool got registered */
 
+		PURPLE_DBUS_REGISTER_POINTER(pool, PurpleCertificatePool);
 		purple_signal_register(pool, /* Signals emitted from pool */
 				       "certificate-stored",
 				       purple_marshal_VOID__POINTER_POINTER,
@@ -1748,6 +1778,7 @@
 	}
 
 	/* Uninit the pool if needed */
+	PURPLE_DBUS_UNREGISTER_POINTER(pool);
 	if (pool->uninit) {
 		pool->uninit();
 	}
@@ -1775,7 +1806,6 @@
 	GByteArray *sha_bin;
 	gchar *cn;
 	time_t activation, expiration;
-	/* Length of these buffers is dictated by 'man ctime_r' */
 	gchar *activ_str, *expir_str;
 	gchar *secondary;
 
@@ -1792,7 +1822,11 @@
 	/* Get the certificate times */
 	/* TODO: Check the times against localtime */
 	/* TODO: errorcheck? */
-	g_assert(purple_certificate_get_times(crt, &activation, &expiration));
+	if (!purple_certificate_get_times(crt, &activation, &expiration)) {
+		purple_debug_error("certificate",
+				   "Failed to get certificate times!\n");
+		activation = expiration = 0;
+	}
 	activ_str = g_strdup(ctime(&activation));
 	expir_str = g_strdup(ctime(&expiration));
 
@@ -1819,6 +1853,3 @@
 	g_byte_array_free(sha_bin, TRUE);
 }
 
-
-
-
--- a/libpurple/conversation.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/conversation.c	Fri Aug 31 16:10:18 2007 +0000
@@ -39,6 +39,7 @@
 static GList *ims = NULL;
 static GList *chats = NULL;
 static PurpleConversationUiOps *default_ops = NULL;
+static GHashTable *histories = NULL;
 
 void
 purple_conversations_set_ui_ops(PurpleConversationUiOps *ops)
@@ -111,17 +112,17 @@
 
 	/* Always linkfy the text for display, unless we're
 	 * explicitly asked to do otheriwse*/
-	if(msgflags & PURPLE_MESSAGE_NO_LINKIFY)
-		displayed = g_strdup(message);
-	else
-		displayed = purple_markup_linkify(message);
-
-	if ((conv->features & PURPLE_CONNECTION_HTML) &&
-		!(msgflags & PURPLE_MESSAGE_RAW))
-	{
+	if (!(msgflags & PURPLE_MESSAGE_INVISIBLE)) {
+		if(msgflags & PURPLE_MESSAGE_NO_LINKIFY)
+			displayed = g_strdup(message);
+		else
+			displayed = purple_markup_linkify(message);
+	}
+
+	if (displayed && (conv->features & PURPLE_CONNECTION_HTML) &&
+		!(msgflags & PURPLE_MESSAGE_RAW)) {
 		sent = g_strdup(displayed);
-	}
-	else
+	} else
 		sent = g_strdup(message);
 
 	msgflags |= PURPLE_MESSAGE_SEND;
@@ -202,6 +203,51 @@
 							   conv, time(NULL), NULL));
 }
 
+/* Functions that deal with PurpleConvMessage */
+
+static void
+add_message_to_history(PurpleConversation *conv, const char *who, const char *message,
+		PurpleMessageFlags flags, time_t when)
+{
+	GList *list;
+	PurpleConvMessage *msg;
+
+	if (flags & PURPLE_MESSAGE_SEND) {
+		const char *me = NULL;
+		if (conv->account->gc)
+			me = conv->account->gc->display_name;
+		if (!me)
+			me = conv->account->username;
+		who = me;
+	}
+	
+	msg = g_new0(PurpleConvMessage, 1);
+	PURPLE_DBUS_REGISTER_POINTER(msg, PurpleConvMessage);
+	msg->who = g_strdup(who);
+	msg->flags = flags;
+	msg->what = g_strdup(message);
+	msg->when = when;
+
+	list = g_hash_table_lookup(histories, conv);
+	list = g_list_prepend(list, msg);
+	g_hash_table_insert(histories, conv, list);
+}
+
+static void
+free_conv_message(PurpleConvMessage *msg)
+{
+	g_free(msg->who);
+	g_free(msg->what);
+	PURPLE_DBUS_UNREGISTER_POINTER(msg);
+	g_free(msg);
+}
+
+static void
+message_history_free(GList *list)
+{
+	g_list_foreach(list, (GFunc)free_conv_message, NULL);
+	g_list_free(list);
+}
 
 /**************************************************************************
  * Conversation API
@@ -473,6 +519,8 @@
 
 	purple_conversation_close_logs(conv);
 
+	purple_conversation_clear_message_history(conv);
+
 	PURPLE_DBUS_UNREGISTER_POINTER(conv);
 	g_free(conv);
 	conv = NULL;
@@ -805,9 +853,6 @@
 
 	ops = purple_conversation_get_ui_ops(conv);
 
-	if (ops == NULL || ops->write_conv == NULL)
-		return;
-
 	account = purple_conversation_get_account(conv);
 	type = purple_conversation_get_type(conv);
 
@@ -891,7 +936,9 @@
 		}
 	}
 
-	ops->write_conv(conv, who, alias, displayed, flags, mtime);
+	if (ops && ops->write_conv)
+		ops->write_conv(conv, who, alias, displayed, flags, mtime);
+	add_message_to_history(conv, who, message, flags, mtime);
 
 	purple_signal_emit(purple_conversations_get_handle(),
 		(type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"),
@@ -2030,6 +2077,42 @@
 	return menu;
 }
 
+void purple_conversation_clear_message_history(PurpleConversation *conv)
+{
+	GList *list = g_hash_table_lookup(histories, conv);
+	message_history_free(list);
+	g_hash_table_remove(histories, conv);
+}
+
+GList *purple_conversation_get_message_history(PurpleConversation *conv)
+{
+	return g_hash_table_lookup(histories, conv);
+}
+
+const char *purple_conversation_message_get_sender(PurpleConvMessage *msg)
+{
+	g_return_val_if_fail(msg, NULL);
+	return msg->who;
+}
+
+const char *purple_conversation_message_get_message(PurpleConvMessage *msg)
+{
+	g_return_val_if_fail(msg, NULL);
+	return msg->what;
+}
+
+PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg)
+{
+	g_return_val_if_fail(msg, 0);
+	return msg->flags;
+}
+
+time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg)
+{
+	g_return_val_if_fail(msg, 0);
+	return msg->when;
+}
+
 gboolean
 purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
 				const gchar *markup, gchar **error)
@@ -2310,6 +2393,9 @@
 			     purple_value_new(PURPLE_TYPE_SUBTYPE,
 					    PURPLE_SUBTYPE_CONVERSATION),
 			     purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
+
+	/* Initialize the history */
+	histories = g_hash_table_new(g_direct_hash, g_direct_equal);
 }
 
 void
@@ -2318,4 +2404,6 @@
 	while (conversations)
 		purple_conversation_destroy((PurpleConversation*)conversations->data);
 	purple_signals_unregister_by_instance(purple_conversations_get_handle());
+	g_hash_table_destroy(histories);
+	histories = NULL;
 }
--- a/libpurple/conversation.h	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/conversation.h	Fri Aug 31 16:10:18 2007 +0000
@@ -37,6 +37,7 @@
 typedef struct _PurpleConvIm            PurpleConvIm;
 typedef struct _PurpleConvChat          PurpleConvChat;
 typedef struct _PurpleConvChatBuddy     PurpleConvChatBuddy;
+typedef struct _PurpleConvMessage       PurpleConvMessage;
 
 /**
  * A type of conversation.
@@ -117,9 +118,9 @@
 	                                        apply formatting         */
 	PURPLE_MESSAGE_IMAGES      = 0x1000, /**< Message contains images  */
 	PURPLE_MESSAGE_NOTIFY      = 0x2000, /**< Message is a notification */
-	PURPLE_MESSAGE_NO_LINKIFY  = 0x4000  /**< Message should not be auto-
+	PURPLE_MESSAGE_NO_LINKIFY  = 0x4000, /**< Message should not be auto-
 										   linkified */
-
+	PURPLE_MESSAGE_INVISIBLE   = 0x8000, /**< Message should not be displayed */
 } PurpleMessageFlags;
 
 /**
@@ -283,6 +284,17 @@
 };
 
 /**
+ * Description of a conversation message
+ */
+struct _PurpleConvMessage
+{
+	char *who;
+	char *what;
+	PurpleMessageFlags flags;
+	time_t when;
+};
+
+/**
  * A core representation of a conversation between two or more people.
  *
  * The conversation can be an IM or a chat.
@@ -650,6 +662,60 @@
  */
 void purple_conversation_foreach(void (*func)(PurpleConversation *conv));
 
+/**
+ * Retrieve the message history of a conversation.
+ *
+ * @param conv   The conversation
+ *
+ * @return  A GList of PurpleConvMessage's. The must not modify the list or the data within.
+ *          The list contains the newest message at the beginning, and the oldest message at
+ *          the end.
+ */
+GList *purple_conversation_get_message_history(PurpleConversation *conv);
+
+/**
+ * Clear the message history of a conversation.
+ *
+ * @param conv  The conversation
+ */
+void purple_conversation_clear_message_history(PurpleConversation *conv);
+
+/**
+ * Get the sender from a PurpleConvMessage
+ *
+ * @param msg   A PurpleConvMessage
+ *
+ * @return   The name of the sender of the message
+ */
+const char *purple_conversation_message_get_sender(PurpleConvMessage *msg);
+
+/**
+ * Get the message from a PurpleConvMessage
+ *
+ * @param msg   A PurpleConvMessage
+ *
+ * @return   The name of the sender of the message
+ */
+const char *purple_conversation_message_get_message(PurpleConvMessage *msg);
+
+/**
+ * Get the message-flags of a PurpleConvMessage
+ *
+ * @param msg   A PurpleConvMessage
+ *
+ * @return   The name of the sender of the message
+ */
+PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg);
+
+/**
+ * Get the timestamp of a PurpleConvMessage
+ *
+ * @param msg   A PurpleConvMessage
+ *
+ * @return   The name of the sender of the message
+ */
+time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg);
+
 /*@}*/
 
 
--- a/libpurple/dbus-analyze-functions.py	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/dbus-analyze-functions.py	Fri Aug 31 16:10:18 2007 +0000
@@ -66,6 +66,7 @@
     "purple_savedstatuses_get_all",
     "purple_status_type_get_attrs",
     "purple_presence_get_statuses",
+    "purple_conversation_get_message_history",
 ]
 
 pointer = "#pointer#"
--- a/libpurple/example/nullclient.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/example/nullclient.c	Fri Aug 31 16:10:18 2007 +0000
@@ -197,7 +197,7 @@
 	purple_util_set_user_dir(CUSTOM_USER_DIRECTORY);
 
 	/* We do not want any debugging for now to keep the noise to a minimum. */
-	purple_debug_set_enabled(TRUE);
+	purple_debug_set_enabled(FALSE);
 
 	/* Set the core-uiops, which is used to
 	 * 	- initialize the ui specific preferences.
@@ -257,24 +257,6 @@
 				PURPLE_CALLBACK(signed_on), NULL);
 }
 
-
-
-
-void signedOn( PurpleConnection *gc, gpointer dummy ) { 
-
-    
-    if( gc ) {
-
-        PurpleAccount* a = purple_connection_get_account( gc );
-
-        if( a ) {
-            
-            purple_presence_set_idle( purple_account_get_presence( a ), TRUE, time( NULL ) );
-        }
-    }    
-}
-
-
 int main()
 {
 	GList *iter;
@@ -300,26 +282,30 @@
 			names = g_list_append(names, info->id);
 		}
 	}
+	printf("Select the protocol [0-%d]: ", i-1);
+	fgets(name, sizeof(name), stdin);
+	sscanf(name, "%d", &num);
+	prpl = g_list_nth_data(names, num);
+
+	printf("Username: ");
+	fgets(name, sizeof(name), stdin);
+	name[strlen(name) - 1] = 0;  /* strip the \n at the end */
 
 	/* Create the account */
-	account = purple_account_new("msimprpl@xyzzy.cjb.net", "prpl-myspace" );
-	purple_account_set_password(account, "4224jc" );
+	account = purple_account_new(name, prpl);
+
+	/* Get the password for the account */
+	password = getpass("Password: ");
+	purple_account_set_password(account, password);
 
 	/* It's necessary to enable the account first. */
 	purple_account_set_enabled(account, UI_ID, TRUE);
 
-#if 0 
-	static int handle;    
-	purple_signal_connect( purple_connections_get_handle(), 
-                           "signed-on", &handle,
-                           PURPLE_CALLBACK( signedOn ), 
-                           NULL );
-    
 	/* Now, to connect the account(s), create a status and activate it. */
-	purple_savedstatus_activate( purple_savedstatus_get_default() );
+	status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE);
+	purple_savedstatus_activate(status);
 
 	connect_to_signals_for_demonstration_purposes_only();
-#endif
 
 	g_main_loop_run(loop);
 
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Fri Aug 31 16:10:18 2007 +0000
@@ -60,7 +60,7 @@
 		(gnutls_realloc_function) g_realloc, /* realloc */
 		(gnutls_free_function)    g_free     /* free */
 		);
-	
+
 	gnutls_global_init();
 
 	gnutls_certificate_allocate_credentials(&xcred);
@@ -73,7 +73,7 @@
 static gboolean
 ssl_gnutls_init(void)
 {
-   return TRUE;
+	return TRUE;
 }
 
 static void
@@ -130,17 +130,18 @@
 
 		purple_ssl_close(gsc);
 	} else {
-		purple_debug_info("gnutls", "Handshake complete\n");
-
-		/* TODO: Remove all this debugging babble */
 		/* Now we are cooking with gas! */
 		PurpleSslOps *ops = purple_ssl_get_ops();
 		GList * peers = ops->get_peer_certificates(gsc);
-		
+
 		PurpleCertificateScheme *x509 =
 			purple_certificate_find_scheme("x509");
 
 		GList * l;
+
+		/* TODO: Remove all this debugging babble */
+		purple_debug_info("gnutls", "Handshake complete\n");
+
 		for (l=peers; l; l = l->next) {
 			PurpleCertificate *crt = l->data;
 			GByteArray *z =
@@ -155,72 +156,71 @@
 
 			/* Kill the cert! */
 			x509->destroy_certificate(crt);
-			
+
 			g_free(fpr);
 			g_byte_array_free(z, TRUE);
 		}
 		g_list_free(peers);
-		
+
 		{
-		  const gnutls_datum_t *cert_list;
-		  unsigned int cert_list_size = 0;
-		  gnutls_session_t session=gnutls_data->session;
-		  
-		  cert_list =
-		    gnutls_certificate_get_peers(session, &cert_list_size);
-		  
-		  purple_debug_info("gnutls",
-				    "Peer provided %d certs\n",
-				    cert_list_size);
-		  int i;
-		  for (i=0; i<cert_list_size; i++)
-		    {
-		      gchar fpr_bin[256];
-		      gsize fpr_bin_sz = sizeof(fpr_bin);
-		      gchar * fpr_asc = NULL;
-		      gchar tbuf[256];
-		      gsize tsz=sizeof(tbuf);
-		      gchar * tasc = NULL;
-		      gnutls_x509_crt_t cert;
-		      
-		      gnutls_x509_crt_init(&cert);
-		      gnutls_x509_crt_import (cert, &cert_list[i],
-					      GNUTLS_X509_FMT_DER);
-		      
-		      gnutls_x509_crt_get_fingerprint(cert, GNUTLS_MAC_SHA,
-						      fpr_bin, &fpr_bin_sz);
-		      
-		      fpr_asc =
-			purple_base16_encode_chunked(fpr_bin,fpr_bin_sz);
-		      
-		      purple_debug_info("gnutls", 
-					"Lvl %d SHA1 fingerprint: %s\n",
-					i, fpr_asc);
-		      
-		      tsz=sizeof(tbuf);
-		      gnutls_x509_crt_get_serial(cert,tbuf,&tsz);
-		      tasc=
-			purple_base16_encode_chunked(tbuf, tsz);
-		      purple_debug_info("gnutls",
-					"Serial: %s\n",
-					tasc);
-		      g_free(tasc);
+			const gnutls_datum_t *cert_list;
+			unsigned int cert_list_size = 0;
+			gnutls_session_t session=gnutls_data->session;
+			int i;
+
+			cert_list =
+				gnutls_certificate_get_peers(session, &cert_list_size);
+
+			purple_debug_info("gnutls",
+					    "Peer provided %d certs\n",
+					    cert_list_size);
+			for (i=0; i<cert_list_size; i++)
+			{
+				gchar fpr_bin[256];
+				gsize fpr_bin_sz = sizeof(fpr_bin);
+				gchar * fpr_asc = NULL;
+				gchar tbuf[256];
+				gsize tsz=sizeof(tbuf);
+				gchar * tasc = NULL;
+				gnutls_x509_crt_t cert;
+
+				gnutls_x509_crt_init(&cert);
+				gnutls_x509_crt_import (cert, &cert_list[i],
+						GNUTLS_X509_FMT_DER);
+
+				gnutls_x509_crt_get_fingerprint(cert, GNUTLS_MAC_SHA,
+						fpr_bin, &fpr_bin_sz);
 
-		      tsz=sizeof(tbuf);
-		      gnutls_x509_crt_get_dn (cert, tbuf, &tsz);
-		      purple_debug_info("gnutls",
-					"Cert DN: %s\n",
-					tbuf);
-		      tsz=sizeof(tbuf);
-		      gnutls_x509_crt_get_issuer_dn (cert, tbuf, &tsz);
-		      purple_debug_info("gnutls",
-					"Cert Issuer DN: %s\n",
-					tbuf);
+				fpr_asc =
+						purple_base16_encode_chunked((const guchar *)fpr_bin, fpr_bin_sz);
+
+				purple_debug_info("gnutls",
+						"Lvl %d SHA1 fingerprint: %s\n",
+						i, fpr_asc);
+
+				tsz=sizeof(tbuf);
+				gnutls_x509_crt_get_serial(cert,tbuf,&tsz);
+				tasc=purple_base16_encode_chunked((const guchar *)tbuf, tsz);
+				purple_debug_info("gnutls",
+						"Serial: %s\n",
+						tasc);
+				g_free(tasc);
 
-		      g_free(fpr_asc); fpr_asc = NULL;
-		      gnutls_x509_crt_deinit(cert);
-		    }
-		  
+				tsz=sizeof(tbuf);
+				gnutls_x509_crt_get_dn (cert, tbuf, &tsz);
+				purple_debug_info("gnutls",
+						"Cert DN: %s\n",
+						tbuf);
+				tsz=sizeof(tbuf);
+				gnutls_x509_crt_get_issuer_dn (cert, tbuf, &tsz);
+				purple_debug_info("gnutls",
+						"Cert Issuer DN: %s\n",
+						tbuf);
+
+				g_free(fpr_asc);
+				fpr_asc = NULL;
+				gnutls_x509_crt_deinit(cert);
+			}
 		}
 
 		/* TODO: The following logic should really be in libpurple */
@@ -378,7 +378,7 @@
 	unsigned int cert_list_size = 0;
 
 	unsigned int i;
-	
+
 	/* This should never, ever happen. */
 	g_return_val_if_fail( gnutls_certificate_type_get (gnutls_data->session) == GNUTLS_CRT_X509, NULL);
 
@@ -426,12 +426,14 @@
 static void
 x509_crtdata_delref(x509_crtdata_t *cd)
 {
-	g_assert(cd->refcount > 0);
-	
 	(cd->refcount)--;
 
+	if (cd->refcount < 0)
+		g_critical("Refcount of x509_crtdata_t is %d, which is less "
+				"than zero!\n", cd->refcount);
+
 	/* If the refcount reaches zero, kill the structure */
-	if (cd->refcount == 0) {
+	if (cd->refcount <= 0) {
 		purple_debug_info("gnutls/x509",
 				  "Freeing unused cert data at %p\n",
 				  cd);
@@ -466,11 +468,11 @@
 	certdat = g_new0(x509_crtdata_t, 1);
 	gnutls_x509_crt_init(&(certdat->crt));
 	certdat->refcount = 0;
-	
+
 	/* Perform the actual certificate parse */
 	/* Yes, certdat->crt should be passed as-is */
 	gnutls_x509_crt_import(certdat->crt, &dt, mode);
-	
+
 	/* Allocate the certificate and load it with data */
 	crt = g_new0(PurpleCertificate, 1);
 	crt->scheme = &x509_gnutls;
@@ -495,7 +497,7 @@
 	purple_debug_info("gnutls",
 			  "Attempting to load X.509 certificate from %s\n",
 			  filename);
-	
+
 	/* Next, we'll simply yank the entire contents of the file
 	   into memory */
 	/* TODO: Should I worry about very large files here? */
@@ -506,7 +508,7 @@
 			    NULL      /* No error checking for now */
 		),
 		NULL);
-	
+
 	/* Load the datum struct */
 	dt.data = (unsigned char *) buf;
 	dt.size = buf_sz;
@@ -514,7 +516,7 @@
 	/* Perform the conversion */
 	crt = x509_import_from_datum(dt,
 				     GNUTLS_X509_FMT_PEM); // files should be in PEM format
-	
+
 	/* Cleanup */
 	g_free(buf);
 
@@ -571,7 +573,6 @@
 	success = purple_util_write_data_to_file_absolute(filename,
 							  out_buf, out_size);
 
-	
 	g_free(out_buf);
 	g_return_val_if_fail(success, FALSE);
 	return success;
@@ -596,10 +597,10 @@
 }
 /** Frees a Certificate
  *
- *  Destroys a Certificate's internal data structures and frees the pointer
- *  given.
- *  @param crt  Certificate instance to be destroyed. It WILL NOT be destroyed
- *              if it is not of the correct CertificateScheme. Can be NULL
+ * Destroys a Certificate's internal data structures and frees the pointer
+ * given.
+ * @param crt Certificate instance to be destroyed. It WILL NOT be destroyed
+ *            if it is not of the correct CertificateScheme. Can be NULL
  *
  */
 static void
@@ -622,7 +623,7 @@
 	/* Use the reference counting system to free (or not) the
 	   underlying data */
 	x509_crtdata_delref((x509_crtdata_t *)crt->data);
-	
+
 	/* Kill the structure itself */
 	g_free(crt);
 }
@@ -643,7 +644,7 @@
 	gnutls_x509_crt_t issuer_dat;
 	unsigned int verify; /* used to store result from GnuTLS verifier */
 	int ret;
-	
+
 	g_return_val_if_fail(crt, FALSE);
 	g_return_val_if_fail(issuer, FALSE);
 
@@ -685,7 +686,7 @@
 		/* The issuer is not correct, or there were errors */
 		return FALSE;
 	}
-	
+
 	/* Now, check the signature */
 	/* The second argument is a ptr to an array of "trusted" issuer certs,
 	   but we're only using one trusted one */
@@ -696,7 +697,7 @@
 					current standard) */
 				     GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
 				     &verify);
-	
+
 	if (ret != 0) {
 		purple_debug_error("gnutls/x509",
 				   "Attempted certificate verification caused a GnuTLS error code %d. I will just say the signature is bad, but you should look into this.\n", ret);
@@ -713,7 +714,7 @@
 				  issuer_id, crt_id);
 		g_free(crt_id);
 		g_free(issuer_id);
-		
+
 		return FALSE;
 	} /* if (ret, etc.) */
 
@@ -742,7 +743,7 @@
 
 	/* This shouldn't happen */
 	g_return_val_if_fail(tmpsz == hashlen, NULL);
-	
+
 	/* Okay, now create and fill hash array */
 	hash = g_byte_array_new();
 	g_byte_array_append(hash, hashbuf, hashlen);
@@ -776,7 +777,7 @@
 		g_free(dn);
 		return NULL;
 	}
-	
+
 	return dn;
 }
 
@@ -807,7 +808,7 @@
 		g_free(dn);
 		return NULL;
 	}
-	
+
 	return dn;
 }
 
@@ -848,7 +849,6 @@
 		return NULL;
 	}
 
-	
 	return cn;
 }
 
@@ -893,7 +893,7 @@
 	if (*activation == errval || *expiration == errval) {
 		return FALSE;
 	}
-	
+
 	return TRUE;
 }
 
--- a/libpurple/protocols/Makefile.mingw	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/protocols/Makefile.mingw	Fri Aug 31 16:10:18 2007 +0000
@@ -8,7 +8,7 @@
 PIDGIN_TREE_TOP := ../..
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
-SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc10 simple yahoo bonjour myspace
+SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc simple yahoo bonjour myspace
 
 .PHONY: all install clean
 
--- a/libpurple/protocols/bonjour/issues.txt	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/protocols/bonjour/issues.txt	Fri Aug 31 16:10:18 2007 +0000
@@ -2,6 +2,5 @@
 ============= Known issues ===============
 ==========================================
 
-* Status changes don't work
 * File transfers
 * Typing notifications
--- a/libpurple/protocols/silc/Makefile.mingw	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/protocols/silc/Makefile.mingw	Fri Aug 31 16:10:18 2007 +0000
@@ -8,8 +8,8 @@
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
 TARGET = libsilc
-NEEDED_DLLS =		$(SILC_TOOLKIT)/lib/silc.dll \
-			$(SILC_TOOLKIT)/lib/silcclient.dll
+NEEDED_DLLS =		$(SILC_TOOLKIT)/bin/libsilc-1-1-2.dll \
+			$(SILC_TOOLKIT)/bin/libsilcclient-1-1-2.dll
 TYPE = PLUGIN
 
 # Static or Plugin...
--- a/libpurple/protocols/silc/silc.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/protocols/silc/silc.c	Fri Aug 31 16:10:18 2007 +0000
@@ -1975,9 +1975,6 @@
 silc_log_set_debug_string("*client*");
 #endif
 
-#ifdef _WIN32
-	silc_net_win32_init();
-#endif
 }
 
 PURPLE_INIT_PLUGIN(silc, init_plugin, info);
--- a/libpurple/protocols/yahoo/yahoo.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Fri Aug 31 16:10:18 2007 +0000
@@ -969,7 +969,6 @@
 	PurpleConnection *gc;
 	char *id;
 	char *who;
-	char *msg;
 	int protocol;
 };
 
@@ -987,7 +986,6 @@
 
 	g_free(add_req->id);
 	g_free(add_req->who);
-	g_free(add_req->msg);
 	g_free(add_req);
 }
 
@@ -1018,7 +1016,6 @@
 
 	g_free(add_req->id);
 	g_free(add_req->who);
-	g_free(add_req->msg);
 	g_free(add_req);
 }
 
@@ -1132,10 +1129,18 @@
 			l = l->next;
 		}
 
-		if (add_req->id) {
-			char *alias = NULL;
+		if (add_req->id && add_req->who) {
+			char *alias = NULL, *dec_msg = NULL;
+
+			if (!yahoo_privacy_check(gc, add_req->who)) {
+				purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
+						  add_req->who);
+				yahoo_buddy_add_deny_cb(add_req, NULL);
+				return;
+			}
+
 			if (msg)
-				add_req->msg = yahoo_string_decode(gc, msg, FALSE);
+				dec_msg = yahoo_string_decode(gc, msg, FALSE);
 
 			if (firstname && lastname)
 				alias = g_strdup_printf("%s %s", firstname, lastname);
@@ -1144,20 +1149,19 @@
 			else if (lastname)
 				alias = g_strdup(lastname);
 
-
 			/* DONE! this is almost exactly the same as what MSN does,
 			 * this should probably be moved to the core.
 			 */
 			 purple_account_request_authorization(purple_connection_get_account(gc), add_req->who, add_req->id,
-						    alias, add_req->msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL,
+						    alias, dec_msg, purple_find_buddy(purple_connection_get_account(gc), add_req->who) != NULL,
 						    yahoo_buddy_add_authorize_cb,
 						    yahoo_buddy_add_deny_reason_cb,
 						    add_req);
 			g_free(alias);
+			g_free(dec_msg);
 		} else {
 			g_free(add_req->id);
 			g_free(add_req->who);
-			/*g_free(add_req->msg);*/
 			g_free(add_req);
 		}
 	} else {
@@ -1165,6 +1169,7 @@
 	}
 }
 
+/* I don't think this happens anymore in Version 15 */
 static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) {
 	struct yahoo_add_request *add_req;
 	char *msg = NULL;
@@ -1192,22 +1197,31 @@
 		l = l->next;
 	}
 
-	if (add_req->id) {
+	if (add_req->id && add_req->who) {
+		char *dec_msg = NULL;
+
+		if (!yahoo_privacy_check(gc, add_req->who)) {
+			purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
+					  add_req->who);
+			yahoo_buddy_add_deny_cb(add_req, NULL);
+			return;
+		}
+
 		if (msg)
-			add_req->msg = yahoo_string_decode(gc, msg, FALSE);
+			dec_msg = yahoo_string_decode(gc, msg, FALSE);
 
 		/* DONE! this is almost exactly the same as what MSN does,
 		 * this should probably be moved to the core.
 		 */
 		 purple_account_request_authorization(purple_connection_get_account(gc), add_req->who, add_req->id,
-                                                    NULL, add_req->msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL,
+                                                    NULL, dec_msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL,
 						    yahoo_buddy_add_authorize_cb,
 						    yahoo_buddy_add_deny_reason_cb,
                                                     add_req);
+		g_free(dec_msg);
 	} else {
 		g_free(add_req->id);
 		g_free(add_req->who);
-		/*g_free(add_req->msg);*/
 		g_free(add_req);
 	}
 }
@@ -3016,6 +3030,11 @@
 	if (yd->ycht)
 		ycht_connection_close(yd->ycht);
 
+	g_free(yd->pending_chat_room);
+	g_free(yd->pending_chat_id);
+	g_free(yd->pending_chat_topic);
+	g_free(yd->pending_chat_goto);
+
 	g_free(yd);
 	gc->proto_data = NULL;
 }
@@ -4104,9 +4123,7 @@
 
 	purple_debug(PURPLE_DEBUG_INFO, "yahoo",
 	           "Sending <ding> on account %s to buddy %s.\n", username, c->name);
-	/* TODO: find out how to send a <ding> without showing up as a blank line on
-	 * the conversation window. */
-	purple_conv_im_send(PURPLE_CONV_IM(c), "<ding>");
+	purple_conv_im_send_with_flags(PURPLE_CONV_IM(c), "<ding>", PURPLE_MESSAGE_INVISIBLE);
 
 	return TRUE;
 }
--- a/libpurple/protocols/yahoo/yahoo.h	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Fri Aug 31 16:10:18 2007 +0000
@@ -130,6 +130,10 @@
 	gboolean chat_online;
 	gboolean in_chat;
 	char *chat_name;
+	char *pending_chat_room;
+	char *pending_chat_id;
+	char *pending_chat_topic;
+	char *pending_chat_goto;
 	char *auth;
 	gsize auth_written;
 	char *cookie_y;
--- a/libpurple/protocols/yahoo/yahoochat.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c	Fri Aug 31 16:10:18 2007 +0000
@@ -55,16 +55,24 @@
 {
 	struct yahoo_data *yd = gc->proto_data;
 	struct yahoo_packet *pkt;
+	const char *rll;
 
 	if (yd->wm) {
 		ycht_connection_open(gc);
 		return;
 	}
 
+	rll = purple_account_get_string(purple_connection_get_account(gc),
+								  "room_list_locale", YAHOO_ROOMLIST_LOCALE);
+
 	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE,0);
-	yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc),
-	                  109, purple_connection_get_display_name(gc), 6, "abcde",
-	                  135, "ym8.1.0.415");
+	yahoo_packet_hash(pkt, "sssss",
+					  109, purple_connection_get_display_name(gc),
+					  1, purple_connection_get_display_name(gc),
+					  6, "abcde",
+					/* I'm not sure this is the correct way to set this. */
+					  98, rll,
+					  135, "ym8.1.0.415");
 	yahoo_packet_send_and_free(pkt, yd);
 }
 
@@ -125,6 +133,7 @@
 		case 1: /* us, but we already know who we are */
 			break;
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 50: /* inviter */
@@ -136,6 +145,7 @@
 			g_string_append_printf(members, "%s\n", pair->value);
 			break;
 		case 58:
+			g_free(msg);
 			msg = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 13: /* ? */
@@ -145,6 +155,17 @@
 
 	if (!room) {
 		g_string_free(members, TRUE);
+		g_free(msg);
+		return;
+	}
+
+	if (!yahoo_privacy_check(gc, who) ||
+			(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
+		purple_debug_info("yahoo",
+		    "Invite to conference %s from %s has been dropped.\n", room, who);
+		g_free(room);
+		g_free(msg);
+		g_string_free(members, TRUE);
 		return;
 	}
 
@@ -153,19 +174,9 @@
 	if (msg)
 		g_hash_table_replace(components, g_strdup("topic"), msg);
 	g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
-	if (members) {
-		g_hash_table_replace(components, g_strdup("members"), g_strdup(members->str));
-	}
-	if (!yahoo_privacy_check(gc, who) ||
-		(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
-		purple_debug_info("yahoo",
-		    "Invite to conference %s from %s has been dropped.\n", room, who);
-		g_string_free(members, TRUE);
-		return;
-	}
+	g_hash_table_replace(components, g_strdup("members"), g_string_free(members, FALSE));
 	serv_got_chat_invite(gc, room, who, msg, components);
 
-	g_string_free(members, TRUE);
 }
 
 void yahoo_process_conference_decline(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -180,20 +191,21 @@
 
 		switch (pair->key) {
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 54:
 			who = pair->value;
 			break;
 		case 14:
+			g_free(msg);
 			msg = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		}
 	}
 	if (!yahoo_privacy_check(gc, who)) {
 		g_free(room);
-		if (msg != NULL)
-			g_free(msg);
+		g_free(msg);
 		return;
 	}
 
@@ -209,8 +221,7 @@
 		}
 
 		g_free(room);
-		if (msg)
-			g_free(msg);
+		g_free(msg);
 	}
 }
 
@@ -226,6 +237,7 @@
 
 		switch (pair->key) {
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 53:
@@ -254,6 +266,7 @@
 
 		switch (pair->key) {
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 56:
@@ -276,7 +289,6 @@
 	char *room = NULL;
 	char *who = NULL;
 	char *msg = NULL;
-	char *msg2;
 	int utf8 = 0;
 	PurpleConversation *c;
 
@@ -285,6 +297,7 @@
 
 		switch (pair->key) {
 		case 57:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 3:
@@ -299,28 +312,82 @@
 		}
 	}
 
-		if (room && who && msg) {
-			msg2 = yahoo_string_decode(gc, msg, utf8);
-			c = yahoo_find_conference(gc, room);
-			if (!c)
-				return;
-			msg = yahoo_codes_to_html(msg2);
-			serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), who, 0, msg, time(NULL));
-			g_free(msg);
-			g_free(msg2);
+	if (room && who && msg) {
+		char *msg2;
+
+		c = yahoo_find_conference(gc, room);
+		if (!c) {
+			g_free(room);
+			return;
 		}
-		if (room)
-			g_free(room);
+
+		msg2 = yahoo_string_decode(gc, msg, utf8);
+		msg = yahoo_codes_to_html(msg2);
+		serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), who, 0, msg, time(NULL));
+		g_free(msg);
+		g_free(msg2);
+	}
+
+	g_free(room);
 }
 
+static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *room, const char *topic, const char *id)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	struct yahoo_packet *pkt;
+	char *room2;
+	gboolean utf8 = TRUE;
+
+	if (yd->wm) {
+		g_return_if_fail(yd->ycht != NULL);
+		ycht_chat_join(yd->ycht, room);
+		return;
+	}
+
+	/* apparently room names are always utf8, or else always not utf8,
+	 * so we don't have to actually pass the flag in the packet. Or something. */
+	room2 = yahoo_string_encode(gc, room, &utf8);
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pkt, "ssss",
+						1, purple_connection_get_display_name(gc),
+						104, room2,
+						62, "2",
+						129, id ? id : "0");
+	yahoo_packet_send_and_free(pkt, yd);
+	g_free(room2);
+}
 
 /* this is a confirmation of yahoo_chat_online(); */
 void yahoo_process_chat_online(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data;
 
-	if (pkt->status == 1)
+	if (pkt->status == 1) {
 		yd->chat_online = 1;
+
+		/* We need to goto a user in chat */
+		if (yd->pending_chat_goto) {
+			struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_CHATGOTO, YAHOO_STATUS_AVAILABLE, 0);
+			yahoo_packet_hash(pkt, "sss",
+				109, yd->pending_chat_goto,
+				1, purple_connection_get_display_name(gc),
+				62, "2");
+			yahoo_packet_send_and_free(pkt, yd);
+		} else if (yd->pending_chat_room) {
+			yahoo_chat_join(gc, purple_connection_get_display_name(gc), yd->pending_chat_room,
+				yd->pending_chat_topic, yd->pending_chat_id);
+		}
+
+		g_free(yd->pending_chat_room);
+		yd->pending_chat_room = NULL;
+		g_free(yd->pending_chat_id);
+		yd->pending_chat_id = NULL;
+		g_free(yd->pending_chat_topic);
+		yd->pending_chat_topic = NULL;
+		g_free(yd->pending_chat_goto);
+		yd->pending_chat_goto = NULL;
+	}
 }
 
 /* this is basicly the opposite of chat_online */
@@ -340,6 +407,14 @@
 
 	if (pkt->status == 1) {
 		yd->chat_online = 0;
+		g_free(yd->pending_chat_room);
+		yd->pending_chat_room = NULL;
+		g_free(yd->pending_chat_id);
+		yd->pending_chat_id = NULL;
+		g_free(yd->pending_chat_topic);
+		yd->pending_chat_topic = NULL;
+		g_free(yd->pending_chat_goto);
+		yd->pending_chat_goto = NULL;
 		if (yd->in_chat)
 			yahoo_c_leave(gc, YAHOO_CHAT_ID);
 	}
@@ -384,9 +459,11 @@
 		switch (pair->key) {
 
 		case 104:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, TRUE);
 			break;
 		case 105:
+			g_free(topic);
 			topic = yahoo_string_decode(gc, pair->value, TRUE);
 			break;
 		case 128:
@@ -445,8 +522,11 @@
 			purple_conversation_set_name(c, room);
 
 			c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
-			if (topic)
+			if (topic) {
 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+				/* Also print the topic to the backlog so that the captcha link is clickable */
+				purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", topic, PURPLE_MESSAGE_SYSTEM, time(NULL));
+			}
 			yd->in_chat = 1;
 			yd->chat_name = g_strdup(room);
 			purple_conv_chat_add_users(PURPLE_CONV_CHAT(c), members, NULL, flags, FALSE);
@@ -456,14 +536,22 @@
 			g_free(tmpmsg);
 		} else {
 			c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
-			if (topic)
+			if (topic) {
 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+				/* Also print the topic to the backlog so that the captcha link is clickable */
+				purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", topic, PURPLE_MESSAGE_SYSTEM, time(NULL));
+			}
 			yd->in_chat = 1;
 			yd->chat_name = g_strdup(room);
 			purple_conv_chat_add_users(PURPLE_CONV_CHAT(c), members, NULL, flags, FALSE);
 		}
 		g_list_free(flags);
 	} else if (c) {
+		if (topic) {
+			const char *cur_topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(c));
+			if (cur_topic == NULL || strcmp(cur_topic, topic) != 0)
+				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic);
+		}
 		yahoo_chat_add_users(PURPLE_CONV_CHAT(c), members);
 	}
 
@@ -497,8 +585,10 @@
 	for (l = pkt->hash; l; l = l->next) {
 		struct yahoo_pair *pair = l->data;
 
-		if (pair->key == 104)
+		if (pair->key == 104) {
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, TRUE);
+		}
 		if (pair->key == 109)
 			who = pair->value;
 	}
@@ -529,6 +619,7 @@
 			utf8 = strtol(pair->value, NULL, 10);
 			break;
 		case 104:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, TRUE);
 			break;
 		case 109:
@@ -583,6 +674,7 @@
 
 		switch (pair->key) {
 		case 104:
+			g_free(room);
 			room = yahoo_string_decode(gc, pair->value, TRUE);
 			break;
 		case 129: /* room id? */
@@ -590,6 +682,7 @@
 		case 126: /* ??? */
 			break;
 		case 117:
+			g_free(msg);
 			msg = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 119:
@@ -603,24 +696,21 @@
 	if (room && who) {
 		GHashTable *components;
 
+		if (!yahoo_privacy_check(gc, who) ||
+				(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
+			purple_debug_info("yahoo", "Invite to room %s from %s has been dropped.\n", room, who);
+			g_free(room);
+			g_free(msg);
+			return;
+		}
+
 		components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 		g_hash_table_replace(components, g_strdup("room"), g_strdup(room));
-		if (!yahoo_privacy_check(gc, who) ||
-			(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
-			purple_debug_info("yahoo",
-			"Invite to room %s from %s has been dropped.\n", room, who);
-			if (room != NULL)
-				g_free(room);
-			if (msg != NULL)
-				g_free(msg);
-			return;
-		}
 		serv_got_chat_invite(gc, room, who, msg, components);
 	}
-	if (room)
-		g_free(room);
-	if (msg)
-		g_free(msg);
+
+	g_free(room);
+	g_free(msg);
 }
 
 void yahoo_process_chat_goto(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -783,6 +873,14 @@
 	yahoo_packet_send_and_free(pkt, yd);
 
 	yd->chat_online = 0;
+	g_free(yd->pending_chat_room);
+	yd->pending_chat_room = NULL;
+	g_free(yd->pending_chat_id);
+	yd->pending_chat_id = NULL;
+	g_free(yd->pending_chat_topic);
+	yd->pending_chat_topic = NULL;
+	g_free(yd->pending_chat_goto);
+	yd->pending_chat_goto = NULL;
 	g_free(eroom);
 }
 
@@ -829,29 +927,6 @@
 	return 0;
 }
 
-static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *room, const char *topic)
-{
-	struct yahoo_data *yd = gc->proto_data;
-	struct yahoo_packet *pkt;
-	char *room2;
-	gboolean utf8 = TRUE;
-
-	if (yd->wm) {
-		g_return_if_fail(yd->ycht != NULL);
-		ycht_chat_join(yd->ycht, room);
-		return;
-	}
-
-	/* apparently room names are always utf8, or else always not utf8,
-	 * so we don't have to actually pass the flag in the packet. Or something. */
-	room2 = yahoo_string_encode(gc, room, &utf8);
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc),
-	                  62, "2", 104, room2, 129, "0");
-	yahoo_packet_send_and_free(pkt, yd);
-	g_free(room2);
-}
 
 static void yahoo_chat_invite(PurpleConnection *gc, const char *dn, const char *buddy,
 							const char *room, const char *msg)
@@ -892,8 +967,18 @@
 		return;
 	}
 
-	if (!yd->chat_online)
+	if (!yd->chat_online) {
 		yahoo_chat_online(gc);
+		g_free(yd->pending_chat_room);
+		yd->pending_chat_room = NULL;
+		g_free(yd->pending_chat_id);
+		yd->pending_chat_id = NULL;
+		g_free(yd->pending_chat_topic);
+		yd->pending_chat_topic = NULL;
+		g_free(yd->pending_chat_goto);
+		yd->pending_chat_goto = g_strdup(name);
+		return;
+	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATGOTO, YAHOO_STATUS_AVAILABLE, 0);
 	yahoo_packet_hash(pkt, "sss", 109, name, 1, purple_connection_get_display_name(gc), 62, "2");
@@ -988,8 +1073,7 @@
 void yahoo_c_join(PurpleConnection *gc, GHashTable *data)
 {
 	struct yahoo_data *yd;
-	char *room, *topic, *members, *type;
-	int id;
+	char *room, *topic, *type;
 	PurpleConversation *c;
 
 	yd = (struct yahoo_data *) gc->proto_data;
@@ -1004,9 +1088,9 @@
 	if (!topic)
 		topic = "";
 
-	members = g_hash_table_lookup(data, "members");
-
 	if ((type = g_hash_table_lookup(data, "type")) && !strcmp(type, "Conference")) {
+		int id;
+		const char *members = g_hash_table_lookup(data, "members");
 		id = yd->conf_id++;
 		c = serv_got_joined_chat(gc, id, room);
 		yd->confs = g_slist_prepend(yd->confs, c);
@@ -1014,13 +1098,27 @@
 		yahoo_conf_join(yd, c, purple_connection_get_display_name(gc), room, topic, members);
 		return;
 	} else {
-		if (yd->in_chat)
+		const char *id;
+		/*if (yd->in_chat)
 			yahoo_chat_leave(gc, room,
 					purple_connection_get_display_name(gc),
-					FALSE);
-		if (!yd->chat_online)
+					FALSE);*/
+
+		id = g_hash_table_lookup(data, "id");
+
+		if (!yd->chat_online) {
 			yahoo_chat_online(gc);
-		yahoo_chat_join(gc, purple_connection_get_display_name(gc), room, topic);
+			g_free(yd->pending_chat_room);
+			yd->pending_chat_room = g_strdup(room);
+			g_free(yd->pending_chat_id);
+			yd->pending_chat_id = g_strdup(id);
+			g_free(yd->pending_chat_topic);
+			yd->pending_chat_topic = g_strdup(topic);
+			g_free(yd->pending_chat_goto);
+			yd->pending_chat_goto = NULL;
+		} else {
+			yahoo_chat_join(gc, purple_connection_get_display_name(gc), room, topic, id);
+		}
 		return;
 	}
 }
@@ -1148,16 +1246,13 @@
 
 		for (i = 0; anames[i]; i++) {
 			if (!strcmp(anames[i], "id")) {
-				if (s->room.id)
-					g_free(s->room.id);
+				g_free(s->room.id);
 				s->room.id = g_strdup(avalues[i]);
 			} else if (!strcmp(anames[i], "name")) {
-				if (s->room.name)
-					g_free(s->room.name);
+				g_free(s->room.name);
 				s->room.name = g_strdup(avalues[i]);
 			} else if (!strcmp(anames[i], "topic")) {
-				if (s->room.topic)
-					g_free(s->room.topic);
+				g_free(s->room.topic);
 				s->room.topic = g_strdup(avalues[i]);
 			} else if (!strcmp(anames[i], "type")) {
 				if (!strcmp("yahoo", avalues[i]))
--- a/libpurple/prpl.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/prpl.c	Fri Aug 31 16:10:18 2007 +0000
@@ -199,8 +199,10 @@
 		if(NULL == status)
 			continue;
 
-		purple_status_set_active(status, FALSE);
-		purple_blist_update_buddy_status(buddy, status);
+		if (purple_status_is_active(status)) {
+			purple_status_set_active(status, FALSE);
+			purple_blist_update_buddy_status(buddy, status);
+		}
 	}
 
 	g_slist_free(list);
--- a/libpurple/server.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/server.c	Fri Aug 31 16:10:18 2007 +0000
@@ -272,6 +272,7 @@
 	PurpleAttentionType *attn;
 	PurpleMessageFlags flags;
 	PurplePlugin *prpl;
+	PurpleConversation *conv;
 	gboolean (*send_attention)(PurpleConnection *, const char *, guint);
 	
 	gchar *description;
@@ -302,8 +303,8 @@
 	if (!send_attention(gc, who, type_code))
 		return;
 
-	/* TODO: icons, sound, shaking... same as serv_got_attention(). */
-	serv_got_im(gc, who, description, flags, mtime);
+	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who);
+	purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime);
 
 	g_free(description);
 }
--- a/libpurple/win32/global.mak	Tue Aug 28 09:20:27 2007 +0000
+++ b/libpurple/win32/global.mak	Fri Aug 31 16:10:18 2007 +0000
@@ -20,7 +20,7 @@
 NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4
 NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4
 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl58
-SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.0.2
+SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.2
 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5
 
 # Where we installing this stuff to?
--- a/pidgin/gtkaccount.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtkaccount.c	Fri Aug 31 16:10:18 2007 +0000
@@ -2405,13 +2405,6 @@
 
 	g_free(accounts_window);
 	accounts_window = NULL;
-
-	/* See if we're the main window here. */
-	if (PIDGIN_BLIST(purple_get_blist())->window == NULL &&
-		purple_connections_get_all() == NULL) {
-
-		purple_core_quit();
-	}
 }
 
 static void
--- a/pidgin/gtkblist.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtkblist.c	Fri Aug 31 16:10:18 2007 +0000
@@ -125,7 +125,7 @@
 static PidginBuddyList *gtkblist = NULL;
 
 static gboolean pidgin_blist_refresh_timer(PurpleBuddyList *list);
-static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean statusChange);
+static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean status_change);
 static void pidgin_blist_selection_changed(GtkTreeSelection *selection, gpointer data);
 static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node);
 static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node);
@@ -332,8 +332,10 @@
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name,
 											   chat->account);
 
-	if (conv != NULL)
+	if (conv != NULL) {
+		pidgin_conv_attach_to_conversation(conv);
 		purple_conversation_present(conv);
+	}
 
 	serv_join_chat(chat->account->gc, chat->components);
 	g_free(chat_name);
@@ -612,6 +614,8 @@
 static void
 pidgin_blist_update_privacy_cb(PurpleBuddy *buddy)
 {
+	if (buddy->node.ui_data == NULL || ((struct _pidgin_blist_node*)buddy->node.ui_data)->row == NULL)
+		return;
 	pidgin_blist_update_buddy(purple_get_blist(), (PurpleBlistNode*)(buddy), TRUE);
 }
 
@@ -5156,7 +5160,7 @@
 
 
 
-static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean statusChange)
+static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean status_change)
 {
 	PurpleBuddy *buddy;
 	struct _pidgin_blist_node *gtkparentnode;
--- a/pidgin/gtkcertmgr.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtkcertmgr.c	Fri Aug 31 16:10:18 2007 +0000
@@ -597,7 +597,7 @@
 			for (l=idlist; l; l = l->next) {
 				purple_debug_info("gtkcertmgr",
 						  "- %s\n",
-						  (gchar *) l->data);
+						  l->data ? (gchar *) l->data : "(null)");
 			} /* idlist */
 			purple_certificate_pool_destroy_idlist(idlist);
 		} /* poollist */
@@ -676,11 +676,4 @@
 	gtk_widget_destroy(certmgr_dialog->window);
 	g_free(certmgr_dialog);
 	certmgr_dialog = NULL;
-
-	/* If this was the only window left, quit */
-	if (PIDGIN_BLIST(purple_get_blist())->window == NULL &&
-		purple_connections_get_all() == NULL) {
-
-		purple_core_quit();
-	}
 }
--- a/pidgin/gtkconv.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtkconv.c	Fri Aug 31 16:10:18 2007 +0000
@@ -330,6 +330,7 @@
                  const char *cmd, char **args, char **error, void *data)
 {
 	clear_conversation_scrollback(conv);
+	purple_conversation_clear_message_history(conv);
 	return PURPLE_CMD_STATUS_OK;
 }
 
@@ -1303,6 +1304,14 @@
 }
 
 static void
+menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget)
+{
+	PidginWindow *win = data;
+	PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
+	purple_conversation_set_ui_ops(conv, NULL);
+}
+
+static void
 menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget)
 {
 	PidginWindow *win = data;
@@ -2701,6 +2710,7 @@
 pidgin_conv_present_conversation(PurpleConversation *conv)
 {
 	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+	GdkModifierType state;
 
 	if(gtkconv->win==hidden_convwin) {
 		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
@@ -2708,7 +2718,10 @@
 	}
 
 	pidgin_conv_switch_active_conversation(conv);
-	pidgin_conv_window_switch_gtkconv(gtkconv->win, gtkconv);
+	/* Switch the tab only if the user initiated the event by pressing
+	 * a button or hitting a key. */
+	if (gtk_get_current_event_state(&state))
+		pidgin_conv_window_switch_gtkconv(gtkconv->win, gtkconv);
 	gtk_window_present(GTK_WINDOW(gtkconv->win->window));
 }
 
@@ -2734,7 +2747,7 @@
 		PurpleConversation *conv = (PurpleConversation*)l->data;
 		PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
-		if(gtkconv->active_conv != conv)
+		if(gtkconv == NULL || gtkconv->active_conv != conv)
 			continue;
 
 		if (gtkconv->unseen_state >= min_state
@@ -2861,6 +2874,8 @@
 	{ "/Conversation/sep4", NULL, NULL, 0, "<Separator>", NULL },
 
 
+	{ N_("/Conversation/_Hide"), NULL, menu_hide_conv_cb, 0,
+			"<StockItem>", NULL},
 	{ N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0,
 			"<StockItem>", GTK_STOCK_CLOSE },
 
@@ -5040,6 +5055,10 @@
 	g_list_foreach(gtkconv->send_history, (GFunc)g_free, NULL);
 	g_list_free(gtkconv->send_history);
 
+	if (gtkconv->attach.timer) {
+		g_source_remove(gtkconv->attach.timer);
+	}
+
 	if (tooltip.gtkconv == gtkconv)
 		reset_tooltip();
 
@@ -5256,6 +5275,15 @@
 	gtkconv = PIDGIN_CONVERSATION(conv);
 	g_return_if_fail(gtkconv != NULL);
 
+	if (gtkconv->attach.timer) {
+		/* We are currently in the process of filling up the buffer with the message
+		 * history of the conversation. So we do not need to add the message here.
+		 * Instead, this message will be added to the message-list, which in turn will
+		 * be processed and displayed by the attach-callback.
+		 */
+		return;
+	}
+
 	if (conv != gtkconv->active_conv)
 	{
 		if (flags & PURPLE_MESSAGE_ACTIVE_ONLY)
@@ -7222,6 +7250,55 @@
 	pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC);
 }
 
+static gboolean
+add_message_history_to_gtkconv(gpointer data)
+{
+	PidginConversation *gtkconv = data;
+	int count = 0;
+	int timer = gtkconv->attach.timer;
+	gtkconv->attach.timer = 0;
+	while (gtkconv->attach.current && count < 100) {  /* XXX: 100 is a random value here */
+		PurpleConvMessage *msg = gtkconv->attach.current->data;
+		pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when);
+		gtkconv->attach.current = gtkconv->attach.current->prev;
+		count++;
+	}
+	gtkconv->attach.timer = timer;
+	if (gtkconv->attach.current)
+		return TRUE;
+
+	g_source_remove(gtkconv->attach.timer);
+	gtkconv->attach.timer = 0;
+	return FALSE;
+}
+
+gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv)
+{
+	GList *list;
+	PidginConversation *gtkconv;
+
+	if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+		return FALSE;
+
+	purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops());
+	private_gtkconv_new(conv, FALSE);
+	gtkconv = PIDGIN_CONVERSATION(conv);
+
+	list = purple_conversation_get_message_history(conv);
+	if (list) {
+		list = g_list_last(list);
+		gtkconv->attach.current = list;
+		gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
+	}
+
+	/* XXX: If this is a chat:
+	 * 	- populate the userlist
+	 * 	- set the topic
+	 */
+
+	return TRUE;
+}
+
 void *
 pidgin_conversations_get_handle(void)
 {
@@ -8867,9 +8944,8 @@
 	if (pidgin_conv_window_get_gtkconv_count(win) == 1) 
 		gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook),
 					   purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs") &&
-                                           (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") ||  
-                                           purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_LEFT ||
-                                           purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_RIGHT));
+                                           (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") ||
+                                           purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") != GTK_POS_TOP));
 
 	/* show the widgets */
 /*	gtk_widget_show(gtkconv->icon); */
--- a/pidgin/gtkconv.h	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtkconv.h	Fri Aug 31 16:10:18 2007 +0000
@@ -162,6 +162,13 @@
 	GtkWidget *infopane;
 	GtkListStore *infopane_model;
 	GtkTreeIter infopane_iter;
+
+	/* Used when attaching a PidginConversation to a PurpleConversation
+	 * with message history */
+	struct {
+		int timer;
+		GList *current;
+	} attach;
 };
 
 /*@}*/
@@ -238,6 +245,8 @@
  */
 void pidgin_conv_present_conversation(PurpleConversation *conv);
 
+gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv);
+
 PidginWindow *pidgin_conv_get_window(PidginConversation *gtkconv);
 GdkPixbuf *pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon);
 void pidgin_conv_new(PurpleConversation *conv);
--- a/pidgin/gtkdialogs.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtkdialogs.c	Fri Aug 31 16:10:18 2007 +0000
@@ -99,6 +99,7 @@
         {"Dennis 'EvilDennisR' Ristuccia",	N_("Senior Contributor/QA"),	NULL},
 	{"Peter 'Fmoo' Ruibal",		NULL,	NULL},
 	{"Gabriel 'Nix' Schulhof", 	NULL, 	NULL},
+	{"Will 'resiak' Thompson",	NULL,	NULL},
 	{NULL, NULL, NULL}
 };
 
@@ -768,6 +769,7 @@
 	if (conv == NULL)
 		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username);
 
+	pidgin_conv_attach_to_conversation(conv);
 	purple_conversation_present(conv);
 }
 
--- a/pidgin/gtksound.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtksound.c	Fri Aug 31 16:10:18 2007 +0000
@@ -114,7 +114,7 @@
 play_conv_event(PurpleConversation *conv, PurpleSoundEventID event)
 {
 	/* If we should not play the sound for some reason, then exit early */
-	if (conv != NULL)
+	if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv))
 	{
 		PidginConversation *gtkconv;
 		PidginWindow *win;
--- a/pidgin/gtksourceundomanager.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtksourceundomanager.c	Fri Aug 31 16:10:18 2007 +0000
@@ -677,8 +677,6 @@
 	if (um->priv->running_not_undoable_actions > 0)
 		return;
 
-	g_return_if_fail (strlen (text) >= (guint)length);
-	
 	undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT;
 
 	undo_action.action.insert.pos    = gtk_text_iter_get_offset (pos);
@@ -774,7 +772,7 @@
 		*action = *undo_action;
 
 		if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
-			action->action.insert.text = g_strdup (undo_action->action.insert.text);
+			action->action.insert.text = g_strndup (undo_action->action.insert.text, undo_action->action.insert.length);
 		else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
 			action->action.delete.text = g_strdup (undo_action->action.delete.text); 
 		else
--- a/pidgin/gtkstatusbox.c	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Fri Aug 31 16:10:18 2007 +0000
@@ -1394,7 +1394,7 @@
 		return;
 	}
 	gtk_grab_add (box->popup_window);
-	box->popup_in_progress = TRUE;
+//	box->popup_in_progress = TRUE;
 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (box->toggle_button),
 				      TRUE);
 
@@ -1590,14 +1590,15 @@
 		    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (status_box->toggle_button))) {
 			pidgin_status_box_popdown (status_box);
 			return TRUE;
+		} else if (ewidget == status_box->toggle_button) {
+			status_box->popup_in_progress = TRUE;		
 		}
 
 		/* released outside treeview */
-		if (ewidget != status_box->toggle_button)
-			{
+		if (ewidget != status_box->toggle_button) {
 				pidgin_status_box_popdown (status_box);
 				return TRUE;
-			}
+		}
 
 		return FALSE;
 	}
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Tue Aug 28 09:20:27 2007 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Fri Aug 31 16:10:18 2007 +0000
@@ -503,8 +503,8 @@
     ${If} ${IsNT}
     ${AndIf} ${IsWinNT4}
       Delete "$INSTDIR\plugins\libsilc.dll"
-      Delete "$INSTDIR\silcclient.dll"
-      Delete "$INSTDIR\silc.dll"
+      Delete "$INSTDIR\libsilcclient-1-1-2.dll"
+      Delete "$INSTDIR\libsilc-1-1-2.dll"
     ${EndIf}
 
     SetOutPath "$INSTDIR"
@@ -693,6 +693,8 @@
     ; Remove Language preference info (TODO: check if NSIS removes this)
 
     Delete "$INSTDIR\ca-certs\Equifax_Secure_CA.pem"
+    Delete "$INSTDIR\ca-certs\GTE_CyberTrust_Global_Root.pem"
+    Delete "$INSTDIR\ca-certs\Verisign_Class3_Primary_CA.pem"
     Delete "$INSTDIR\ca-certs\Verisign_RSA_Secure_Server_CA.pem"
     RMDir "$INSTDIR\ca-certs"
     RMDir /r "$INSTDIR\locale"
@@ -764,8 +766,8 @@
     Delete "$INSTDIR\pidgin.dll"
     Delete "$INSTDIR\plc4.dll"
     Delete "$INSTDIR\plds4.dll"
-    Delete "$INSTDIR\silc.dll"
-    Delete "$INSTDIR\silcclient.dll"
+    Delete "$INSTDIR\libsilc-1-1-2.dll"
+    Delete "$INSTDIR\libsilcclient-1-1-2.dll"
     Delete "$INSTDIR\smime3.dll"
     Delete "$INSTDIR\softokn3.dll"
     Delete "$INSTDIR\ssl3.dll"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/GTE_CyberTrust_Global_Root.pem	Fri Aug 31 16:10:18 2007 +0000
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRy
+dXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg
+R2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1
+MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYD
+VQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMT
+GkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9pTAipTHBsiQl8i4
+ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6XALn
+ZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8F
+LztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh3
+46B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq
+81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0PlZPvy5TYnh+d
+XIVtx6quTx8itc2VrbqnzPmrC3p/
+-----END CERTIFICATE-----
--- a/share/ca-certs/Makefile.am	Tue Aug 28 09:20:27 2007 +0000
+++ b/share/ca-certs/Makefile.am	Fri Aug 31 16:10:18 2007 +0000
@@ -1,8 +1,11 @@
 cacertsdir =	$(datadir)/purple/ca-certs
 cacerts_DATA =	\
 		Equifax_Secure_CA.pem \
-		Verisign_RSA_Secure_Server_CA.pem
+		GTE_CyberTrust_Global_Root.pem \
+		Verisign_RSA_Secure_Server_CA.pem \
+		Verisign_Class3_Primary_CA.pem
 
-EXTRA_DIST = \
-                $(cacerts_DATA)
+EXTRA_DIST =	\
+		Makefile.mingw \
+		$(cacerts_DATA)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/Verisign_Class3_Primary_CA.pem	Fri Aug 31 16:10:18 2007 +0000
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
+BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
+I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
+CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
+lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
+AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+-----END CERTIFICATE-----