changeset 25960:43da17b9a27e

propagate from branch 'im.pidgin.pidgin' (head c5b982597812ab3c0fd2dbca9be31f173fda67bb) to branch 'im.pidgin.cpw.malu.xmpp.idle' (head 4379b3a32fa6bbd860103c93b291406542af52f9)
author Marcus Lundblad <ml@update.uu.se>
date Thu, 22 Jan 2009 20:39:12 +0000
parents d978395b4e18 (current diff) c814641afcf2 (diff)
children b018c91fb90b
files libpurple/protocols/jabber/buddy.c
diffstat 25 files changed, 275 insertions(+), 162 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue Jan 13 18:39:17 2009 +0000
+++ b/COPYRIGHT	Thu Jan 22 20:39:12 2009 +0000
@@ -63,6 +63,7 @@
 Damien Carbery
 Michael Carlson
 Keegan Carruthers-Smith
+Ludovico Cavedon
 Steve Cavilia
 Julien Cegarra
 Cerulean Studios, LLC
@@ -210,6 +211,7 @@
 Jaromír Karmazín
 John Kelm
 Jochen Kemnade
+Yann Kerherve
 Akuke Kok
 Kir Kolyshkin
 Konstantin Korikov
@@ -229,6 +231,7 @@
 Steve Láposi
 Daniel Larsson
 Peter Lawler
+Vadim Lebedev
 Ho-seok Lee
 Jean-Yves Lefort
 Moses Lei
--- a/ChangeLog	Tue Jan 13 18:39:17 2009 +0000
+++ b/ChangeLog	Thu Jan 22 20:39:12 2009 +0000
@@ -1,5 +1,15 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.5.5 (??/??/????):
+	libpurple:
+	* Fix transfer of buddy icons, custom smileys and files from the
+	latest WLM 9 official client. (Thomas Gibson-Robinson)
+	* Fix a crash when removing an account with an unknown protocol id.
+
+	Finch:
+	* Allow rebinding keys to change the focused widget (details in the
+	  man-page, look for GntBox::binding)
+
 version 2.5.4 (01/12/2009):
 	libpurple:
 	* Fix a connection timeout with empty Gadu-Gady buddy lists. (Martin
--- a/ChangeLog.API	Tue Jan 13 18:39:17 2009 +0000
+++ b/ChangeLog.API	Thu Jan 22 20:39:12 2009 +0000
@@ -1,5 +1,12 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.5.5 (??/??/2009):
+	libpurple:
+		Changed:
+		* purple_status_type_new now defaults "saveable" to TRUE.
+		  This was necessary in order to maintain the current behavior
+		  while fixing non-saveable statuses not to be saved.
+
 version 2.5.4 (01/12/2009):
 	perl:
 		Changed:
--- a/doc/finch.1.in	Tue Jan 13 18:39:17 2009 +0000
+++ b/doc/finch.1.in	Thu Jan 22 20:39:12 2009 +0000
@@ -296,6 +296,15 @@
 \fI~/.gntrc\fR correspond to the default keybindings for the actions:
 
 .br
+[GntBox::binding]
+.br
+tab = focus-next
+.br
+right = focus-next
+.br
+left = focus-prev
+
+.br
 [GntEntry::binding]
 .br
 c-a = cursor-home
--- a/finch/libgnt/gntbox.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/finch/libgnt/gntbox.c	Thu Jan 22 20:39:12 2009 +0000
@@ -21,6 +21,7 @@
  */
 
 #include "gntbox.h"
+#include "gntstyle.h"
 #include "gntutils.h"
 
 #include <string.h>
@@ -304,38 +305,38 @@
 gnt_box_key_pressed(GntWidget *widget, const char *text)
 {
 	GntBox *box = GNT_BOX(widget);
-	GntWidget *now;
+	gboolean ret;
+
+	if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_DISABLE_ACTIONS))
+		return FALSE;
 
 	if (box->active == NULL && !find_focusable_widget(box))
 		return FALSE;
 
 	if (gnt_widget_key_pressed(box->active, text))
 		return TRUE;
-	
+
+	/* This dance is necessary to make sure that the child widgets get a chance
+	   to trigger their bindings first */
+	GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_DISABLE_ACTIONS);
+	ret = gnt_widget_key_pressed(widget, text);
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_DISABLE_ACTIONS);
+	return ret;
+}
+
+static gboolean
+box_focus_change(GntBox *box, gboolean next)
+{
+	GntWidget *now;
 	now = box->active;
 
-	if (text[0] == 27)
-	{
-		if (strcmp(text, GNT_KEY_LEFT) == 0)
-		{
-			find_prev_focus(box);
-		}
-		else if (strcmp(text, GNT_KEY_RIGHT) == 0)
-		{
-			find_next_focus(box);
-		}
-		else if (strcmp(text, GNT_KEY_BACK_TAB) == 0)
-		{
-			find_prev_focus(box);
-		}
-	}
-	else if (text[0] == '\t')
-	{
+	if (next) {
 		find_next_focus(box);
+	} else {
+		find_prev_focus(box);
 	}
 
-	if (now && now != box->active)
-	{
+	if (now && now != box->active) {
 		gnt_widget_set_focus(now, FALSE);
 		gnt_widget_set_focus(box->active, TRUE);
 		return TRUE;
@@ -344,6 +345,18 @@
 	return FALSE;
 }
 
+static gboolean
+action_focus_next(GntBindable *bindable, GList *null)
+{
+	return box_focus_change(GNT_BOX(bindable), TRUE);
+}
+
+static gboolean
+action_focus_prev(GntBindable *bindable, GList *null)
+{
+	return box_focus_change(GNT_BOX(bindable), FALSE);
+}
+
 static void
 gnt_box_lost_focus(GntWidget *widget)
 {
@@ -556,6 +569,7 @@
 static void
 gnt_box_class_init(GntBoxClass *klass)
 {
+	GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass);
 	GObjectClass *gclass = G_OBJECT_CLASS(klass);
 	parent_class = GNT_WIDGET_CLASS(klass);
 	parent_class->destroy = gnt_box_destroy;
@@ -589,6 +603,15 @@
 				G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
 			)
 		);
+
+	gnt_bindable_class_register_action(bindable, "focus-next", action_focus_next,
+			"\t", NULL);
+	gnt_bindable_register_binding(bindable, "focus-next", GNT_KEY_RIGHT, NULL);
+	gnt_bindable_class_register_action(bindable, "focus-prev", action_focus_prev,
+			GNT_KEY_BACK_TAB, NULL);
+	gnt_bindable_register_binding(bindable, "focus-prev", GNT_KEY_LEFT, NULL);
+
+	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable);
 }
 
 static void
@@ -599,7 +622,7 @@
 	/* Initially make both the height and width resizable.
 	 * Update the flags as necessary when widgets are added to it. */
 	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y);
-	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS);
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_DISABLE_ACTIONS);
 	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
 	box->pad = 1;
 	box->fill = TRUE;
--- a/libpurple/account.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/account.c	Thu Jan 22 20:39:12 2009 +0000
@@ -257,15 +257,20 @@
 statuses_to_xmlnode(const PurplePresence *presence)
 {
 	xmlnode *node, *child;
-	GList *statuses, *status;
+	GList *statuses;
+	PurpleStatus *status;
 
 	node = xmlnode_new("statuses");
 
 	statuses = purple_presence_get_statuses(presence);
-	for (status = statuses; status != NULL; status = status->next)
+	for (; statuses != NULL; statuses = statuses->next)
 	{
-		child = status_to_xmlnode((PurpleStatus *)status->data);
-		xmlnode_insert_child(node, child);
+		status = statuses->data;
+		if (purple_status_type_is_saveable(purple_status_get_type(status)))
+		{
+			child = status_to_xmlnode(status);
+			xmlnode_insert_child(node, child);
+		}
 	}
 
 	return node;
--- a/libpurple/blist.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/blist.c	Thu Jan 22 20:39:12 2009 +0000
@@ -1811,7 +1811,7 @@
 	PurpleGroup *group;
 	struct _purple_hbuddy hb;
 	PurplePlugin *prpl;
-	PurplePluginProtocolInfo *prpl_info;
+	PurplePluginProtocolInfo *prpl_info = NULL;
 
 	g_return_if_fail(buddy != NULL);
 
@@ -1872,7 +1872,8 @@
 	 * can free proto_data
 	 */
 	prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	if (prpl)
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 	if (prpl_info && prpl_info->buddy_free)
 		prpl_info->buddy_free(buddy);
 
--- a/libpurple/core.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/core.c	Thu Jan 22 20:39:12 2009 +0000
@@ -137,7 +137,7 @@
 	 * subsystem right away too.
 	 */
 	purple_plugins_init();
-	
+
 	/* Initialize all static protocols. */
 	static_proto_init();
 
@@ -198,10 +198,22 @@
 	/* Transmission ends */
 	purple_connections_disconnect_all();
 
+	/* The SSL plugins must be uninit before they're unloaded */
+	purple_ssl_uninit();
+
+	/* Unload all plugins before the UI because UI plugins might call
+	 * UI-specific functions */
+	purple_debug_info("main", "Unloading all plugins\n");
+	purple_plugins_destroy_all();
+
+	/* Shut down the UI before all the subsystems */
+	ops = purple_core_get_ui_ops();
+	if (ops != NULL && ops->quit != NULL)
+		ops->quit();
+
 	/* Save .xml files, remove signals, etc. */
 	purple_smileys_uninit();
 	purple_idle_uninit();
-	purple_ssl_uninit();
 	purple_pounces_uninit();
 	purple_blist_uninit();
 	purple_ciphers_uninit();
@@ -213,7 +225,6 @@
 	purple_accounts_uninit();
 	purple_savedstatuses_uninit();
 	purple_status_uninit();
-	purple_prefs_uninit();
 	purple_sound_uninit();
 	purple_xfers_uninit();
 	purple_proxy_uninit();
@@ -221,19 +232,15 @@
 	purple_imgstore_uninit();
 	purple_network_uninit();
 
-	purple_debug_info("main", "Unloading all plugins\n");
-	purple_plugins_destroy_all();
-
-	ops = purple_core_get_ui_ops();
-	if (ops != NULL && ops->quit != NULL)
-		ops->quit();
-
+	/* Everything after this must not try to read any prefs */
+	purple_prefs_uninit();
 	purple_plugins_uninit();
 #ifdef HAVE_DBUS
 	purple_dbus_uninit();
 #endif
 
 	purple_cmds_uninit();
+	/* Everything after this cannot try to write things to the confdir */
 	purple_util_uninit();
 
 	purple_signals_uninit();
--- a/libpurple/prefs.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/prefs.c	Thu Jan 22 20:39:12 2009 +0000
@@ -693,12 +693,15 @@
 	char *name;
 	GSList *l;
 
-	if(!pref || pref == &prefs)
+	if(!pref)
 		return;
 
 	while(pref->first_child)
 		remove_pref(pref->first_child);
 
+	if(pref == &prefs)
+		return;
+
 	if(pref->parent->first_child == pref) {
 		pref->parent->first_child = pref->sibling;
 	} else {
@@ -711,7 +714,8 @@
 
 	name = pref_full_name(pref);
 
-	purple_debug_info("prefs", "removing pref %s\n", name);
+	if (prefs_loaded)
+		purple_debug_info("prefs", "removing pref %s\n", name);
 
 	g_hash_table_remove(prefs_hash, name);
 	g_free(name);
@@ -1451,6 +1455,9 @@
 		sync_prefs();
 	}
 
+	prefs_loaded = FALSE;
+	purple_prefs_destroy();
+	g_hash_table_destroy(prefs_hash);
+	prefs_hash = NULL;
 
-	prefs_loaded = FALSE;
 }
--- a/libpurple/protocols/gg/gg.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/gg/gg.c	Thu Jan 22 20:39:12 2009 +0000
@@ -755,18 +755,29 @@
 
 /* ----- CONFERENCES ---------------------------------------------------- */
 
-static void ggp_callback_add_to_chat_ok(PurpleConnection *gc, PurpleRequestFields *fields)
+static void ggp_callback_add_to_chat_ok(PurpleBuddy *buddy, PurpleRequestFields *fields)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info;
+	PurpleConnection *conn;
 	PurpleRequestField *field;
-	/* TODO: sel may be null. */
 	GList *sel;
 
+	conn = purple_account_get_connection(purple_buddy_get_account(buddy));
+
+	g_return_if_fail(conn != NULL);
+
+	info = conn->proto_data;
+
 	field = purple_request_fields_get_field(fields, "name");
 	sel = purple_request_field_list_get_selected(field);
 
-	ggp_confer_participants_add_uin(gc, sel->data, info->tmp_buddy);
-	info->tmp_buddy = 0;
+	if (sel == NULL) {
+		purple_debug_error("gg", "No chat selected\n");
+		return;
+	}
+
+	ggp_confer_participants_add_uin(conn, sel->data,
+					ggp_str_to_uin(purple_buddy_get_name(buddy)));
 }
 
 static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored)
@@ -786,9 +797,6 @@
 	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 	info = gc->proto_data;
 
-	/* TODO: It tmp_buddy != 0 then stop! */
-	info->tmp_buddy = ggp_str_to_uin(purple_buddy_get_name(buddy));
-
 	fields = purple_request_fields_new();
 	group = purple_request_field_group_new(NULL);
 	purple_request_fields_add_group(fields, group);
@@ -796,8 +804,7 @@
 	field = purple_request_field_list_new("name", "Chat name");
 	for (l = info->chats; l != NULL; l = l->next) {
 		GGPChat *chat = l->data;
-		purple_request_field_list_add(field, g_strdup(chat->name),
-					    g_strdup(chat->name));
+		purple_request_field_list_add(field, chat->name, chat->name);
 	}
 	purple_request_field_group_add_field(group, field);
 
@@ -810,8 +817,8 @@
 			fields,
 			_("Add"), G_CALLBACK(ggp_callback_add_to_chat_ok),
 			_("Cancel"), NULL,
-			purple_connection_get_account(gc), NULL, NULL,			  
-			gc);
+			purple_connection_get_account(gc), NULL, NULL,
+			buddy);
 	g_free(msg);
 }
 
@@ -1699,14 +1706,20 @@
 {
 	PurpleMenuAction *act;
 	GList *m = NULL;
+	PurpleAccount *account;
+	GGPInfo *info;
 
 	if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
 		return NULL;
 
-	act = purple_menu_action_new(_("Add to chat"),
-	                           PURPLE_CALLBACK(ggp_bmenu_add_to_chat),
-	                           NULL, NULL);
-	m = g_list_append(m, act);
+	account = purple_buddy_get_account((PurpleBuddy *) node);
+	info = purple_account_get_connection(account)->proto_data;
+	if (info->chats) {
+		act = purple_menu_action_new(_("Add to chat"),
+			PURPLE_CALLBACK(ggp_bmenu_add_to_chat),
+			NULL, NULL);
+		m = g_list_append(m, act);
+	}
 
 	/* Using a blist node boolean here is also wrong.
 	 * Once the Block and Unblock actions are added to the core,
--- a/libpurple/protocols/gg/gg.h	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/gg/gg.h	Thu Jan 22 20:39:12 2009 +0000
@@ -61,10 +61,7 @@
 	GGPToken *token;
 	GList *chats;
 	GGPSearches *searches;
-
-	uin_t tmp_buddy;
 	int chats_count;
-
 	GList *pending_richtext_messages;
 	GHashTable *pending_images;
 } GGPInfo;
--- a/libpurple/protocols/jabber/auth.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/jabber/auth.c	Thu Jan 22 20:39:12 2009 +0000
@@ -749,8 +749,8 @@
 
 				val_end = cur;
 				while (val_end != val_start && (*val_end == ' ' || *val_end == ',' || *val_end == '\t'
-						|| *val_end == '\r' || *val_start == '\n'
-						|| *val_end == '"'))
+						|| *val_end == '\r' || *val_end == '\n'
+						|| *val_end == '"'  || *val_end == '\0'))
 					val_end--;
 
 				if (val_start != val_end)
--- a/libpurple/protocols/jabber/buddy.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Thu Jan 22 20:39:12 2009 +0000
@@ -2560,7 +2560,7 @@
 	JabberBuddyResource *jbr = jabber_buddy_find_resource((JabberBuddy*)jb, NULL);
 
 	if (!jbr) {
-		purple_debug_error("jabber",
+		purple_debug_info("jabber",
 			"Unable to find caps: buddy might be offline\n");
 		return FALSE;
 	}
--- a/libpurple/protocols/msn/session.h	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/msn/session.h	Thu Jan 22 20:39:12 2009 +0000
@@ -123,7 +123,7 @@
 	} passport_info;
 
 	GHashTable *soap_table;
-	int soap_cleanup_handle;
+	guint soap_cleanup_handle;
 };
 
 /**
--- a/libpurple/protocols/msn/slpcall.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Thu Jan 22 20:39:12 2009 +0000
@@ -206,7 +206,7 @@
 	body = slpmsg->buffer;
 	body_len = slpmsg->size;
 
-	if (slpmsg->flags == 0x0)
+	if (slpmsg->flags == 0x0 || slpmsg->flags == 0x1000000)
 	{
 		char *body_str;
 
@@ -214,7 +214,9 @@
 		slpcall = msn_slp_sip_recv(slplink, body_str);
 		g_free(body_str);
 	}
-	else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
+	else if (slpmsg->flags == 0x20 ||
+	         slpmsg->flags == 0x1000020 ||
+	         slpmsg->flags == 0x1000030)
 	{
 		slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id);
 
@@ -237,6 +239,9 @@
 			msn_slpcall_session_init(slpcall);
 	}
 #endif
+	else
+		purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%08lx\n",
+		                     slpmsg->flags);
 
 	return slpcall;
 }
--- a/libpurple/protocols/msn/slplink.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/msn/slplink.c	Thu Jan 22 20:39:12 2009 +0000
@@ -281,7 +281,8 @@
 		g_list_append(slpmsg->msgs, msg);
 	msn_slplink_send_msg(slplink, msg);
 
-	if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) &&
+	if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||
+	     slpmsg->flags == 0x1000030) &&
 		(slpmsg->slpcall != NULL))
 	{
 		slpmsg->slpcall->progress = TRUE;
@@ -316,7 +317,8 @@
 	else
 	{
 		/* The whole message has been sent */
-		if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
+		if (slpmsg->flags == 0x20 ||
+		    slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030)
 		{
 			if (slpmsg->slpcall != NULL)
 			{
@@ -362,7 +364,8 @@
 		msg->msnslp_header.ack_size = slpmsg->ack_size;
 		msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id;
 	}
-	else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
+	else if (slpmsg->flags == 0x20 ||
+	         slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030)
 	{
 		MsnSlpCall *slpcall;
 		slpcall = slpmsg->slpcall;
@@ -398,6 +401,8 @@
 void
 msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
 {
+	g_return_if_fail(slpmsg != NULL);
+
 	slpmsg->id = slplink->slp_seq_id++;
 
 	g_queue_push_tail(slplink->slp_msg_queue, slpmsg);
@@ -530,7 +535,8 @@
 
 			if (slpmsg->slpcall != NULL)
 			{
-				if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
+				if (slpmsg->flags == 0x20 ||
+				    slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030)
 				{
 					PurpleXfer *xfer;
 
@@ -593,7 +599,8 @@
 			memcpy(slpmsg->buffer + offset, data, len);
 	}
 
-	if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) &&
+	if ((slpmsg->flags == 0x20 ||
+	     slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) &&
 		(slpmsg->slpcall != NULL))
 	{
 		slpmsg->slpcall->progress = TRUE;
@@ -628,8 +635,9 @@
 				msn_directconn_send_handshake(directconn);
 #endif
 		}
-		else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 ||
-				 slpmsg->flags == 0x1000030)
+		else if (slpmsg->flags == 0x00 || slpmsg->flags == 0x1000000 ||  
+		         slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||  
+		         slpmsg->flags == 0x1000030)
 		{
 			/* Release all the messages and send the ACK */
 
--- a/libpurple/protocols/oscar/odc.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/oscar/odc.c	Thu Jan 22 20:39:12 2009 +0000
@@ -394,7 +394,7 @@
 		}
 	}
 
-	/* Send the message */
+	/* Display the message we received */
 	imflags = 0;
 	if (images != NULL)
 		imflags |= PURPLE_MESSAGE_IMAGES;
--- a/libpurple/protocols/oscar/oscar.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Jan 22 20:39:12 2009 +0000
@@ -4337,8 +4337,7 @@
 	}
 	g_string_free(data, TRUE);
 
-	peer_odc_send_im(conn, msg->str, msg->len, charset,
-			imflags & PURPLE_MESSAGE_AUTO_RESP);
+	peer_odc_send_im(conn, msg->str, msg->len, charset, imflags);
 	g_string_free(msg, TRUE);
 }
 
--- a/libpurple/prpl.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/prpl.c	Thu Jan 22 20:39:12 2009 +0000
@@ -190,7 +190,7 @@
 
 	g_return_if_fail(account != NULL);
 	g_return_if_fail(name    != NULL);
-	g_return_if_fail(purple_account_is_connected(account));
+	g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
 
 	if ((list = purple_find_buddies(account, name)) == NULL)
 		return;
--- a/libpurple/status.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/status.c	Thu Jan 22 20:39:12 2009 +0000
@@ -250,7 +250,7 @@
 {
 	g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, NULL);
 
-	return purple_status_type_new_full(primitive, id, name, FALSE,
+	return purple_status_type_new_full(primitive, id, name, TRUE,
 			user_settable, FALSE);
 }
 
--- a/libpurple/status.h	Tue Jan 13 18:39:17 2009 +0000
+++ b/libpurple/status.h	Thu Jan 22 20:39:12 2009 +0000
@@ -199,8 +199,8 @@
 										  gboolean independent);
 
 /**
- * Creates a new status type with some default values (not
- * savable and not independent).
+ * Creates a new status type with some default values (
+ * saveable and not independent).
  *
  * @param primitive     The primitive status type.
  * @param id            The ID of the status type, or @c NULL to use the id of
--- a/pidgin/gtkaccount.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/pidgin/gtkaccount.c	Thu Jan 22 20:39:12 2009 +0000
@@ -84,6 +84,13 @@
 
 typedef struct
 {
+	GtkWidget *widget;
+	gchar *setting;
+	PurplePrefType type;
+} ProtocolOptEntry;
+
+typedef struct
+{
 	PidginAccountDialogType type;
 
 	PurpleAccount *account;
@@ -740,21 +747,22 @@
 	char *title, *tmp;
 	const char *str_value;
 	gboolean bool_value;
+	ProtocolOptEntry *opt_entry;
 
 	if (dialog->protocol_frame != NULL) {
 		gtk_widget_destroy(dialog->protocol_frame);
 		dialog->protocol_frame = NULL;
 	}
 
-	if (dialog->protocol_opt_entries != NULL) {
-		g_list_free(dialog->protocol_opt_entries);
-		dialog->protocol_opt_entries = NULL;
-	}
-
 	if (dialog->prpl_info == NULL ||
-		dialog->prpl_info->protocol_options == NULL) {
-
+			dialog->prpl_info->protocol_options == NULL)
 		return;
+
+	while (dialog->protocol_opt_entries != NULL) {
+		ProtocolOptEntry *opt_entry = dialog->protocol_opt_entries->data;
+		g_free(opt_entry->setting);
+		g_free(opt_entry);
+		dialog->protocol_opt_entries = g_list_delete_link(dialog->protocol_opt_entries, dialog->protocol_opt_entries);
 	}
 
 	account = dialog->account;
@@ -778,7 +786,11 @@
 	{
 		option = (PurpleAccountOption *)l->data;
 
-		switch (purple_account_option_get_type(option))
+		opt_entry = g_new0(ProtocolOptEntry, 1);
+		opt_entry->type = purple_account_option_get_type(option);
+		opt_entry->setting = g_strdup(purple_account_option_get_setting(option));
+
+		switch (opt_entry->type)
 		{
 			case PURPLE_PREF_BOOLEAN:
 				if (account == NULL ||
@@ -795,7 +807,7 @@
 				}
 
 				tmp = g_strconcat("_", purple_account_option_get_text(option), NULL);
-				check = gtk_check_button_new_with_mnemonic(tmp);
+				opt_entry->widget = check = gtk_check_button_new_with_mnemonic(tmp);
 				g_free(tmp);
 
 				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check),
@@ -803,10 +815,6 @@
 
 				gtk_box_pack_start(GTK_BOX(vbox), check, FALSE, FALSE, 0);
 				gtk_widget_show(check);
-
-				dialog->protocol_opt_entries =
-					g_list_append(dialog->protocol_opt_entries, check);
-
 				break;
 
 			case PURPLE_PREF_INT:
@@ -825,19 +833,13 @@
 
 				g_snprintf(buf, sizeof(buf), "%d", int_value);
 
-				entry = gtk_entry_new();
+				opt_entry->widget = entry = gtk_entry_new();
 				gtk_entry_set_text(GTK_ENTRY(entry), buf);
 
 				title = g_strdup_printf("_%s:",
 						purple_account_option_get_text(option));
-
 				add_pref_box(dialog, vbox, title, entry);
-
 				g_free(title);
-
-				dialog->protocol_opt_entries =
-					g_list_append(dialog->protocol_opt_entries, entry);
-
 				break;
 
 			case PURPLE_PREF_STRING:
@@ -854,7 +856,7 @@
 						purple_account_option_get_default_string(option));
 				}
 
-				entry = gtk_entry_new();
+				opt_entry->widget = entry = gtk_entry_new();
 				if (purple_account_option_get_masked(option))
 				{
 					gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
@@ -867,14 +869,8 @@
 
 				title = g_strdup_printf("_%s:",
 						purple_account_option_get_text(option));
-
 				add_pref_box(dialog, vbox, title, entry);
-
 				g_free(title);
-
-				dialog->protocol_opt_entries =
-					g_list_append(dialog->protocol_opt_entries, entry);
-
 				break;
 
 			case PURPLE_PREF_STRING_LIST:
@@ -896,7 +892,7 @@
 
 				list = purple_account_option_get_list(option);
 				model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
-				combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
+				opt_entry->widget = combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
 
 				/* Loop through list of PurpleKeyValuePair items */
 				for (node = list; node != NULL; node = node->next) {
@@ -928,20 +924,21 @@
 
 				title = g_strdup_printf("_%s:",
 						purple_account_option_get_text(option));
-
 				add_pref_box(dialog, vbox, title, combo);
-
 				g_free(title);
-
-				dialog->protocol_opt_entries =
-					g_list_append(dialog->protocol_opt_entries, combo);
-
 				break;
 
-
 			default:
-				break;
+				purple_debug_error("gtkaccount", "Invalid Account Option pref type (%d)\n",
+						   opt_entry->type);
+				g_free(opt_entry->setting);
+				g_free(opt_entry);
+				continue;
 		}
+
+		dialog->protocol_opt_entries =
+			g_list_append(dialog->protocol_opt_entries, opt_entry);
+
 	}
 }
 
@@ -1152,7 +1149,12 @@
 	gtk_widget_destroy(dialog->window);
 
 	g_list_free(dialog->user_split_entries);
-	g_list_free(dialog->protocol_opt_entries);
+	while (dialog->protocol_opt_entries != NULL) {
+		ProtocolOptEntry *opt_entry = dialog->protocol_opt_entries->data;
+		g_free(opt_entry->setting);
+		g_free(opt_entry);
+		dialog->protocol_opt_entries = g_list_delete_link(dialog->protocol_opt_entries, dialog->protocol_opt_entries);
+	}
 	g_free(dialog->protocol_id);
 	g_object_unref(dialog->sg);
 
@@ -1317,46 +1319,38 @@
 
 	/* Add the protocol settings */
 	if (dialog->prpl_info) {
-		for (l = dialog->prpl_info->protocol_options,
-				l2 = dialog->protocol_opt_entries;
-				l != NULL && l2 != NULL;
-				l = l->next, l2 = l2->next) {
-
-			PurplePrefType type;
-			PurpleAccountOption *option = l->data;
-			GtkWidget *widget = l2->data;
-			GtkTreeIter iter;
-			const char *setting;
-			char *value2;
-			int int_value;
-			gboolean bool_value;
-
-			type = purple_account_option_get_type(option);
-
-			setting = purple_account_option_get_setting(option);
-
-			switch (type) {
+		ProtocolOptEntry *opt_entry;
+		GtkTreeIter iter;
+		char *value2;
+		int int_value;
+		gboolean bool_value;
+
+		for (l2 = dialog->protocol_opt_entries; l2; l2 = l2->next) {
+
+			opt_entry = l2->data;
+
+			switch (opt_entry->type) {
 				case PURPLE_PREF_STRING:
-					value = gtk_entry_get_text(GTK_ENTRY(widget));
-					purple_account_set_string(account, setting, value);
+					value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget));
+					purple_account_set_string(account, opt_entry->setting, value);
 					break;
 
 				case PURPLE_PREF_INT:
-					int_value = atoi(gtk_entry_get_text(GTK_ENTRY(widget)));
-					purple_account_set_int(account, setting, int_value);
+					int_value = atoi(gtk_entry_get_text(GTK_ENTRY(opt_entry->widget)));
+					purple_account_set_int(account, opt_entry->setting, int_value);
 					break;
 
 				case PURPLE_PREF_BOOLEAN:
 					bool_value =
-						gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
-					purple_account_set_bool(account, setting, bool_value);
+						gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(opt_entry->widget));
+					purple_account_set_bool(account, opt_entry->setting, bool_value);
 					break;
 
 				case PURPLE_PREF_STRING_LIST:
 					value2 = NULL;
-					if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter))
-						gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)), &iter, 1, &value2, -1);
-					purple_account_set_string(account, setting, value2);
+					if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(opt_entry->widget), &iter))
+						gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(opt_entry->widget)), &iter, 1, &value2, -1);
+					purple_account_set_string(account, opt_entry->setting, value2);
 					break;
 
 				default:
--- a/pidgin/gtkmain.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/pidgin/gtkmain.c	Thu Jan 22 20:39:12 2009 +0000
@@ -322,9 +322,6 @@
 	pidgin_session_end();
 #endif
 
-	/* Save the plugins we have loaded for next time. */
-	pidgin_plugins_save();
-
 	/* Uninit */
 	pidgin_smileys_uninit();
 	pidgin_conversations_uninit();
--- a/pidgin/pidgintooltip.c	Tue Jan 13 18:39:17 2009 +0000
+++ b/pidgin/pidgintooltip.c	Thu Jan 22 20:39:12 2009 +0000
@@ -82,7 +82,8 @@
 static void
 destroy_tooltip_data(PidginTooltipData *data)
 {
-	gtk_tree_path_free(data->common.treeview.path);
+	if (data->common.treeview.path)
+		gtk_tree_path_free(data->common.treeview.path);
 	pidgin_tooltip_destroy();
 	g_free(data);
 }
@@ -380,7 +381,7 @@
 
 	g_signal_connect(G_OBJECT(widget), "motion-notify-event", G_CALLBACK(widget_motion_cb), wdata);
 	g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL);
-	g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(g_free), wdata);
+	g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(destroy_tooltip_data), wdata);
 	return TRUE;
 }
 
--- a/po/l10n.xsl	Tue Jan 13 18:39:17 2009 +0000
+++ b/po/l10n.xsl	Thu Jan 22 20:39:12 2009 +0000
@@ -1,22 +1,47 @@
 <?xml version='1.0' ?>
 <xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
+		<xsl:output
+		method="html"
+		omit-xml-declaration="yes"
+		doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
+		doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+		indent="yes"
+		/>
 	<xsl:template match='/project'>
-		<html>
+		<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 			<head>
 				<title><xsl:value-of select='@name'/> translation statistics</title>
+				<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 				<!-- <link rel="Stylesheet" href="/gaim.css" type="text/css" media="screen" /> -->
-				<style>
+				<style type="text/css">
 					.bargraph {
 						width: 200px;
 						height: 20px;
-						background: black;
+						background-color: red;
 						border-collapse: collapse;
 						border-spacing: 0px;
 						margin: 0px;
 						border: 0px;
 						padding: 0px;
 					}
-
+					.translated { 
+						background-color: green; 
+						padding: 0px;
+					}
+					.fuzzy { 
+						background-color: blue; 
+						padding: 0px;
+					}
+					.untranslated { 
+						background-color: red; 
+						padding: 0px;
+					}
+					td.sep {
+						padding-right: 10px;
+					}
+					th {
+						text-align: left;
+					}
 				</style>
 			</head>
 			<body>
@@ -28,21 +53,23 @@
 						<xsl:sort select='@code' />
 						<tr>
 							<td><a><xsl:attribute name='href'><xsl:value-of select='@code'/>.po</xsl:attribute><xsl:value-of select='@name'/> (<xsl:value-of select='@code'/>)</a></td>
-							<td><xsl:value-of select='@translated'/></td><td><xsl:value-of select="format-number(@translated div ../@strings * 100,'#.##')"/> %</td>
-							<td><xsl:value-of select='@fuzzy'/></td><td><xsl:value-of select="format-number(@fuzzy div ../@strings * 100,'#.##')"/> %</td>
-							<td><xsl:value-of select='../@strings - (@translated + @fuzzy)'/></td><td><xsl:value-of select="format-number((../@strings - (@translated + @fuzzy)) div ../@strings * 100,'#.##')"/> %</td>
+							<td><xsl:value-of select='@translated'/></td>
+							<td class='sep'><xsl:value-of select="format-number(@translated div ../@strings * 100,'#.##')"/> %</td>
+							<td><xsl:value-of select='@fuzzy'/></td>
+							<td class='sep'><xsl:value-of select="format-number(@fuzzy div ../@strings * 100,'#.##')"/> %</td>
+							<td><xsl:value-of select='../@strings - (@translated + @fuzzy)'/></td>
+							<td><xsl:value-of select="format-number((../@strings - (@translated + @fuzzy)) div ../@strings * 100,'#.##')"/> %</td>
 						<td>
 							<table class='bargraph'><tr>
-									<td bgcolor='green'><xsl:attribute name='width'><xsl:value-of select='round(@translated div ../@strings * 200)'/>px;</xsl:attribute></td>
-									<td bgcolor='blue'><xsl:attribute name='width'><xsl:value-of select='round(@fuzzy div ../@strings * 200)'/>px;</xsl:attribute></td>
-									<!-- <td bgcolor='red'><xsl:attribute name='width'><xsl:value-of select='200 - round((@translated + @fuzzy) div ../@strings * 200)'/>px;</xsl:attribute></td> -->
-									<td bgcolor='red'></td>
+									<td class="translated"><xsl:attribute name='style'>width:<xsl:value-of select='round(@translated div ../@strings * 200)'/>px;</xsl:attribute></td>
+									<td class="fuzzy"><xsl:attribute name='style'>width:<xsl:value-of select='round(@fuzzy div ../@strings * 200)'/>px;</xsl:attribute></td>
+									<td class="untranslated"><xsl:attribute name='style'>width:<xsl:value-of select='round((../@strings - @translated - @fuzzy) div ../@strings * 200)'/>px;</xsl:attribute></td>
 							</tr></table>
 						</td>
 						</tr>
 					</xsl:for-each>
 				</table>
-				<a><xsl:attribute name='href'><xsl:value-of select='@pofile'/></xsl:attribute><xsl:value-of select='@pofile'/></a> generated on <xsl:value-of select='@generated'/>
+				<p><a><xsl:attribute name='href'><xsl:value-of select='@pofile'/></xsl:attribute><xsl:value-of select='@pofile'/></a> generated on <xsl:value-of select='@generated'/></p>
 				<!-- </div> -->
 			</body>
 		</html>