changeset 27346:9a2c1c6ad899

merge of '0db5728dd6db78a86154abd7e8e7cf32db421f7c' and 'a77ae8c1cf4a1b0c52b53de1d45a4cd86f42cafb'
author Etan Reisner <pidgin@unreliablesource.net>
date Sun, 28 Jun 2009 15:49:35 +0000
parents 283dea451801 (diff) e07abac099a2 (current diff)
children 4fd6b50d03b3
files ChangeLog ChangeLog.API pidgin/gtkprefs.c
diffstat 24 files changed, 800 insertions(+), 441 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Jun 28 07:06:31 2009 +0000
+++ b/ChangeLog	Sun Jun 28 15:49:35 2009 +0000
@@ -123,6 +123,33 @@
 	* Preferences have been reorganized into three tabs for Colors, Fonts, and
 	  Miscellaneous categories.
 
+version 2.5.8 (06/27/2009):
+	ICQ:
+	* Fix misparsing a web message as an SMS message. (Yuriy Kaminskiy)
+
+	MSN:
+	* Increase NS command history size to prevent crashes on buddy lists that
+	  have a lot of buddies on other networks like Yahoo!.
+
+	MySpace:
+	* Accounts with empty buddy lists are now properly marked as connected.
+	* Fix receiving messages from users of MySpace IM's web client.
+
+	Yahoo:
+	* Fixed phantom online buddies.  They should now properly disappear when
+	  signing out.
+	* Fixed the crashes some users were seeing with cn.scs.msg.yahoo.com in
+	  2.5.7.
+	* Fixed compiling on systems with glib 2.4.x or older.
+	* Fixed an issue with file transfers.  This may not resolve all issues,
+	  but it should resolve at least some of the most common ones.
+	* The pager server will automatically update to scsa.msg.yahoo.com if the
+	  user empties the field or if it is scs.msg.yahoo.com.  This should ease
+	  the pain of transition to the new login method.
+
+	XMPP:
+	* Fix an incompatibility betweeen Prosody and libpurple clients.
+
 version 2.5.7 (06/20/2009):
 	* Yahoo Protocol 16 support, including new HTTPS login method; this should
 	  fix a number of login problems that have recently cropped up.  (Sulabh
--- a/ChangeLog.API	Sun Jun 28 07:06:31 2009 +0000
+++ b/ChangeLog.API	Sun Jun 28 15:49:35 2009 +0000
@@ -129,6 +129,9 @@
 			* Purple::Request::Field::string_new
 			* Purple::Request::Field::group_new
 
+version 2.5.8 (06/27/2009):
+	No changes
+
 version 2.5.7 (06/20/2009):
 	No changes
 
--- a/ChangeLog.win32	Sun Jun 28 07:06:31 2009 +0000
+++ b/ChangeLog.win32	Sun Jun 28 15:49:35 2009 +0000
@@ -1,5 +1,8 @@
 version 2.6.0 (??/??/2009):
 
+version 2.5.8 (06/27/2009):
+	* No changes
+
 version 2.5.7 (06/20/2009):
 	* No changes
 
--- a/NEWS	Sun Jun 28 07:06:31 2009 +0000
+++ b/NEWS	Sun Jun 28 15:49:35 2009 +0000
@@ -2,6 +2,12 @@
 
 Our development blog is available at: http://planet.pidgin.im
 
+2.5.8 (06/27/2009):
+	John:  This release is another somewhat rushed bugfix release to fix
+	a number of bugs that have come up since we released Pidgin 2.5.7.
+	Hopefully anything I broke there is fixed now, or at least made to be
+	less broken.  Enjoy!
+
 2.5.7 (06/20/2009):
 	John:  This release is really just a rushed fix for the broken Yahoo
 	protocol plugin.  I spent way more time on this release than I care
--- a/libpurple/core.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/core.c	Sun Jun 28 15:49:35 2009 +0000
@@ -175,7 +175,6 @@
 	purple_xfers_init();
 	purple_idle_init();
 	purple_smileys_init();
-	purple_theme_manager_init();
 	/*
 	 * Call this early on to try to auto-detect our IP address and
 	 * hopefully save some time later.
--- a/libpurple/protocols/jabber/buddy.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Sun Jun 28 15:49:35 2009 +0000
@@ -1102,8 +1102,12 @@
 	char *txt, *vcard_hash = NULL;
 
 	if (type == JABBER_IQ_ERROR) {
-		purple_debug_warning("jabber", "Server returned error while retrieving vCard");
-		return;
+		xmlnode *error;
+		purple_debug_warning("jabber", "Server returned error while retrieving vCard\n");
+
+		error = xmlnode_get_child(packet, "error");
+		if (!error || !xmlnode_get_child(error, "item-not-found"))
+			return;
 	}
 
 	if((vcard = xmlnode_get_child(packet, "vCard")) ||
--- a/libpurple/protocols/jabber/ping.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/jabber/ping.c	Sun Jun 28 15:49:35 2009 +0000
@@ -33,11 +33,9 @@
                                      xmlnode *packet, gpointer data)
 {
 	if (js->keepalive_timeout != 0) {
-		purple_debug_misc("jabber", "Keepalive PONG\n");
 		purple_timeout_remove(js->keepalive_timeout);
 		js->keepalive_timeout = 0;
-	} else
-		purple_debug_warning("jabber", "Keepalive PONG with no outstanding timeout!\n");
+	}
 }
 
 void
--- a/libpurple/protocols/oscar/bstream.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/oscar/bstream.c	Sun Jun 28 15:49:35 2009 +0000
@@ -161,15 +161,19 @@
 	return aimutil_getle32(bs->data + bs->offset - 4);
 }
 
+static void byte_stream_getrawbuf_nocheck(ByteStream *bs, guint8 *buf, int len)
+{
+	memcpy(buf, bs->data + bs->offset, len);
+	bs->offset += len;
+}
+
 int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, int len)
 {
 
 	if (byte_stream_empty(bs) < len)
 		return 0;
 
-	memcpy(buf, bs->data + bs->offset, len);
-	bs->offset += len;
-
+	byte_stream_getrawbuf_nocheck(bs, buf, len);
 	return len;
 }
 
@@ -177,12 +181,12 @@
 {
 	guint8 *ob;
 
+	if (byte_stream_empty(bs) < len)
+		return NULL;
+
 	ob = g_malloc(len);
 
-	if (byte_stream_getrawbuf(bs, ob, len) < len) {
-		g_free(ob);
-		return NULL;
-	}
+	byte_stream_getrawbuf_nocheck(bs, ob, len);
 
 	return ob;
 }
@@ -191,12 +195,12 @@
 {
 	char *ob;
 
+	if (byte_stream_empty(bs) < len)
+		return NULL;
+
 	ob = g_malloc(len + 1);
 
-	if (byte_stream_getrawbuf(bs, (guint8 *)ob, len) < len) {
-		g_free(ob);
-		return NULL;
-	}
+	byte_stream_getrawbuf_nocheck(bs, (guint8 *)ob, len);
 
 	ob[len] = '\0';
 
--- a/libpurple/protocols/oscar/clientlogin.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/oscar/clientlogin.c	Sun Jun 28 15:49:35 2009 +0000
@@ -138,7 +138,7 @@
 static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **host, unsigned short *port, char **cookie)
 {
 	xmlnode *response_node, *tmp_node, *data_node;
-	xmlnode *host_node, *port_node, *cookie_node;
+	xmlnode *host_node = NULL, *port_node = NULL, *cookie_node = NULL;
 	char *tmp;
 
 	/* Parse the response as XML */
@@ -321,7 +321,7 @@
 static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **token, char **secret, time_t *hosttime)
 {
 	xmlnode *response_node, *tmp_node, *data_node;
-	xmlnode *secret_node, *hosttime_node, *token_node, *tokena_node;
+	xmlnode *secret_node = NULL, *hosttime_node = NULL, *token_node = NULL, *tokena_node = NULL;
 	char *tmp;
 
 	/* Parse the response as XML */
@@ -518,7 +518,7 @@
 
 	/* Tack on the body */
 	g_string_append_printf(request, "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n");
-	g_string_append_printf(request, "Content-Length: %lu\r\n\r\n", body->len);
+	g_string_append_printf(request, "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n", body->len);
 	g_string_append_len(request, body->str, body->len);
 	g_string_free(body, TRUE);
 
--- a/libpurple/protocols/oscar/family_icbm.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Sun Jun 28 15:49:35 2009 +0000
@@ -1566,7 +1566,7 @@
 
 static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
 {
-	guint16 type, length, magic1, msglen;
+	guint16 type, length, magic1, msglen = 0;
 	aim_rxcallback_t userfunc;
 	int ret = 0;
 	int rev = 0;
--- a/libpurple/protocols/oscar/oscar.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sun Jun 28 15:49:35 2009 +0000
@@ -2905,9 +2905,15 @@
 			/* From libicq2000-0.3.2/src/ICQ.cpp */
 			byte_stream_init(&qbs, (guint8 *)args->msg, args->msglen);
 			byte_stream_advance(&qbs, 21);
+			/* expected:	01 00 00 20 00 0e 28 f6 00 11 e7 d3 11 bc f3 00 04 ac 96 9d c2 | 00 00 | 06 00 00 00 | 49 43 51 53 43 53 ...*/
+			/* unexpected:	00 00 26 00 81 1a 18 bc 0e 6c 18 47 a5 91 6f 18 dc c7 6f 1a | 00 00 | 0d 00 00 00 | 49 43 51 57 65 62 4d 65 73 73 61 67 65 ... */
 			smstype = byte_stream_getle16(&qbs);
+			if (smstype != 0)
+				break;
 			taglen = byte_stream_getle32(&qbs);
 			tagstr = byte_stream_getstr(&qbs, taglen);
+			if (tagstr == NULL)
+				break;
 			byte_stream_advance(&qbs, 3);
 			byte_stream_advance(&qbs, 4);
 			smslen = byte_stream_getle32(&qbs);
--- a/libpurple/protocols/yahoo/yahoo.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sun Jun 28 15:49:35 2009 +0000
@@ -628,6 +628,9 @@
 			else
 				g_string_append(yd->tmp_serv_ilist, pair->value);
 			break;
+		case 89:
+			yd->profiles = g_strsplit(pair->value, ",", -1);
+			break;
 		case 59: /* cookies, yum */
 			yahoo_process_cookie(yd, pair->value);
 			break;
@@ -745,7 +748,6 @@
 	gint val_11 = 0;
 	struct yahoo_data *yd = gc->proto_data;
 	gboolean msn = FALSE;
-	char *msn_from = NULL;
 
 	account = purple_connection_get_account(gc);
 
@@ -778,17 +780,16 @@
 		return;
 	}
 
-	if(msn)
-		msn_from = g_strconcat("msn/", from, NULL);
-
 	if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
 		&& (purple_privacy_check(account, from)))
 	{
 		if(msn) {
+			char *msn_from = g_strconcat("msn/", from, NULL);
 			if (*stat == '1')
 				serv_got_typing(gc, msn_from, 0, PURPLE_TYPING);
 			else
 				serv_got_typing_stopped(gc, msn_from);
+			g_free(msn_from);
 		}
 		else	{
 			if (*stat == '1')
@@ -822,18 +823,18 @@
 		purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
 		g_free(buf);
 	}
-
-	g_free(msn_from);
 }
 
 
 struct _yahoo_im {
 	char *from;
+	char *active_id;
 	int time;
 	int utf8;
 	int buddy_icon;
 	char *id;
 	char *msg;
+	gboolean msn;
 };
 
 static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -907,8 +908,6 @@
 	struct _yahoo_im *im = NULL;
 	const char *imv = NULL;
 	gint val_11 = 0;
-	gboolean msn = FALSE;
-	char *msn_from = NULL;
 
 	account = purple_connection_get_account(gc);
 
@@ -923,6 +922,8 @@
 				im->time = time(NULL);
 				im->utf8 = TRUE;
 			}
+			if (im && pair->key == 5)
+				im->active_id = pair->value;
 			if (pair->key == 97)
 				if (im)
 					im->utf8 = strtol(pair->value, NULL, 10);
@@ -936,9 +937,9 @@
 				if (im)
 					im->msg = pair->value;
 			}
-			if (pair->key == 241) {
+			if (im && pair->key == 241) {
 				if(strtol(pair->value, NULL, 10) == 2)
-					msn = TRUE;
+					im->msn = TRUE;
 			}
 			/* peer session id */
 			if (pair->key == 11) {
@@ -960,9 +961,6 @@
 		                  _("Your Yahoo! message did not get sent."), NULL);
 	}
 
-	if(msn)
-		msn_from = g_strconcat("msn/", im->from, NULL);
-
 	/* disconnect the peer if connected through p2p and sends wrong value for session id */
 	if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) {
 		purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from);
@@ -1007,6 +1005,8 @@
 	for (l = list; l; l = l->next) {
 		YahooFriend *f;
 		char *m, *m2;
+		char *msn_from = NULL;
+		const char *from;
 		PurpleConversation *c;
 		im = l->data;
 
@@ -1033,7 +1033,7 @@
 			pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK,
 					YAHOO_STATUS_AVAILABLE, pkt->id);
 			yahoo_packet_hash(pkt2, "ssisii",
-					1, purple_connection_get_display_name(gc),
+					1, im->active_id,  /* May not always be the connection's display name */
 					5, im->from,
 					302, 430,
 					430, im->id,
@@ -1052,24 +1052,22 @@
 		m = m2;
 		purple_util_chrreplace(m, '\r', '\n');
 
-		c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->from, account);
-		if ((c == NULL) && msn)
-			c=purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, msn_from, account);
+		if (im->msn) {
+			msn_from = g_strconcat("msn/", im->from, NULL);
+			from = msn_from;
+		} else {
+			from = im->from;
+		}
+
+		c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account);
 
 		if (!strcmp(m, "<ding>")) {
 			char *username;
 
-			if(c == NULL) {
-				if(msn)
-					c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, msn_from);
-				else
-					c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, im->from);
+			if (c == NULL) {
+				c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from);
 			}
-			if(msn)
-				username = g_markup_escape_text(msn_from, -1);
-			else
-				username = g_markup_escape_text(im->from, -1);
-
+			username = g_markup_escape_text(from, -1);
 			purple_prpl_got_attention(gc, username, YAHOO_BUZZ);
 			g_free(username);
 			g_free(m);
@@ -1081,15 +1079,11 @@
 		m2 = yahoo_codes_to_html(m);
 		g_free(m);
 
-		if(msn)
-			serv_got_im(gc, msn_from, m2, 0, im->time);
-		else
-			serv_got_im(gc, im->from, m2, 0, im->time);
-
+		serv_got_im(gc, from, m2, 0, im->time);
 		g_free(m2);
 
 		/* laters : implement buddy icon for msn friends */
-		if(!msn) {
+		if (!im->msn) {
 			if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) {
 				if (yahoo_friend_get_buddy_icon_need_request(f)) {
 					yahoo_send_picture_request(gc, im->from);
@@ -3540,6 +3534,7 @@
 	g_free(yd->pending_chat_id);
 	g_free(yd->pending_chat_topic);
 	g_free(yd->pending_chat_goto);
+	g_strfreev(yd->profiles);
 
 	g_free(yd->current_list15_grp);
 
@@ -3925,15 +3920,16 @@
 	}
 }
 
-static void yahoo_act_id(PurpleConnection *gc, const char *entry)
+static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	struct yahoo_data *yd = gc->proto_data;
+	const char *name = yd->profiles[purple_request_fields_get_choice(fields, "id")];
 
 	struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash_str(pkt, 3, entry);
+	yahoo_packet_hash_str(pkt, 3, name);
 	yahoo_packet_send_and_free(pkt, yd);
 
-	purple_connection_set_display_name(gc, entry);
+	purple_connection_set_display_name(gc, name);
 }
 
 static void
@@ -4015,9 +4011,28 @@
 
 static void yahoo_show_act_id(PurplePluginAction *action)
 {
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	purple_request_input(gc, NULL, _("Activate which ID?"), NULL,
-					   purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
+	struct yahoo_data *yd = purple_connection_get_protocol_data(gc);
+	const char *name = purple_connection_get_display_name(gc);
+	int iter;
+
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+	field = purple_request_field_choice_new("id", "Activate which ID?", 0);
+	purple_request_field_group_add_field(group, field);
+
+	for (iter = 0; yd->profiles[iter]; iter++) {
+		purple_request_field_choice_add(field, yd->profiles[iter]);
+		if (purple_strequal(yd->profiles[iter], name))
+			purple_request_field_choice_set_default_value(field, iter);
+	}
+
+	purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL,
+					   fields,
 					   _("OK"), G_CALLBACK(yahoo_act_id),
 					   _("Cancel"), NULL,
 					   purple_connection_get_account(gc), NULL, NULL,
--- a/libpurple/protocols/yahoo/yahoo.h	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Sun Jun 28 15:49:35 2009 +0000
@@ -89,7 +89,7 @@
 #define YAHOOJP_CLIENT_VERSION_ID "4194239"
 #define YAHOOJP_CLIENT_VERSION "9.0.0.2152"
 
-#define YAHOO_CLIENT_USERAGENT "Mozilla/4.0 (compatible; MSIE 5.5)"
+#define YAHOO_CLIENT_USERAGENT "Mozilla/5.0"
 
 /* Index into attention types list. */
 #define YAHOO_BUZZ 0
@@ -155,6 +155,8 @@
 	guint txhandler;
 	GHashTable *friends;
 
+	char **profiles;  /* Multiple profiles can be associated with an account */
+
 	/**
 	 * This is used to keep track of the IMVironment chosen
 	 * by people you talk to.  We don't do very much with
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Sun Jun 28 15:49:35 2009 +0000
@@ -139,7 +139,7 @@
 		data->checksum = checksum;
 		/* TODO: Does this need to be MSIE 5.0? */
 		url_data = purple_util_fetch_url(url, use_whole_url,
-				"Mozilla/4.0 (compatible; MSIE 5.0)", FALSE,
+				"Mozilla/4.0 (compatible; MSIE 5.5)", FALSE,
 				yahoo_fetch_picture_cb, data);
 		if (url_data != NULL) {
 			yd = gc->proto_data;
@@ -315,8 +315,8 @@
 	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "sssssi", 1, purple_connection_get_display_name(gc),
-	                  4, purple_connection_get_display_name(gc), 5, who,
+	yahoo_packet_hash(pkt, "ssssi", 1, purple_connection_get_display_name(gc),
+	                  5, who,
 	                  13, "2", 20, yd->picture_url, 192, yd->picture_checksum);
 	yahoo_packet_send_and_free(pkt, yd);
 }
@@ -327,7 +327,7 @@
 	struct yahoo_packet *pkt;
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash_str(pkt, 4, purple_connection_get_display_name(gc)); /* me */
+	yahoo_packet_hash_str(pkt, 1, purple_connection_get_display_name(gc)); /* me */
 	yahoo_packet_hash_str(pkt, 5, who); /* the other guy */
 	yahoo_packet_hash_str(pkt, 13, "1"); /* 1 = request, 2 = reply */
 	yahoo_packet_send_and_free(pkt, yd);
--- a/libpurple/sound-theme-loader.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/sound-theme-loader.c	Sun Jun 28 15:49:35 2009 +0000
@@ -25,6 +25,7 @@
 #include "sound-theme.h"
 #include "util.h"
 #include "xmlnode.h"
+#include "debug.h"
 
 /*****************************************************************************
  * Sound Theme Builder
@@ -34,8 +35,9 @@
 purple_sound_loader_build(const gchar *dir)
 {
 	xmlnode *root_node = NULL, *sub_node;
-	gchar *filename_full, *data;
+	gchar *filename_full, *data = NULL;
 	PurpleSoundTheme *theme = NULL;
+	const gchar *name;
 
 	/* Find the theme file */
 	g_return_val_if_fail(dir != NULL, NULL);
@@ -45,30 +47,35 @@
 		root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-theme-loader");
 
 	g_free(filename_full);
-	g_return_val_if_fail(root_node != NULL, NULL);
+	if (root_node == NULL)
+		return NULL;
+
+	name = xmlnode_get_attrib(root_node, "name");
 
-	/* Parse the tree */
-	sub_node = xmlnode_get_child(root_node, "description");
-	data = xmlnode_get_data(sub_node);
+	if (name && purple_strequal(xmlnode_get_attrib(root_node, "type"), "sound")) {
+		/* Parse the tree */
+		sub_node = xmlnode_get_child(root_node, "description");
+		data = xmlnode_get_data(sub_node);
 
-	if (xmlnode_get_attrib(root_node, "name") != NULL) {
-		theme = g_object_new(PURPLE_TYPE_SOUND_THEME,
-				"type", "sound",
-				"name", xmlnode_get_attrib(root_node, "name"),
-				"author", xmlnode_get_attrib(root_node, "author"),
-				"image", xmlnode_get_attrib(root_node, "image"),
-				"directory", dir,
-				"description", data, NULL);
+		if (xmlnode_get_attrib(root_node, "name") != NULL) {
+			theme = g_object_new(PURPLE_TYPE_SOUND_THEME,
+					"type", "sound",
+					"name", name,
+					"author", xmlnode_get_attrib(root_node, "author"),
+					"image", xmlnode_get_attrib(root_node, "image"),
+					"directory", dir,
+					"description", data, NULL);
 
-		sub_node = xmlnode_get_child(root_node, "event");
+			sub_node = xmlnode_get_child(root_node, "event");
 
-		while (sub_node) {
-			purple_sound_theme_set_file(theme,
-					xmlnode_get_attrib(sub_node, "name"),
-					xmlnode_get_attrib(sub_node, "file"));
-			sub_node = xmlnode_get_next_twin(sub_node);
+			while (sub_node) {
+				purple_sound_theme_set_file(theme,
+						xmlnode_get_attrib(sub_node, "name"),
+						xmlnode_get_attrib(sub_node, "file"));
+				sub_node = xmlnode_get_next_twin(sub_node);
+			}
 		}
-	}
+	} else purple_debug_warning("sound-theme-loader", "Missing attribute or problem with the root element\n");
 
 	xmlnode_free(root_node);
 	g_free(data);
--- a/libpurple/theme-manager.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/theme-manager.c	Sun Jun 28 15:49:35 2009 +0000
@@ -294,3 +294,16 @@
 	g_hash_table_foreach(theme_table,
 			(GHFunc) purple_theme_manager_function_wrapper, func);
 }
+
+PurpleTheme *
+purple_theme_manager_load_theme(const gchar *theme_dir, const gchar *type)
+{
+	PurpleThemeLoader *loader;	
+
+	g_return_val_if_fail(theme_dir != NULL && type != NULL, NULL);
+
+	loader = g_hash_table_lookup(theme_table, type);
+	g_return_val_if_fail(PURPLE_IS_THEME_LOADER(loader), NULL);
+
+	return purple_theme_loader_build(loader, theme_dir);
+}
--- a/libpurple/theme-manager.h	Sun Jun 28 07:06:31 2009 +0000
+++ b/libpurple/theme-manager.h	Sun Jun 28 15:49:35 2009 +0000
@@ -127,5 +127,13 @@
  */
 void purple_theme_manager_for_each_theme(PTFunc func);
 
+/**
+ * Loads a theme of the given type without adding it to the manager
+ *
+ * @param theme_dir	the directory of the theme to load
+ * @param type		the type of theme to load
+ */
+PurpleTheme *purple_theme_manager_load_theme(const gchar *theme_dir, const gchar *type);
+
 G_END_DECLS
 #endif /* PURPLE_THEME_MANAGER_H */
--- a/pidgin/gtkblist-theme-loader.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/pidgin/gtkblist-theme-loader.c	Sun Jun 28 15:49:35 2009 +0000
@@ -24,6 +24,8 @@
 #include <string.h>
 
 #include "xmlnode.h"
+#include "debug.h"
+#include "util.h"
 
 #include "gtkblist-theme-loader.h"
 #include "gtkblist-theme.h"
@@ -58,8 +60,8 @@
 pidgin_blist_loader_build(const gchar *dir)
 {
 	xmlnode *root_node = NULL, *sub_node, *sub_sub_node;
-	gchar *filename_full, *data;
-	const gchar *temp;
+	gchar *filename_full, *data = NULL;
+	const gchar *temp, *name;
 	gboolean success = TRUE;
 	GdkColor bgcolor, expanded_bgcolor, collapsed_bgcolor, contact_color;
 	PidginThemeFont *expanded, *collapsed, *contact, *online, *away, *offline, *idle, *message, *message_nick_said, *status;
@@ -100,58 +102,81 @@
 		root_node = xmlnode_from_file(dir, "theme.xml", "buddy list themes", "blist-loader");
 
 	g_free(filename_full);
-	g_return_val_if_fail(root_node != NULL, NULL);
+	if (root_node == NULL)
+		return NULL;
 
 	sub_node = xmlnode_get_child(root_node, "description");
 	data = xmlnode_get_data(sub_node);
 
+	name = xmlnode_get_attrib(root_node, "name");
+
 	/* <blist> */
-	if ((success = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL)) {
-		if ((temp = xmlnode_get_attrib(sub_node, "color")) != NULL && gdk_color_parse(temp, &bgcolor))
-			gdk_colormap_alloc_color(gdk_colormap_get_system(), &bgcolor, FALSE, TRUE);
-		else
-			memset(&bgcolor, 0, sizeof(GdkColor));
+	success = name && purple_strequal(xmlnode_get_attrib(root_node, "type"), "pidgin buddy list");
+
+	if (!success)
+		purple_debug_warning("gtkblist-theme-loader", "Missing attribute or problem with the root element\n");
+
+	if (success) {
+		if ((success = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL)) {
+
+			if ((temp = xmlnode_get_attrib(sub_node, "color")) != NULL && gdk_color_parse(temp, &bgcolor))
+				gdk_colormap_alloc_color(gdk_colormap_get_system(), &bgcolor, FALSE, TRUE);
+			else
+				memset(&bgcolor, 0, sizeof(GdkColor));
+
+		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <blist>.\n");
 	}
 
 	/* <groups> */
-	if ((success = (success && (sub_node = xmlnode_get_child(root_node, "groups")) != NULL
-		     && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL)))
-	{
-		expanded = pidgin_theme_font_parse(sub_sub_node);
+	if (success) {
+		if ((success = (sub_node = xmlnode_get_child(root_node, "groups")) != NULL
+			     && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL)) {
+			expanded = pidgin_theme_font_parse(sub_sub_node);
 
-		if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, &expanded_bgcolor))
-			gdk_colormap_alloc_color(gdk_colormap_get_system(), &expanded_bgcolor, FALSE, TRUE);
-		else
-			memset(&expanded_bgcolor, 0, sizeof(GdkColor));
+			if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, &expanded_bgcolor))
+				gdk_colormap_alloc_color(gdk_colormap_get_system(), &expanded_bgcolor, FALSE, TRUE);
+			else
+				memset(&expanded_bgcolor, 0, sizeof(GdkColor));
+
+		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <groups> <expanded>.\n");
 	}
 
-	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL)))
-	{
-		collapsed = pidgin_theme_font_parse(sub_sub_node);
+	if (success) {
+		if ((success = sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL)) {
+
+			collapsed = pidgin_theme_font_parse(sub_sub_node);
 
-		if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, &collapsed_bgcolor))
-			gdk_colormap_alloc_color(gdk_colormap_get_system(), &collapsed_bgcolor, FALSE, TRUE);
-		else
-			memset(&collapsed_bgcolor, 0, sizeof(GdkColor));
+			if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, &collapsed_bgcolor))
+				gdk_colormap_alloc_color(gdk_colormap_get_system(), &collapsed_bgcolor, FALSE, TRUE);
+			else
+				memset(&collapsed_bgcolor, 0, sizeof(GdkColor));
+
+		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <groups> <collapsed>.\n");
 	}
 
 	/* <buddys> */
-	if ((success = (success && (sub_node = xmlnode_get_child(root_node, "buddys")) != NULL &&
-		     (sub_sub_node = xmlnode_get_child(sub_node, "placement")) != NULL)))
-	{
-		layout.status_icon = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0;
-		layout.text = (temp = xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1;
-		layout.emblem = (temp = xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2;
-		layout.protocol_icon = (temp = xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3;
-		layout.buddy_icon = (temp = xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4;
-		layout.show_status = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1;
+	if (success) {
+		if ((success = (sub_node = xmlnode_get_child(root_node, "buddys")) != NULL &&
+			     (sub_sub_node = xmlnode_get_child(sub_node, "placement")) != NULL)) {
+
+			layout.status_icon = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0;
+			layout.text = (temp = xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1;
+			layout.emblem = (temp = xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2;
+			layout.protocol_icon = (temp = xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3;
+			layout.buddy_icon = (temp = xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4;
+			layout.show_status = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1;
+
+		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <buddys> <placement>.\n");
 	}
 
-	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL))) {
-		if(gdk_color_parse(xmlnode_get_attrib(sub_sub_node, "color"), &contact_color))
-			gdk_colormap_alloc_color(gdk_colormap_get_system(), &contact_color, FALSE, TRUE);
-		else
-			memset(&contact_color, 0, sizeof(GdkColor));
+	if (success) {
+		if ((success = (sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL))) {
+			if(gdk_color_parse(xmlnode_get_attrib(sub_sub_node, "color"), &contact_color))
+				gdk_colormap_alloc_color(gdk_colormap_get_system(), &contact_color, FALSE, TRUE);
+			else
+				memset(&contact_color, 0, sizeof(GdkColor));
+
+		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <buddys> <background>.\n");
 	}
 
 	for (i = 0; success && lookups[i].tag; i++) {
@@ -169,7 +194,7 @@
 	/* the new theme */
 	theme = g_object_new(PIDGIN_TYPE_BLIST_THEME,
 			"type", "blist",
-			"name", xmlnode_get_attrib(root_node, "name"),
+			"name", name,
 			"author", xmlnode_get_attrib(root_node, "author"),
 			"image", xmlnode_get_attrib(root_node, "image"),
 			"directory", dir,
--- a/pidgin/gtkblist-theme.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/pidgin/gtkblist-theme.c	Sun Jun 28 15:49:35 2009 +0000
@@ -124,6 +124,9 @@
 static PidginThemeFont *
 copy_font_and_color(const PidginThemeFont *pair)
 {
+	if (pair == NULL)
+		return NULL;
+
 	PidginThemeFont *copy = g_new0(PidginThemeFont, 1);
 	copy->font  = g_strdup(pair->font);
 	strncpy(copy->color, pair->color, sizeof(copy->color) - 1);
--- a/pidgin/gtkicon-theme-loader.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/pidgin/gtkicon-theme-loader.c	Sun Jun 28 15:49:35 2009 +0000
@@ -24,6 +24,7 @@
 #include "gtkstatus-icon-theme.h"
 
 #include "xmlnode.h"
+#include "debug.h"
 
 /*****************************************************************************
  * Icon Theme Builder
@@ -33,8 +34,9 @@
 pidgin_icon_loader_build(const gchar *dir)
 {
 	xmlnode *root_node = NULL, *sub_node;
-	gchar *filename_full, *data;
+	gchar *filename_full, *data = NULL;
 	PidginIconTheme *theme = NULL;
+	const gchar *name;
 
 	/* Find the theme file */
 	g_return_val_if_fail(dir != NULL, NULL);
@@ -44,28 +46,33 @@
 		root_node = xmlnode_from_file(dir, "theme.xml", "icon themes", "icon-theme-loader");
 
 	g_free(filename_full);
-	g_return_val_if_fail(root_node != NULL, NULL);
+	if (root_node == NULL)
+		return NULL;
+
+	name = xmlnode_get_attrib(root_node, "name");
 
-	/* Parse the tree */
-	sub_node = xmlnode_get_child(root_node, "description");
-	data = xmlnode_get_data(sub_node);
+	if (name) {
+		/* Parse the tree */
+		sub_node = xmlnode_get_child(root_node, "description");
+		data = xmlnode_get_data(sub_node);
 
-	if (xmlnode_get_attrib(root_node, "name") != NULL) {
-		theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME,
-				"type", "status-icon",
-				"name", xmlnode_get_attrib(root_node, "name"),
-				"author", xmlnode_get_attrib(root_node, "author"),
-				"image", xmlnode_get_attrib(root_node, "image"),
-				"directory", dir,
-				"description", data, NULL);
+		if (xmlnode_get_attrib(root_node, "name") != NULL) {
+			theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME,
+					"type", "status-icon",
+					"name", name,
+					"author", xmlnode_get_attrib(root_node, "author"),
+					"image", xmlnode_get_attrib(root_node, "image"),
+					"directory", dir,
+					"description", data, NULL);
 
-		sub_node = xmlnode_get_child(root_node, "icon");
+			sub_node = xmlnode_get_child(root_node, "icon");
 
-		while (sub_node) {
-			pidgin_icon_theme_set_icon(theme,
-					xmlnode_get_attrib(sub_node, "id"),
-					xmlnode_get_attrib(sub_node, "file"));
-			sub_node = xmlnode_get_next_twin(sub_node);
+			while (sub_node) {
+				pidgin_icon_theme_set_icon(theme,
+						xmlnode_get_attrib(sub_node, "id"),
+						xmlnode_get_attrib(sub_node, "file"));
+				sub_node = xmlnode_get_next_twin(sub_node);
+			}
 		}
 	}
 
--- a/pidgin/gtknotify.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/pidgin/gtknotify.c	Sun Jun 28 15:49:35 2009 +0000
@@ -557,7 +557,10 @@
 						gtk_tree_store_remove(treemodel, &iter);
 						advanced = (iter.stamp == 0) ? FALSE : TRUE;
 #endif
-						purple_notify_close(PURPLE_NOTIFY_EMAILS, data);
+						if (data->purple_has_handle)
+							purple_notify_close(PURPLE_NOTIFY_EMAILS, data);
+						else
+							pidgin_close_notify(PURPLE_NOTIFY_EMAILS, data);
 						/* We're completely done if we've processed all entries */
 						if (!advanced)
 							return NULL;
@@ -612,7 +615,7 @@
 	char *notification;
 	PurpleAccount *account;
 	PidginNotifyMailData *data = NULL, *data2;
-	gboolean new_data;
+	gboolean new_data = FALSE;
 
 	/* Don't bother updating if there aren't new emails and we don't have any displayed currently */
 	if (count == 0 && mail_dialog == NULL)
@@ -660,7 +663,7 @@
 
 			/* If we don't keep track of this, will leak "data" for each of the notifications except the last */
 			data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, 0, FALSE, &new_data);
-			if (new_data) {
+			if (data2 && new_data) {
 				if (data)
 					data->purple_has_handle = FALSE;
 				data = data2;
@@ -677,7 +680,7 @@
 							   (int)count),
 							   *tos, (int)count);
 			data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, count, FALSE, &new_data);
-			if (new_data) {
+			if (data2 && new_data) {
 				if (data)
 					data->purple_has_handle = FALSE;
 				data = data2;
--- a/pidgin/gtkprefs.c	Sun Jun 28 07:06:31 2009 +0000
+++ b/pidgin/gtkprefs.c	Sun Jun 28 15:49:35 2009 +0000
@@ -61,6 +61,12 @@
 
 #define PREFS_OPTIMAL_ICON_SIZE 32
 
+struct theme_info {
+	gchar *type;
+	gchar *extension;
+	gchar *original_name;
+};
+
 static int sound_row_sel = 0;
 static GtkWidget *prefsnotebook;
 
@@ -74,11 +80,13 @@
 static int notebook_page = 0;
 static GtkTreeRowReference *previous_smiley_row = NULL;
 
-static gboolean prefs_themes_unsorted = TRUE;
 static GtkListStore *prefs_sound_themes;
 static GtkListStore *prefs_blist_themes;
 static GtkListStore *prefs_status_icon_themes;
 
+static GtkWidget *prefs_sound_themes_combo_box;
+static GtkWidget *prefs_blist_themes_combo_box;
+static GtkWidget *prefs_status_themes_combo_box;
 
 /*
  * PROTOTYPES
@@ -442,141 +450,6 @@
 	return row_ref;
 }
 
-static void theme_install_theme(char *path, char *extn) {
-#ifndef _WIN32
-	gchar *command;
-#endif
-	gchar *destdir;
-	gchar *tail;
-	GtkTreeRowReference *theme_rowref;
-
-	/* Just to be safe */
-	g_strchomp(path);
-
-	/* I dont know what you are, get out of here */
-	if (extn != NULL)
-		tail = extn;
-	else if ((tail = strrchr(path, '.')) == NULL)
-		return;
-
-	destdir = g_strconcat(purple_user_dir(), G_DIR_SEPARATOR_S "smileys", NULL);
-
-	/* We'll check this just to make sure. This also lets us do something different on
-	 * other platforms, if need be */
-	if (!g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz")) {
-#ifndef _WIN32
-		gchar *path_escaped = g_shell_quote(path);
-		gchar *destdir_escaped = g_shell_quote(destdir);
-		command = g_strdup_printf("tar > /dev/null xzf %s -C %s", path_escaped, destdir_escaped);
-		g_free(path_escaped);
-		g_free(destdir_escaped);
-#else
-		if(!winpidgin_gz_untar(path, destdir)) {
-			g_free(destdir);
-			return;
-		}
-#endif
-	}
-	else {
-		g_free(destdir);
-		return;
-	}
-
-#ifndef _WIN32
-	/* Fire! */
-	if (system(command))
-	{
-		purple_notify_error(NULL, NULL, _("Smiley theme failed to unpack."), NULL);
-	}
-
-	g_free(command);
-#endif
-	g_free(destdir);
-
-	theme_rowref = theme_refresh_theme_list();
-	if (theme_rowref != NULL) {
-		GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref);
-
-		if (tp)
-			gtk_tree_selection_select_path(smiley_theme_sel, tp);
-		gtk_tree_row_reference_free(theme_rowref);
-	}
-}
-
-static void
-theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
-		const gchar *themedata, size_t len, const gchar *error_message)
-{
-	FILE *f;
-	gchar *path;
-	size_t wc;
-
-	if ((error_message != NULL) || (len == 0))
-		return;
-
-	f = purple_mkstemp(&path, TRUE);
-	wc = fwrite(themedata, len, 1, f);
-	if (wc != 1) {
-		purple_debug_warning("theme_got_url", "Unable to write theme data.\n");
-		fclose(f);
-		g_unlink(path);
-		g_free(path);
-		return;
-	}
-	fclose(f);
-
-	theme_install_theme(path, user_data);
-
-	g_unlink(path);
-	g_free(path);
-}
-
-static void
-theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
-		GtkSelectionData *sd, guint info, guint t, gpointer data)
-{
-	gchar *name = (gchar *)sd->data;
-
-	if ((sd->length >= 0) && (sd->format == 8)) {
-		/* Well, it looks like the drag event was cool.
-		 * Let's do something with it */
-
-		if (!g_ascii_strncasecmp(name, "file://", 7)) {
-			GError *converr = NULL;
-			gchar *tmp;
-			/* It looks like we're dealing with a local file. Let's
-			 * just untar it in the right place */
-			if(!(tmp = g_filename_from_uri(name, NULL, &converr))) {
-				purple_debug(PURPLE_DEBUG_ERROR, "theme dnd", "%s\n",
-						   (converr ? converr->message :
-							"g_filename_from_uri error"));
-				return;
-			}
-			theme_install_theme(tmp, NULL);
-			g_free(tmp);
-		} else if (!g_ascii_strncasecmp(name, "http://", 7)) {
-			/* Oo, a web drag and drop. This is where things
-			 * will start to get interesting */
-			purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, ".tgz");
-		} else if (!g_ascii_strncasecmp(name, "https://", 8)) {
-			/* purple_util_fetch_url() doesn't support HTTPS, but we want users
-			 * to be able to drag and drop links from the SF trackers, so
-			 * we'll try it as an HTTP URL. */
-			char *tmp = g_strdup(name + 1);
-			tmp[0] = 'h';
-			tmp[1] = 't';
-			tmp[2] = 't';
-			tmp[3] = 'p';
-			purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, ".tgz");
-			g_free(tmp);
-		}
-
-		gtk_drag_finish(dc, TRUE, FALSE, t);
-	}
-
-	gtk_drag_finish(dc, FALSE, FALSE, t);
-}
-
 /* Rebuild the markup for the sound theme selection for "(Custom)" themes */
 static void
 pref_sound_generate_markup()
@@ -596,7 +469,7 @@
 
 			print_custom = customized && g_str_equal(current_theme, name);
 
-			if (g_str_equal(name, ""))
+			if (!name || *name == '\0')
 				markup = g_strdup_printf("<b>(Default)</b>%s%s - None\n<span foreground='dim grey'>The default Pidgin sound theme</span>",
 							 print_custom ? " " : "", print_custom ? "(Custom)" : "");
 			else {
@@ -617,7 +490,7 @@
 	}
 }
 
-/* adds the themes to the theme list from the manager so they can be sisplayed in prefs */
+/* adds the themes to the theme list from the manager so they can be displayed in prefs */
 static void
 prefs_themes_sort(PurpleTheme *theme)
 {
@@ -667,53 +540,380 @@
 		if (pixbuf != NULL)
 			g_object_unref(G_OBJECT(pixbuf));
 	}
-
 }
 
-/* init all the theme variables so that the themes can be sorted later and used by pref pages */
 static void
-prefs_themes_init()
+prefs_set_active_theme_combo(GtkWidget *combo_box, GtkListStore *store, const gchar *current_theme)
+{
+	GtkTreeIter iter;
+	gchar *theme = NULL;
+	gboolean unset = TRUE;
+
+	if (current_theme && *current_theme && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
+		do {
+			gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &theme, -1);
+
+			if (g_str_equal(current_theme, theme)) {
+				gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter);
+				unset = FALSE;
+			}
+
+			g_free(theme);
+		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
+	}
+
+	if (unset)
+		gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
+}
+
+static void
+prefs_themes_refresh()
 {
 	GdkPixbuf *pixbuf = NULL;
 	gchar *filename;
 	GtkTreeIter iter;
 
+	/* refresh the list of themes in the manager */
+	purple_theme_manager_refresh();
+
 	filename = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL);
 	pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
 	g_free(filename);
 
 	/* sound themes */
-	prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
-
+	gtk_list_store_clear(prefs_sound_themes);
 	gtk_list_store_append(prefs_sound_themes, &iter);
 	gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, "", -1);
 
 	/* blist themes */
-	prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
-
+	gtk_list_store_clear(prefs_blist_themes);
 	gtk_list_store_append(prefs_blist_themes, &iter);
-	gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>"
-								    "The default Pidgin buddy list theme</span>", 2, "", -1);
+	gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1,
+	                   "<b>(Default)</b> - None\n<span color='dim grey'>"
+	                   "The default Pidgin buddy list theme</span>", 2, "", -1);
 
 	/* status icon themes */
-	prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
-
+	gtk_list_store_clear(prefs_status_icon_themes);
 	gtk_list_store_append(prefs_status_icon_themes, &iter);
-	gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>"
-								    "The default Pidgin status icon theme</span>", 2, "", -1);
-
+	gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1,
+	                   "<b>(Default)</b> - None\n<span color='dim grey'>"
+	                   "The default Pidgin status icon theme</span>", 2, "", -1);
 	g_object_unref(G_OBJECT(pixbuf));
+
+	purple_theme_manager_for_each_theme(prefs_themes_sort);
+	pref_sound_generate_markup();
+
+	/* set active */
+	prefs_set_active_theme_combo(prefs_sound_themes_combo_box, prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"));
+	prefs_set_active_theme_combo(prefs_blist_themes_combo_box, prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"));
+	prefs_set_active_theme_combo(prefs_status_themes_combo_box, prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"));
+}
+
+/* init all the theme variables so that the themes can be sorted later and used by pref pages */
+static void
+prefs_themes_init()
+{
+	prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+	
+	prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+}
+
+static PurpleTheme *
+prefs_theme_find_theme(const gchar *path, const gchar *type) 
+{
+	PurpleTheme *theme = purple_theme_manager_load_theme(path, type);
+	GDir *dir = g_dir_open(path, 0, NULL);
+	const gchar *next;
+
+	while (!PURPLE_IS_THEME(theme) && (next = g_dir_read_name(dir))) {
+		gchar *next_path = g_build_filename(path, next, NULL);
+
+		if (g_file_test(next_path, G_FILE_TEST_IS_DIR))
+			theme = prefs_theme_find_theme(next_path, type);
+
+		g_free(next_path);
+	}
+
+	g_dir_close(dir);
+
+	return theme;
+}
+
+/* installs a theme, info is freed by function */
+static void 
+theme_install_theme(char *path, struct theme_info *info) {
+#ifndef _WIN32
+	gchar *command;
+#endif
+	gchar *destdir, *tail, *type, *original_name;
+	GtkTreeRowReference *theme_rowref;	
+	gboolean is_smiley_theme, is_archive;
+	PurpleTheme *theme = NULL;
+
+	if (info == NULL) 
+		return;
+
+	original_name = info->original_name;
+	type = info->type;
+
+	/* check the extension */
+	tail = info->extension ? info->extension : g_strdup(strrchr(path, '.'));
+
+	if (!tail) {
+		g_free(type);
+		g_free(original_name);
+		g_free(info);
+		return;
+	} else g_free(info);
+
+	is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz");
+
+	g_free(tail);
+
+	/* Just to be safe */
+	g_strchomp(path);
+
+	if ((is_smiley_theme = g_str_equal(type, "smiley")))
+		destdir = g_build_filename(purple_user_dir(), "smileys", NULL);
+	else destdir = g_build_filename(purple_user_dir(), "themes", "temp", NULL);
+
+	/* We'll check this just to make sure. This also lets us do something different on
+	 * other platforms, if need be */
+	if (is_archive) {
+#ifndef _WIN32
+		gchar *path_escaped = g_shell_quote(path);
+		gchar *destdir_escaped = g_shell_quote(destdir);
+
+		if (!g_file_test(destdir, G_FILE_TEST_IS_DIR))
+			g_mkdir_with_parents(destdir, 0700);
+
+		command = g_strdup_printf("tar > /dev/null xzf %s -C %s", path_escaped, destdir_escaped);
+		g_free(path_escaped);
+		g_free(destdir_escaped);
+
+		/* Fire! */
+		if (system(command)) {
+			purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL);
+			g_free(command);
+			g_free(destdir);
+			g_free(type);
+			g_free(original_name);
+			return;
+		}
+#else
+		if(!winpidgin_gz_untar(path, destdir)) {
+			g_free(destdir);
+			g_free(type);
+			g_free(original_name);			
+			return;
+		}
+#endif
+	} 
+
+	if (is_smiley_theme) {
+		/* just extract the folder to the smiley directory */
+		theme_rowref = theme_refresh_theme_list();
+
+		if (theme_rowref != NULL) {
+			GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref);
+
+			if (tp)
+				gtk_tree_selection_select_path(smiley_theme_sel, tp);
+
+			gtk_tree_row_reference_free(theme_rowref);
+		}
+
+	} else if (is_archive) {
+		theme = prefs_theme_find_theme(destdir, type);
+
+		if (PURPLE_IS_THEME(theme)) {
+			/* create the location for the theme */
+			gchar *theme_dest = g_build_filename(purple_user_dir(), "themes", 
+						 purple_theme_get_name(theme),
+						 "purple", type, NULL);
+
+			if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
+				g_mkdir_with_parents(theme_dest, 0700);
+
+			g_free(theme_dest);
+			theme_dest = g_build_filename(purple_user_dir(), "themes", 
+						 purple_theme_get_name(theme),
+						 "purple", type, NULL);
+
+			/* move the entire directory to new location */
+			g_rename(purple_theme_get_dir(theme), theme_dest);
+
+			g_free(theme_dest);
+			g_remove(destdir);
+			g_object_unref(theme);
+
+			prefs_themes_refresh();
+
+		} else {
+			/* something was wrong with the theme archive */
+			g_unlink(destdir);
+			purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL);
+		}
+
+	} else { /* just a single file so copy it to a new temp directory and attempt to load it*/
+		GFile *source, *destination;
+		gchar *temp_path, *temp_file;
+		
+		source = g_file_new_for_path(path);
+
+		temp_path = g_build_filename(purple_user_dir(), "themes", "temp", "sub_folder", NULL);
+
+		if (original_name != NULL) {
+			/* name was changed from the original (probably a dnd) change it back before loading */
+			temp_file = g_build_filename(temp_path, original_name, NULL);
+
+		} else {
+			/* find the file name and name the new file the same thing */
+			GFileInfo* file_info = g_file_query_info (source,
+	                               		G_FILE_ATTRIBUTE_STANDARD_NAME,
+	                               		G_FILE_QUERY_INFO_NONE,
+	                               		NULL,
+	                               		NULL);
+
+			const gchar *source_name = g_file_info_get_content_type(file_info);
+
+			temp_file = g_build_filename(temp_path, source_name, NULL);
+
+			g_object_unref(file_info);
+		}		
+
+		destination = g_file_new_for_path(temp_file);
+
+		if (!g_file_test(temp_path, G_FILE_TEST_IS_DIR))
+			g_mkdir_with_parents(temp_path, 0700);
+
+		g_file_copy(source, destination, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL);
+
+		g_object_unref(source);
+		g_object_unref(destination);
+							
+		/* find the theme, could be in subfolder */
+		theme = prefs_theme_find_theme(temp_path, type);
+
+		if (PURPLE_IS_THEME(theme)) {
+			gchar *theme_dest = g_build_filename(purple_user_dir(), "themes", 
+						 purple_theme_get_name(theme),
+						 "purple", type, NULL);
+				
+			if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
+				g_mkdir_with_parents(theme_dest, 0700);		
+
+			g_rename(purple_theme_get_dir(theme), theme_dest);
+
+			g_free(theme_dest);
+			g_object_unref(theme);
+
+			prefs_themes_refresh();
+
+		} else {
+			g_remove(temp_path);
+			purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL);
+		}
+
+		g_free(temp_file);
+		g_free(temp_path);
+	}
+
+	g_free(type);	
+	g_free(original_name);
+	g_free(destdir);
+}
+
+static void
+theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
+		const gchar *themedata, size_t len, const gchar *error_message)
+{
+	FILE *f;
+	gchar *path;
+	size_t wc;
+
+	if ((error_message != NULL) || (len == 0))
+		return;
+
+	f = purple_mkstemp(&path, TRUE);
+	wc = fwrite(themedata, len, 1, f);
+	if (wc != 1) {
+		purple_debug_warning("theme_got_url", "Unable to write theme data.\n");
+		fclose(f);
+		g_unlink(path);
+		g_free(path);
+		return;
+	}
+	fclose(f);
+
+	theme_install_theme(path, user_data);
+
+	g_unlink(path);
+	g_free(path);
+}
+
+static void
+theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
+		GtkSelectionData *sd, guint info, guint t, gpointer user_data)
+{
+	gchar *name = g_strchomp((gchar *)sd->data);
+
+	if ((sd->length >= 0) && (sd->format == 8)) {
+		/* Well, it looks like the drag event was cool.
+		 * Let's do something with it */
+		gchar *temp;
+		struct theme_info *info =  g_new0(struct theme_info, 1);
+		info->type = g_strdup((gchar *)user_data);
+		info->extension = g_strdup(g_strrstr(name,".")); 	
+		temp = g_strrstr(name, "/"); 
+		info->original_name = temp ? g_strdup(++temp) : NULL;
+
+		if (!g_ascii_strncasecmp(name, "file://", 7)) {
+			GError *converr = NULL;
+			gchar *tmp;
+			/* It looks like we're dealing with a local file. Let's
+			 * just untar it in the right place */
+			if(!(tmp = g_filename_from_uri(name, NULL, &converr))) {
+				purple_debug(PURPLE_DEBUG_ERROR, "theme dnd", "%s\n",
+						   (converr ? converr->message :
+							"g_filename_from_uri error"));
+				return;
+			}
+			theme_install_theme(tmp, info);
+			g_free(tmp);
+		} else if (!g_ascii_strncasecmp(name, "http://", 7)) {
+			/* Oo, a web drag and drop. This is where things
+			 * will start to get interesting */
+			purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, info);
+		} else if (!g_ascii_strncasecmp(name, "https://", 8)) {
+			/* purple_util_fetch_url() doesn't support HTTPS, but we want users
+			 * to be able to drag and drop links from the SF trackers, so
+			 * we'll try it as an HTTP URL. */
+			char *tmp = g_strdup(name + 1);
+			tmp[0] = 'h';
+			tmp[1] = 't';
+			tmp[2] = 't';
+			tmp[3] = 'p';
+
+			purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, info);
+			g_free(tmp);
+		}
+
+		gtk_drag_finish(dc, TRUE, FALSE, t);
+	}
+	
+	gtk_drag_finish(dc, FALSE, FALSE, t);
 }
 
 /* builds a theme combo box from a list store with colums: icon preview, markup, theme name */
 static GtkWidget *
-prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme)
+prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme, gchar *type)
 {
-	GtkWidget *combo_box;
 	GtkCellRenderer *cell_rend;
-	GtkTreeIter iter;
-	gchar *theme = NULL;
-	gboolean unset = TRUE;
+	GtkWidget *combo_box;
+	GtkTargetEntry te[3] = {{"text/plain", 0, 0},{"text/uri-list", 0, 1},{"STRING", 0, 2}};
 
 	g_return_val_if_fail(store != NULL && current_theme != NULL, NULL);
 
@@ -731,21 +931,10 @@
 			g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 #endif*/
 
-	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
-		do {
-			gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &theme, -1);
-
-			if (g_str_equal(current_theme, theme)) {
-				gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter);
-				unset = FALSE;
-			}
-
-			g_free(theme);
-		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
-	}
-
-	if (unset)
-		gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
+	gtk_drag_dest_set(combo_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te,
+					sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+	g_signal_connect(G_OBJECT(combo_box), "drag_data_received", G_CALLBACK(theme_dnd_recv), type);
 
 	return combo_box;
 }
@@ -757,32 +946,32 @@
 	gint i;
 	gchar *pref;
 	gchar *new_theme;
-	gboolean success;
 	GtkTreeIter new_iter;
 
-	success = gtk_combo_box_get_active_iter(combo_box, &new_iter);
-	g_return_if_fail(success);
-
-	gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &new_iter, 2, &new_theme, -1);
-
-	purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme);
-
-	/* New theme removes all customization */
-	for(i=0; i <  PURPLE_NUM_SOUNDS; i++){
-		pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
-					pidgin_sound_get_event_option(i));
-		purple_prefs_set_path(pref, "");
-		g_free(pref);
+	if(gtk_combo_box_get_active_iter(combo_box, &new_iter)) {
+
+		gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &new_iter, 2, &new_theme, -1);
+
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme);
+
+		/* New theme removes all customization */
+		for(i=0; i <  PURPLE_NUM_SOUNDS; i++){
+			pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
+						pidgin_sound_get_event_option(i));
+			purple_prefs_set_path(pref, "");
+			g_free(pref);
+		}
+
+		/* gets rid of the "(Custom)" from the last selection */
+		pref_sound_generate_markup();
+
+		gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
+
+		g_free(new_theme);
 	}
-
-	/* gets rid of the "(Custom)" from the last selection */
-	pref_sound_generate_markup();
-
-	gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
-
-	g_free(new_theme);
 }
 
+
 /* Does same as normal sort, except "none" is sorted first */
 static gint pidgin_sort_smileys (GtkTreeModel	*model,
 						GtkTreeIter		*a,
@@ -821,11 +1010,18 @@
 static void
 request_theme_file_name_cb(gpointer data, char *theme_file_name)
 {
-	theme_install_theme(theme_file_name, NULL) ;
+	struct theme_info *info = g_new0(struct theme_info, 1);
+	info->type = g_strdup("smiley");
+	info->extension = NULL;
+	info->original_name = NULL;
+	
+	theme_install_theme(theme_file_name, info) ;
+
+	g_free(info);
 }
 
 static void
-add_theme_button_clicked_cb(GtkWidget *widget, gpointer null)
+add_theme_button_clicked_cb(GtkWidget *widget, gpointer user_data)
 {
 	purple_request_file(NULL, _("Install Theme"), NULL, FALSE, (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, NULL) ;
 }
@@ -903,7 +1099,7 @@
 	gtk_drag_dest_set(view, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te,
 					sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE);
 
-	g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), smiley_theme_store);
+	g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), "smiley");
 
 	rend = gtk_cell_renderer_pixbuf_new();
 	smiley_theme_sel = sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
@@ -1163,35 +1359,42 @@
 static void
 prefs_set_blist_theme_cb(GtkComboBox *combo_box, gpointer user_data)
 {
-	PidginBlistTheme *theme = NULL;
+	PidginBlistTheme *theme =  NULL;
 	GtkTreeIter iter;
 	gchar *name = NULL;
 
-	g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter));
-	gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &name, -1);
-
-	if (name && *name)
-		theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist"));
-	g_free(name);
-
-	pidgin_blist_set_theme(theme);
+	if(gtk_combo_box_get_active_iter(combo_box, &iter)) {
+
+		gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &name, -1);
+		
+		if(!name || !g_str_equal(name, ""))
+			theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist"));
+		
+		g_free(name);
+
+		pidgin_blist_set_theme(theme);
+	}
 }
 
 /* sets the current icon theme */
 static void
 prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data)
 {
-	PidginStatusIconTheme *theme;
+	PidginStatusIconTheme *theme = NULL;
 	GtkTreeIter iter;
 	gchar *name = NULL;
 
-	g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter));
-	gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1);
-
-	theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon"));
-	g_free(name);
-
-	pidgin_stock_load_status_icon_theme(theme);
+	if(gtk_combo_box_get_active_iter(combo_box, &iter)) {
+
+		gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1);
+
+		if(!name || !g_str_equal(name, ""))
+			theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon"));
+		
+		g_free(name);
+
+		pidgin_stock_load_status_icon_theme(theme);
+	}
 }
 
 static GtkWidget *
@@ -1201,7 +1404,6 @@
 	GtkWidget *vbox;
 	GtkWidget *vbox2;
 	GtkWidget *label;
-	GtkWidget *combo_box;
 	GtkSizeGroup *sg;
 	GList *names = NULL;
 
@@ -1213,14 +1415,20 @@
 	/* Buddy List Themes */
 	vbox = pidgin_make_frame(ret, _("Buddy List Theme"));
 
-	combo_box = prefs_build_theme_combo_box(prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"));
-	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_blist_theme_cb, NULL);
+	prefs_blist_themes_combo_box = prefs_build_theme_combo_box(prefs_blist_themes, 
+						purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"),
+						"blist");
+
+	gtk_box_pack_start(GTK_BOX (vbox), prefs_blist_themes_combo_box, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(prefs_blist_themes_combo_box), "changed", (GCallback)prefs_set_blist_theme_cb, NULL);
 
 	/* Status Icon Themes */
-	combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"));
-	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_status_icon_theme_cb, NULL);
+	prefs_status_themes_combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes, 
+						purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"), 
+						"icon");
+
+	gtk_box_pack_start(GTK_BOX (vbox), prefs_status_themes_combo_box, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(prefs_status_themes_combo_box), "changed", (GCallback)prefs_set_status_icon_theme_cb, NULL);
 
 	/* System Tray */
 	vbox = pidgin_make_frame(ret, _("System Tray Icon"));
@@ -2202,7 +2410,7 @@
 sound_page(void)
 {
 	GtkWidget *ret;
-	GtkWidget *vbox, *sw, *button, *combo_box;
+	GtkWidget *vbox, *sw, *button;
 	GtkSizeGroup *sg;
 	GtkTreeIter iter;
 	GtkWidget *event_view;
@@ -2308,11 +2516,14 @@
 			vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START);
 
 	/* SOUND THEMES */
-	combo_box = prefs_build_theme_combo_box(prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"));
-	pref_sound_generate_markup();
-	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL);
+	prefs_sound_themes_combo_box = prefs_build_theme_combo_box(prefs_sound_themes, 
+						purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"),
+						"sound");
+
+
+	gtk_box_pack_start(GTK_BOX (vbox), prefs_sound_themes_combo_box, FALSE, FALSE, 0);
+
+	g_signal_connect(G_OBJECT(prefs_sound_themes_combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL);
 
 	/* SOUND SELECTION */
 	sw = gtk_scrolled_window_new(NULL,NULL);
@@ -2545,14 +2756,6 @@
 		return;
 	}
 
-	/* Refresh the list of themes before showing the preferences window */
-	purple_theme_manager_refresh();
-
-	/* add everything in the theme manager before the window is loaded */
-	if (prefs_themes_unsorted) {
-		purple_theme_manager_for_each_theme(prefs_themes_sort);
-		prefs_themes_unsorted = FALSE;
-	}
 	/* copy the preferences to tmp values...
 	 * I liked "take affect immediately" Oh well :-( */
 	/* (that should have been "effect," right?) */
@@ -2577,6 +2780,9 @@
 
 	prefs_notebook_init();
 
+	/* Refresh the list of themes before showing the preferences window */
+	prefs_themes_refresh();
+
 	/* Show everything. */
 	gtk_widget_show(prefs);
 }
--- a/po/ChangeLog	Sun Jun 28 07:06:31 2009 +0000
+++ b/po/ChangeLog	Sun Jun 28 15:49:35 2009 +0000
@@ -6,6 +6,12 @@
 	* Slovenian translation updated (Martin Srebotnjak)
 	* Swahili translation added (Paul Msegeya)
 
+version 2.5.8
+	* No changes
+
+version 2.5.7
+	* No changes
+
 version 2.5.6
 	* German translation updated (Björn Vogt)
 
--- a/po/de.po	Sun Jun 28 07:06:31 2009 +0000
+++ b/po/de.po	Sun Jun 28 15:49:35 2009 +0000
@@ -11,9 +11,9 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-06-11 12:42+0200\n"
-"PO-Revision-Date: 2009-06-11 12:40+0200\n"
-"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
+"POT-Creation-Date: 2009-06-28 11:42+0200\n"
+"PO-Revision-Date: 2009-06-28 11:42+0200\n"
+"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -3221,9 +3221,8 @@
 msgid "Gadu-Gadu User"
 msgstr "Gadu-Gadu-Benutzer"
 
-#, fuzzy
 msgid "GG server"
-msgstr "Hole server"
+msgstr "GG-Server"
 
 #, c-format
 msgid "Unknown command: %s"
@@ -3473,9 +3472,8 @@
 msgid "The nickname \"%s\" is already being used."
 msgstr "Der Spitzname „%s“ existiert bereits."
 
-#, fuzzy
 msgid "Nickname in use"
-msgstr "Spitzname"
+msgstr "Spitzname wird benutzt"
 
 msgid "Cannot change nick"
 msgstr "Kann den Spitznamen nicht ändern"
@@ -4022,7 +4020,6 @@
 msgid "%s ago"
 msgstr "vor %s"
 
-#, fuzzy
 msgid "Logged off"
 msgstr "Abgemeldet"
 
@@ -4223,8 +4220,8 @@
 msgid "Invalid XMPP ID. Domain must be set."
 msgstr "Falsche XMPP-ID. Die Domain muss gesetzt werden."
 
-msgid "Malformed BOSH Connect Server"
-msgstr ""
+msgid "Malformed BOSH URL"
+msgstr "Fehlerhafte BOSH-URL"
 
 #, c-format
 msgid "Registration of %s@%s successful"
@@ -4296,6 +4293,9 @@
 msgid "Change Registration"
 msgstr "Ändere Registrierung"
 
+msgid "Malformed BOSH Connect Server"
+msgstr "Fehlerhafter BOSH-Verbindungsserver"
+
 msgid "Error unregistering account"
 msgstr "Fehler beim Aufheben der Kontenregistrierung"
 
@@ -4740,6 +4740,9 @@
 msgid "File transfer proxies"
 msgstr "Proxys für Dateiübertragungen"
 
+msgid "BOSH URL"
+msgstr "BOSH-URL"
+
 #. this should probably be part of global smiley theme settings later on,
 #. shared with MSN
 msgid "Show Custom Smileys"
@@ -5771,15 +5774,17 @@
 
 #, c-format
 msgid ""
-"%s Your password is %d characters, greater than the expected maximum length "
-"of %d for MySpaceIM. Please shorten your password at http://profileedit."
-"myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try "
-"again."
-msgstr ""
-"%s Ihr Passwort hat %d Buchstaben, mehr als die erwartete maximale Länge von "
-"%d für MySpaceIM. Bitte kürzen Sie ihr Passwort unter http://profileedit."
-"myspace.com/index.cfm?fuseaction=accountSettings.changePassword und "
-"versuchen Sie es erneut."
+"%s Your password is %zu characters, which is longer than the maximum length "
+"of %d.  Please shorten your password at http://profileedit.myspace.com/index."
+"cfm?fuseaction=accountSettings.changePassword and try again."
+msgstr ""
+"%s Ihr Passwort hat %zu Buchstaben, mehr als die erwartete maximale Länge "
+"von %d.  Bitte kürzen Sie ihr Passwort unter http://profileedit.myspace.com/"
+"index.cfm?fuseaction=accountSettings.changePassword und versuchen Sie es "
+"erneut."
+
+msgid "Incorrect username or password"
+msgstr "Ungültiger Benutzername oder Passwort"
 
 msgid "MySpaceIM Error"
 msgstr "MySpaceIM-Fehler"
@@ -6100,9 +6105,6 @@
 msgid "Master archive is misconfigured"
 msgstr "Master-Archiv ist falsch konfiguriert"
 
-msgid "Incorrect username or password"
-msgstr "Ungültiger Benutzername oder Passwort"
-
 msgid "Could not recognize the host of the username you entered"
 msgstr ""
 "Konnte den Rechnernamen des Benutzers, den Sie eingegeben haben, nicht "
@@ -6633,6 +6635,29 @@
 "sein oder mit einem Buchstaben beginnen und nur Buchstaben, Ziffern und "
 "Leerzeichen enthalten oder nur aus Ziffern bestehen."
 
+#, c-format
+msgid "You may be disconnected shortly.  If so, check %s for updates."
+msgstr ""
+"Sie werden eventuell gleich abgemeldet. In diesem Fall, überprüfen Sie %s "
+"auf Updates."
+
+msgid "Unable to get a valid AIM login hash."
+msgstr "Konnte keinen gültigen AIM Login-Hash bekommen."
+
+#, c-format
+msgid "You may be disconnected shortly.  Check %s for updates."
+msgstr ""
+"Sie wurden in kurzer Zeit abgemeldet.  Überprüfen Sie %s wegen Updates."
+
+msgid "Unable to get a valid login hash."
+msgstr "Konnte keinen gültigen Login-Hash bekommen."
+
+msgid "Could Not Connect"
+msgstr "Verbinden nicht möglich"
+
+msgid "Received authorization"
+msgstr "Autorisierung empfangen"
+
 #. Unregistered username
 #. uid is not exist
 msgid "Invalid username."
@@ -6650,7 +6675,6 @@
 msgstr "Der AOL-Sofortnachrichtendienst ist zur Zeit nicht erreichbar."
 
 #. username connecting too frequently
-#. IP address connecting too frequently
 msgid ""
 "You have been connecting and disconnecting too frequently. Wait ten minutes "
 "and try again. If you continue to try, you will need to wait even longer."
@@ -6664,11 +6688,14 @@
 msgstr ""
 "Die Client-Version, die Sie nutzen ist zu alt. Bitte updaten Sie unter %s"
 
-msgid "Could Not Connect"
-msgstr "Verbinden nicht möglich"
-
-msgid "Received authorization"
-msgstr "Autorisierung empfangen"
+#. IP address connecting too frequently
+msgid ""
+"You have been connecting and disconnecting too frequently. Wait a minute and "
+"try again. If you continue to try, you will need to wait even longer."
+msgstr ""
+"Sie haben sich zu schnell an- und abgemeldet. Warten Sie eine Minute und "
+"versuchen Sie es noch einmal. Wenn Sie es weiterversuchen, müssen Sie sogar "
+"noch länger warten."
 
 msgid "The SecurID key entered is invalid."
 msgstr "Der eingegebene SecurID-Schlüssel ist falsch."
@@ -6685,23 +6712,6 @@
 msgid "_OK"
 msgstr "_OK"
 
-#, c-format
-msgid "You may be disconnected shortly.  If so, check %s for updates."
-msgstr ""
-"Sie werden eventuell gleich abgemeldet. In diesem Fall, überprüfen Sie %s "
-"auf Updates."
-
-msgid "Unable to get a valid AIM login hash."
-msgstr "Konnte keinen gültigen AIM Login-Hash bekommen."
-
-#, c-format
-msgid "You may be disconnected shortly.  Check %s for updates."
-msgstr ""
-"Sie wurden in kurzer Zeit abgemeldet.  Überprüfen Sie %s wegen Updates."
-
-msgid "Unable to get a valid login hash."
-msgstr "Konnte keinen gültigen Login-Hash bekommen."
-
 msgid "Password sent"
 msgstr "Passwort gesendet"
 
@@ -7233,6 +7243,7 @@
 msgid "Set User Info (web)..."
 msgstr "Benutzer-Info (Web) setzen..."
 
+#. This only happens when connecting with the old-style BUCP login
 msgid "Change Password (web)"
 msgstr "Ändere Passwort (Web)"
 
@@ -7262,6 +7273,9 @@
 msgid "Search for Buddy by Information"
 msgstr "Suche Buddy nach Information"
 
+msgid "Use clientLogin"
+msgstr "clientLogin benutzen"
+
 msgid ""
 "Always use AIM/ICQ proxy server for\n"
 "file transfers and direct IM (slower,\n"
@@ -9574,26 +9588,26 @@
 msgstr "Falsches Passwort"
 
 #. security lock from too many failed login attempts
-#, fuzzy
 msgid ""
 "Account locked: Too many failed login attempts.\n"
 "Logging into the Yahoo! website may fix this."
 msgstr ""
-"Unbekannte Fehlernummer %d. Vielleicht kann dies repariert werden, wenn Sie "
-"sich auf der Yahoo! Webseite anmelden."
+"Konto gesperrt: Zu viele fehlgeschlagene Login-Versuche\n"
+"Eventuell können Sie dies beheben, wenn Sie sich auf der Yahoo!-Webseite "
+"anmelden."
 
 #. the username does not exist
 msgid "Username does not exist"
 msgstr "Benutzername existiert nicht"
 
 #. indicates a lock of some description
-#, fuzzy
 msgid ""
 "Account locked: Unknown reason.\n"
 "Logging into the Yahoo! website may fix this."
 msgstr ""
-"Unbekannte Fehlernummer %d. Vielleicht kann dies repariert werden, wenn Sie "
-"sich auf der Yahoo! Webseite anmelden."
+"Konto gesperrt: Unbekannter Grund\n"
+" Eventuell können Sie dies beheben, wenn Sie sich auf der Yahoo!-Webseite "
+"anmelden."
 
 #. username or password missing
 msgid "Username or password missing"
@@ -9713,8 +9727,8 @@
 msgid "Start Doodling"
 msgstr "Anfangen zu malen"
 
-msgid "Activate which ID?"
-msgstr "Welche ID aktivieren?"
+msgid "Select the ID you want to activate"
+msgstr "Wählen Sie die ID die Sie aktivieren möchten"
 
 msgid "Join whom in chat?"
 msgstr "Wen wollen Sie zum Chat einladen?"
@@ -12516,8 +12530,11 @@
 msgid "Unknown.... Please report this!"
 msgstr "Unbekannt.... Bitte berichten Sie dieses Problem!"
 
-msgid "Smiley theme failed to unpack."
-msgstr "Smiley-Thema konnte nicht entpackt werden."
+msgid "Theme failed to unpack."
+msgstr "Thema konnte nicht entpackt werden."
+
+msgid "Theme failed to load."
+msgstr "Thema konnte nicht geladen werden."
 
 msgid "Install Theme"
 msgstr "Thema installieren"
@@ -13826,7 +13843,6 @@
 msgid "Highlighted Message Name Color"
 msgstr "Farbe des Absendernamens für hervorgehobene Nachrichten"
 
-#, fuzzy
 msgid "Typing Notification Color"
 msgstr "Farbe der Tipp-Benachrichtigung"
 
@@ -13862,19 +13878,17 @@
 msgid "Disable Typing Notification Text"
 msgstr "Tipp-Benachrichtigung aktivieren"
 
-#, fuzzy
 msgid "GTK+ Theme Control Settings"
-msgstr "Pidgin GTK+ Themenkontrolle"
-
-#, fuzzy
+msgstr "Themenkontroll-Einstellungen für GTK+"
+
 msgid "Colors"
-msgstr "Schließen"
+msgstr "Farben"
 
 msgid "Fonts"
 msgstr "Schrift"
 
 msgid "Miscellaneous"
-msgstr ""
+msgstr "Sonstiges"
 
 msgid "Gtkrc File Tools"
 msgstr "Gtkrc-Datei-Werkzeuge"