changeset 22464:53ffaf968813

merge of '409dce18b3381a807c9f3a692d19bc38e55ecf2b' and '90b8f3a86dc4e3a40bee83272b9def28ae2b2602'
author Will Thompson <will.thompson@collabora.co.uk>
date Thu, 13 Mar 2008 18:06:57 +0000
parents 8445ea581a6e (current diff) 8aa07f5d1524 (diff)
children 77b6ff5bdb07 5a85bded9ae8
files ChangeLog
diffstat 29 files changed, 228 insertions(+), 161 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Mar 12 20:42:59 2008 +0000
+++ b/ChangeLog	Thu Mar 13 18:06:57 2008 +0000
@@ -14,11 +14,16 @@
 	* Fix incorrectly marking some Yahoo! contacts as blocked
 	* Improved handling of UTF-8 group names on ICQ (beret)
 	* Fix a crash when starting if you have a Zephyr account
+	* Increase XMPP ping timeout to 120 seconds, to prevent poor network
+	  connections from timing out unnecessarily.
 
 	Pidgin:
 	* Remove a workaround for older versions gstreamer that was causing
 	  crashes on some non-Linux systems such as HPUX
 	* Fix some cases of the conversation input entry area being 1 pixel high
+	* Fix for displaying channel & buddy names in conversation window when
+	  they have '&' in them
+	* Some memory leak fixes, especially in the Text Replacement plugin
 
 	Finch:
 	* Fix compiling with Glib older than 2.6
--- a/libpurple/account.h	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/account.h	Thu Mar 13 18:06:57 2008 +0000
@@ -31,7 +31,9 @@
 #include <glib.h>
 #include <glib-object.h>
 
+/** @copydoc _PurpleAccountUiOps */
 typedef struct _PurpleAccountUiOps PurpleAccountUiOps;
+/** @copydoc _PurpleAccount */
 typedef struct _PurpleAccount      PurpleAccount;
 
 typedef gboolean (*PurpleFilterAccountFunc)(PurpleAccount *account);
@@ -105,6 +107,8 @@
 	void (*_purple_reserved4)(void);
 };
 
+/** Structure representing an account.
+ */
 struct _PurpleAccount
 {
 	char *username;             /**< The username.                          */
--- a/libpurple/core.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/core.c	Thu Mar 13 18:06:57 2008 +0000
@@ -50,7 +50,9 @@
 #include "util.h"
 
 #ifdef HAVE_DBUS
-#  define DBUS_API_SUBJECT_TO_CHANGE
+#  ifndef DBUS_API_SUBJECT_TO_CHANGE
+#    define DBUS_API_SUBJECT_TO_CHANGE
+#  endif
 #  include <dbus/dbus.h>
 #  include "dbus-purple.h"
 #  include "dbus-server.h"
--- a/libpurple/dbus-server.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/dbus-server.c	Thu Mar 13 18:06:57 2008 +0000
@@ -21,7 +21,9 @@
  *
  */
 
+#ifndef DBUS_API_SUBJECT_TO_CHANGE
 #define DBUS_API_SUBJECT_TO_CHANGE
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
--- a/libpurple/log.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/log.c	Thu Mar 13 18:06:57 2008 +0000
@@ -1666,6 +1666,7 @@
 					}
 				}
 				fclose(index);
+				purple_stringref_unref(pathref);
 
 				return list;
 			}
@@ -1829,11 +1830,11 @@
 			purple_debug_warning("log", "Failed to rename index temp file \"%s\" to \"%s\": %s\n",
 			                   index_tmp, pathstr, g_strerror(errno));
 			g_unlink(index_tmp);
-			g_free(index_tmp);
 		}
 		else
 			purple_debug_info("log", "Built index: %s\n", pathstr);
 
+		g_free(index_tmp);
 		g_free(pathstr);
 	}
 	return list;
--- a/libpurple/ntlm.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/ntlm.c	Thu Mar 13 18:06:57 2008 +0000
@@ -31,6 +31,7 @@
 #include "util.h"
 #include "ntlm.h"
 #include "cipher.h"
+#include "debug.h"
 #include <string.h>
 
 #define NTLM_NEGOTIATE_NTLM2_KEY 0x00080000
@@ -291,20 +292,32 @@
 
 	tmp = (char *)tmsg + sizeof(struct type3_message);
 
-	ucs2le = g_convert(domain, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL);
-	memcpy(tmp, ucs2le, domainlen);
-	g_free(ucs2le);
-	tmp += domainlen;
+	ucs2le = g_convert(domain, -1, "UTF-16LE", "UTF-8", NULL, NULL, NULL);
+	if (ucs2le != NULL) {
+		memcpy(tmp, ucs2le, domainlen);
+		g_free(ucs2le);
+		tmp += domainlen;
+	} else {
+		purple_debug_info("ntlm", "Unable to encode domain in UTF-16LE.\n");
+	}
 
-	ucs2le = g_convert(username, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL);
-	memcpy(tmp, ucs2le, usernamelen);
-	g_free(ucs2le);
-	tmp += usernamelen;
+	ucs2le = g_convert(username, -1, "UTF-16LE", "UTF-8", NULL, NULL, NULL);
+	if (ucs2le != NULL) {
+		memcpy(tmp, ucs2le, usernamelen);
+		g_free(ucs2le);
+		tmp += usernamelen;
+	} else {
+		purple_debug_info("ntlm", "Unable to encode username in UTF-16LE.\n");
+	}
 
-	ucs2le = g_convert(hostname, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL);
-	memcpy(tmp, ucs2le, hostnamelen);
-	g_free(ucs2le);
-	tmp += hostnamelen;
+	ucs2le = g_convert(hostname, -1, "UTF-16LE", "UTF-8", NULL, NULL, NULL);
+	if (ucs2le != NULL) {
+		memcpy(tmp, ucs2le, hostnamelen);
+		g_free(ucs2le);
+		tmp += hostnamelen;
+	} else {
+		purple_debug_info("ntlm", "Unable to encode hostname in UTF-16LE.\n");
+	}
 
 	/* LM */
 	if (passwlen > 14)
@@ -327,7 +340,7 @@
 	tmp += 0x18;
 
 	/* NTLM */
-	/* Convert the password to UCS-2LE */
+	/* Convert the password to UTF-16LE */
 	lennt = strlen(passw);
 	for (idx = 0; idx < lennt; idx++)
 	{
--- a/libpurple/plugins/joinpart.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/plugins/joinpart.c	Thu Mar 13 18:06:57 2008 +0000
@@ -77,7 +77,7 @@
 {
 	PurpleConvChat *chat;
 	int threshold;
-	struct joinpart_key *key;
+	struct joinpart_key key;
 	time_t *last_said;
 
 	g_return_val_if_fail(conv != NULL, FALSE);
@@ -94,10 +94,9 @@
 		return FALSE;
 
 	/* Only show the notice if the user has spoken recently. */
-	key = g_new(struct joinpart_key, 1);
-	key->conv = conv;
-	key->user = g_strdup(name);
-	last_said = g_hash_table_lookup(users, key);
+	key.conv = conv;
+	key.user = (gchar *)name;
+	last_said = g_hash_table_lookup(users, &key);
 	if (last_said != NULL)
 	{
 		int delay = purple_prefs_get_int(DELAY_PREF);
--- a/libpurple/protocols/irc/dcc_send.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/protocols/irc/dcc_send.c	Thu Mar 13 18:06:57 2008 +0000
@@ -135,7 +135,7 @@
 		
 		purple_xfer_set_end_fnc(xfer, irc_dccsend_recv_destroy);
 		purple_xfer_set_request_denied_fnc(xfer, irc_dccsend_recv_destroy);
-		purple_xfer_set_cancel_send_fnc(xfer, irc_dccsend_recv_destroy);
+		purple_xfer_set_cancel_recv_fnc(xfer, irc_dccsend_recv_destroy);
 		
 		purple_xfer_request(xfer);
 	}
@@ -179,7 +179,7 @@
 {
 	PurpleXfer *xfer = data;
 	struct irc_xfer_send_data *xd = xfer->data;
-	char *buffer[16];
+	char buffer[64];
 	int len;
 
 	len = read(source, buffer, sizeof(buffer));
--- a/libpurple/protocols/jabber/buddy.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Thu Mar 13 18:06:57 2008 +0000
@@ -1209,6 +1209,7 @@
 	GString *info_text;
 	char *bare_jid;
 	char *text;
+	char *serverside_alias = NULL;
 	xmlnode *vcard;
 	PurpleBuddy *b;
 	JabberBuddyInfo *jbi = data;
@@ -1247,6 +1248,10 @@
 
 			text = xmlnode_get_data(child);
 			if(text && !strcmp(child->name, "FN")) {
+				/* If we havne't found a name yet, use this one as the serverside name */
+				if (!serverside_alias)
+					serverside_alias = g_strdup(text);
+
 				jabber_string_escape_and_append(info_text,
 						_("Full Name"), text, FALSE);
 			} else if(!strcmp(child->name, "N")) {
@@ -1270,11 +1275,11 @@
 					}
 					g_free(text2);
 				}
-			} else if(text && !strcmp(child->name, "NICKNAME")) {
-				serv_got_alias(js->gc, from, text);
-				if(b) {
-					purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", text);
-				}
+			} else if(text && !strcmp(child->name, "NICKNAME")) {				
+				/* Prefer the Nickcname to the Full Name as the serverside alias */
+				g_free(serverside_alias);
+				serverside_alias = g_strdup(text);
+
 				jabber_string_escape_and_append(info_text,
 						_("Nickname"), text, FALSE);
 			} else if(text && !strcmp(child->name, "BDAY")) {
@@ -1434,6 +1439,16 @@
 		}
 	}
 
+	if (serverside_alias) {
+		/* If we found a serverside alias, set it and tell the core */
+		serv_got_alias(js->gc, from, serverside_alias);
+		if (b) {
+			purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", serverside_alias);
+		}
+		
+		g_free(serverside_alias);
+	}
+
 	jbi->vcard_text = purple_strdup_withhtml(info_text->str);
 	g_string_free(info_text, TRUE);
 	g_free(bare_jid);
--- a/libpurple/protocols/jabber/jabber.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Thu Mar 13 18:06:57 2008 +0000
@@ -406,14 +406,17 @@
 void jabber_keepalive(PurpleConnection *gc)
 {
 	JabberStream *js = gc->proto_data;
-	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
 
-	xmlnode *ping = xmlnode_new_child(iq->node, "ping");
-	xmlnode_set_namespace(ping, "urn:xmpp:ping");
-
-	js->keepalive_timeout = purple_timeout_add_seconds(20, (GSourceFunc)(jabber_pong_timeout), gc);
-	jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout));
-	jabber_iq_send(iq);
+	if (js->keepalive_timeout == -1) {
+		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
+		
+		xmlnode *ping = xmlnode_new_child(iq->node, "ping");
+		xmlnode_set_namespace(ping, "urn:xmpp:ping");
+		
+		js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc);
+		jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout));
+		jabber_iq_send(iq);
+	}
 }
 
 static void
--- a/libpurple/protocols/msn/msn.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Thu Mar 13 18:06:57 2008 +0000
@@ -1774,15 +1774,15 @@
 	purple_notify_user_info_add_section_break(user_info);
 	purple_notify_user_info_add_section_header(user_info, _("Social"));
 
-	MSN_GOT_INFO_GET_FIELD("Marital status", _("Marital Status"));
-	MSN_GOT_INFO_GET_FIELD("Interested in", _("Interests"));
-	MSN_GOT_INFO_GET_FIELD("Pets", _("Pets"));
-	MSN_GOT_INFO_GET_FIELD("Hometown", _("Hometown"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Marital status", _("Marital Status"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Interested in", _("Interests"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Pets", _("Pets"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Hometown", _("Hometown"));
 	MSN_GOT_INFO_GET_FIELD("Places lived", _("Places Lived"));
-	MSN_GOT_INFO_GET_FIELD("Fashion", _("Fashion"));
-	MSN_GOT_INFO_GET_FIELD("Humor", _("Humor"));
-	MSN_GOT_INFO_GET_FIELD("Music", _("Music"));
-	MSN_GOT_INFO_GET_FIELD("Favorite quote", _("Favorite Quote"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Fashion", _("Fashion"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Humor", _("Humor"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Music", _("Music"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Favorite quote", _("Favorite Quote"));
 
 	if (sect_info)
 	{
--- a/libpurple/protocols/msnp9/msn.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/protocols/msnp9/msn.c	Thu Mar 13 18:06:57 2008 +0000
@@ -1482,8 +1482,7 @@
 msn_info_strip_search_link(const char *field, size_t len)
 {
 	const char *c;
-	if ((c = strstr(field, " (http://spaces.live.com/default.aspx?page=searchresults")) == NULL &&
-		(c = strstr(field, " (http://spaces.msn.com/default.aspx?page=searchresults")) == NULL)
+	if ((c = strstr(field, " (http://")) == NULL)
 		return g_strndup(field, len);
 	return g_strndup(field, c - field);
 }
@@ -1630,15 +1629,15 @@
 	purple_notify_user_info_add_section_break(user_info);
 	purple_notify_user_info_add_section_header(user_info, _("Social"));
 
-	MSN_GOT_INFO_GET_FIELD("Marital status", _("Marital Status"));
-	MSN_GOT_INFO_GET_FIELD("Interested in", _("Interests"));
-	MSN_GOT_INFO_GET_FIELD("Pets", _("Pets"));
-	MSN_GOT_INFO_GET_FIELD("Hometown", _("Hometown"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Marital status", _("Marital Status"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Interested in", _("Interests"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Pets", _("Pets"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Hometown", _("Hometown"));
 	MSN_GOT_INFO_GET_FIELD("Places lived", _("Places Lived"));
-	MSN_GOT_INFO_GET_FIELD("Fashion", _("Fashion"));
-	MSN_GOT_INFO_GET_FIELD("Humor", _("Humor"));
-	MSN_GOT_INFO_GET_FIELD("Music", _("Music"));
-	MSN_GOT_INFO_GET_FIELD("Favorite quote", _("Favorite Quote"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Fashion", _("Fashion"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Humor", _("Humor"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Music", _("Music"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Favorite quote", _("Favorite Quote"));
 
 	if (sect_info)
 	{
--- a/libpurple/protocols/oscar/oscar.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Mar 13 18:06:57 2008 +0000
@@ -6230,6 +6230,7 @@
 	if (!aim_sncmp(purple_account_get_username(purple_connection_get_account(gc)), nick)) {
 		if (!flap_connection_getbytype(od, SNAC_FAMILY_ADMIN)) {
 			od->setnick = TRUE;
+			g_free(od->newsn);
 			od->newsn = g_strdup(nick);
 			aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
 		} else {
@@ -6520,7 +6521,7 @@
 
 	if (od->icq)
 	{
-		act = purple_plugin_action_new(_("Set User Info (URL)..."),
+		act = purple_plugin_action_new(_("Set User Info (web)..."),
 				oscar_show_set_info_icqurl);
 		menu = g_list_prepend(menu, act);
 	}
@@ -6531,11 +6532,11 @@
 
 	if (od->authinfo->chpassurl != NULL)
 	{
-		act = purple_plugin_action_new(_("Change Password (URL)"),
+		act = purple_plugin_action_new(_("Change Password (web)"),
 				oscar_show_chpassurl);
 		menu = g_list_prepend(menu, act);
 
-		act = purple_plugin_action_new(_("Configure IM Forwarding (URL)"),
+		act = purple_plugin_action_new(_("Configure IM Forwarding (web)"),
 				oscar_show_imforwardingurl);
 		menu = g_list_prepend(menu, act);
 	}
--- a/libpurple/protocols/oscar/util.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/protocols/oscar/util.c	Thu Mar 13 18:06:57 2008 +0000
@@ -156,7 +156,7 @@
 		return FALSE;
 
 	for (i = 0; sn[i] != '\0'; i++) {
-		if (!isalnum(sn[i]) && (sn[i] != ' '))
+		if (!isalnum(sn[i]) && (sn[i] != ' ') && (sn[i] != '.'))
 			return FALSE;
 	}
 
--- a/libpurple/protocols/silc/ops.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/protocols/silc/ops.c	Thu Mar 13 18:06:57 2008 +0000
@@ -48,17 +48,32 @@
 void silc_say(SilcClient client, SilcClientConnection conn,
 	      SilcClientMessageType type, char *msg, ...)
 {
-	if (type == SILC_CLIENT_MESSAGE_ERROR) {
-		char tmp[256];
-		va_list va;
+	char tmp[256];
+	va_list va;
+	PurpleConnection *gc = NULL;
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 
-		va_start(va, msg);
-		silc_vsnprintf(tmp, sizeof(tmp), msg, va);
-		purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp);
+	va_start(va, msg);
+	silc_vsnprintf(tmp, sizeof(tmp), msg, va);
+	va_end(va);
 
-		va_end(va);
+	if (type != SILC_CLIENT_MESSAGE_ERROR) {
+		purple_debug_misc("silc", "silc_say (%d) %s\n", type, tmp);
 		return;
 	}
+
+	purple_debug_error("silc", "silc_say error: %s\n", tmp);
+
+	if (!strcmp(tmp, "Authentication failed"))
+		reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+
+	if (client != NULL)
+		gc = client->application;
+
+	if (gc != NULL)
+		purple_connection_error_reason (gc, reason, tmp);
+	else
+		purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp);
 }
 
 /* Processes incoming MIME message.  Can be private message or channel
--- a/libpurple/purple-client-example.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/purple-client-example.c	Thu Mar 13 18:06:57 2008 +0000
@@ -1,4 +1,6 @@
+#ifndef DBUS_API_SUBJECT_TO_CHANGE
 #define DBUS_API_SUBJECT_TO_CHANGE
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
--- a/libpurple/purple-client.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/purple-client.c	Thu Mar 13 18:06:57 2008 +0000
@@ -1,4 +1,6 @@
+#ifndef DBUS_API_SUBJECT_TO_CHANGE
 #define DBUS_API_SUBJECT_TO_CHANGE
+#endif
 
 #include <dbus/dbus-glib.h>
 #include <stdio.h>
--- a/libpurple/request.h	Wed Mar 12 20:42:59 2008 +0000
+++ b/libpurple/request.h	Thu Mar 13 18:06:57 2008 +0000
@@ -238,6 +238,11 @@
 } PurpleRequestUiOps;
 
 typedef void (*PurpleRequestInputCb)(void *, const char *);
+
+/** The type of callbacks passed to purple_request_action().  The first
+ *  argument is the @a user_data parameter; the second is the index in the list
+ *  of actions of the one chosen.
+ */
 typedef void (*PurpleRequestActionCb)(void *, int);
 typedef void (*PurpleRequestChoiceCb)(void *, int);
 typedef void (*PurpleRequestFieldsCb)(void *, PurpleRequestFields *fields);
@@ -1264,37 +1269,7 @@
 	void *user_data, ...) G_GNUC_NULL_TERMINATED;
 
 /**
- * Prompts the user for multiple-choice input.
- *
- * @param handle        The plugin or connection handle.  For some things this
- *                      is <em>extremely</em> important.  See the comments on
- *                      purple_request_input().
- * @param title         The title of the message, or @c NULL if it should have
- *                      no title.
- * @param primary       The main point of the message, or @c NULL if you're
- *                      feeling enigmatic.
- * @param secondary     Secondary information, or @c NULL if there is none.
- * @param default_value The default choice; this should be one of the values
- *                      listed in the varargs.
- * @param ok_text       The text for the @c OK button, which may not be @c NULL.
- * @param ok_cb         The callback for the @c OK button, which may not be @c
- *                      NULL.
- * @param cancel_text   The text for the @c Cancel button, which may not be @c
- *                      NULL.
- * @param cancel_cb     The callback for the @c Cancel button, or @c NULL to do
- *                      nothing.
- * @param account       The #PurpleAccount associated with this request, or @c
- *                      NULL if none is
- * @param who           The username of the buddy associated with this request,
- *                      or @c NULL if none is
- * @param conv          The #PurpleConversation associated with this request, or
- *                      @c NULL if none is
- * @param user_data     The data to pass to the callback.
- * @param choices       The choices, which should be pairs of <tt>char *</tt>
- *                      descriptions and <tt>int</tt> values, terminated with a
- *                      @c NULL parameter.
- *
- * @return A UI-specific handle.
+ * <tt>va_list</tt> version of purple_request_choice(); see its documentation.
  */
 void *purple_request_choice_varg(void *handle, const char *title,
 	const char *primary, const char *secondary, int default_value,
@@ -1330,10 +1305,10 @@
  * @param action_count   The number of actions.
  * @param ...            A list of actions.  These are pairs of
  *                       arguments.  The first of each pair is the
- *                       <tt>char *</tt> that appears on the button.  It should
- *                       have an underscore before the letter you want
- *                       to use as the accelerator key for the button.
- *                       The second of each pair is the <tt>GCallback</tt>
+ *                       <tt>char *</tt> label that appears on the button.  It
+ *                       should have an underscore before the letter you want
+ *                       to use as the accelerator key for the button.  The
+ *                       second of each pair is the #PurpleRequestActionCb
  *                       function to use when the button is clicked.
  *
  * @return A UI-specific handle.
@@ -1344,39 +1319,7 @@
 	size_t action_count, ...);
 
 /**
- * Prompts the user for an action.
- *
- * This is often represented as a dialog with a button for each action.
- *
- * @param handle         The plugin or connection handle.  For some things this
- *                       is <em>extremely</em> important.  See the comments on
- *                       purple_request_input().
- * @param title          The title of the message, or @c NULL if it should have
- *                       no title.
- * @param primary        The main point of the message, or @c NULL if you're
- *                       feeling enigmatic.
- * @param secondary      Secondary information, or @c NULL if there is none.
- * @param default_action The default action, zero-indexed; if the third action
- *                       supplied should be the default, supply <tt>2</tt>.
- *                       The should be the action that users are most likely
- *                       to select.
- * @param account        The #PurpleAccount associated with this request, or @c
- *                       NULL if none is.
- * @param who            The username of the buddy associated with this request,
- *                       or @c NULL if none is.
- * @param conv           The #PurpleConversation associated with this request, or
- *                       @c NULL if none is.
- * @param user_data      The data to pass to the callback.
- * @param action_count   The number of actions.
- * @param actions        A list of actions.  These are pairs of
- *                       arguments.  The first of each pair is the
- *                       <tt>char *</tt> that appears on the button.  It should
- *                       have an underscore before the letter you want
- *                       to use as the accelerator key for the button.
- *                       The second of each pair is the <tt>GCallback</tt>
- *                       function to use when the button is clicked.
- *
- * @return A UI-specific handle.
+ * <tt>va_list</tt> version of purple_request_action(); see its documentation.
  */
 void *purple_request_action_varg(void *handle, const char *title,
 	const char *primary, const char *secondary, int default_action,
--- a/pidgin/gtkblist.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/gtkblist.c	Thu Mar 13 18:06:57 2008 +0000
@@ -705,6 +705,17 @@
 		for (bnode = node->child; bnode != NULL; bnode = bnode->next) {
 			purple_blist_node_set_bool(bnode, "show_offline", setting);
 		}
+	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+		PurpleBlistNode *cnode, *bnode;
+		gboolean setting = !purple_blist_node_get_bool(node, "show_offline");
+
+		purple_blist_node_set_bool(node, "show_offline", setting);
+		for (cnode = node->child; cnode != NULL; cnode = cnode->next) {
+			purple_blist_node_set_bool(cnode, "show_offline", setting);
+			for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) {
+				purple_blist_node_set_bool(bnode, "show_offline", setting);
+			}
+		}
 	}
 	pidgin_blist_update(purple_get_blist(), node);
 }
@@ -1459,6 +1470,11 @@
 				 G_CALLBACK(pidgin_blist_remove_cb), node, 0, 0, NULL);
 	pidgin_new_item_from_stock(menu, _("_Rename"), NULL,
 				 G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL);
+	if (!(purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)) {
+		gboolean show_offline = purple_blist_node_get_bool(node, "show_offline");
+		pidgin_new_item_from_stock(menu, show_offline ? _("Hide when offline") : _("Show when offline"),
+				NULL, G_CALLBACK(gtk_blist_menu_showoffline_cb), node, 0, 0, NULL);
+	}
 
 	pidgin_append_blist_node_extended_menu(menu, node);
 
@@ -3116,6 +3132,7 @@
 		GList *cur;
 		struct proto_chat_entry *pce;
 		char *name, *value;
+		PurpleConversation *conv;
 		PidginBlistNode *bnode = node->ui_data;
 
 		chat = (PurpleChat *)node;
@@ -3129,11 +3146,24 @@
 			g_free(tmp);
 		}
 
-		if (bnode && bnode->conv.conv &&
-				prpl_info && (prpl_info->options & OPT_PROTO_CHAT_TOPIC) &&
-				!purple_conv_chat_has_left(PURPLE_CONV_CHAT(bnode->conv.conv))) {
-			const char *topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(bnode->conv.conv));
+		if (bnode && bnode->conv.conv) {
+			conv = bnode->conv.conv;
+		} else {
+			char *chat_name;
+			if (prpl_info && prpl_info->get_chat_name)
+				chat_name = prpl_info->get_chat_name(chat->components);
+			else
+				chat_name = g_strdup(purple_chat_get_name(chat));
+
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, chat_name,
+					chat->account);
+			g_free(chat_name);
+		}
+		if (conv && prpl_info && (prpl_info->options & OPT_PROTO_CHAT_TOPIC) &&
+				!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) {
+			char *topic = g_markup_escape_text(purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv)), -1);
 			g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)"));
+			g_free(topic);
 		}
 
 		if (prpl_info->chat_info != NULL)
--- a/pidgin/gtkconv.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/gtkconv.c	Thu Mar 13 18:06:57 2008 +0000
@@ -4577,14 +4577,12 @@
 	conv = gtkconv->active_conv;
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		node = (PurpleBlistNode*)(purple_blist_find_chat(conv->account, conv->name));
-#if 0
-		/* Using the transient blist nodes to show the tooltip doesn't quite work yet. */
 		if (!node)
 			node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat");
-#endif
 	} else {
 		node = (PurpleBlistNode*)(purple_find_buddy(conv->account, conv->name));
 #if 0
+		/* Using the transient blist nodes to show the tooltip doesn't quite work yet. */
 		if (!node)
 			node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy");
 #endif
@@ -6494,14 +6492,16 @@
 			}
 		} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 			const char *topic = gtkconv->u.chat->topic_text ? gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text)) : NULL;
-			char *esc = NULL;
+			char *esc = NULL, *tmp;
 #if GTK_CHECK_VERSION(2,6,0)
 			esc = topic ? g_markup_escape_text(topic, -1) : NULL;
 #else
 			/* GTK < 2.6 doesn't have auto ellipsization, so we do a crude
 			 * trucation to prevent forcing the window to be as wide as the topic */
 			int len = 0;
-			char *c, *tmp = g_strdup(topic);
+			char *c;
+
+			tmp = g_strdup(topic);
 			c = tmp;
 			while(*c && len < 72) {
 				c = g_utf8_next_char(c);
@@ -6516,11 +6516,12 @@
 			esc = tmp ? g_markup_escape_text(tmp, -1) : NULL;
 			g_free(tmp);
 #endif
+			tmp = g_markup_escape_text(purple_conversation_get_title(conv), -1);
 			markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>",
-						purple_conversation_get_title(conv),
-						esc  && *esc ? "\n" : "",
+						tmp, esc  && *esc ? "\n" : "",
 						pidgin_get_dim_grey_string(gtkconv->infopane),
 						esc ? esc : "");
+			g_free(tmp);
 			g_free(esc);
 		}
 		gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter),
--- a/pidgin/gtkft.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/gtkft.c	Thu Mar 13 18:06:57 2008 +0000
@@ -1064,6 +1064,9 @@
 					   COLUMN_REMAINING, remaining_str,
 					   -1);
 
+	g_free(size_str);
+	g_free(remaining_str);
+
 	if (purple_xfer_is_completed(xfer))
 	{
 		GdkPixbuf *pixbuf;
--- a/pidgin/gtkimhtml.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/gtkimhtml.c	Thu Mar 13 18:06:57 2008 +0000
@@ -440,6 +440,10 @@
 	gtk_widget_set_app_paintable (imhtml->tip_window, TRUE);
 	gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
 	gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
+#if GTK_CHECK_VERSION(2,10,0)
+	gtk_window_set_type_hint (GTK_WINDOW (imhtml->tip_window),
+		GDK_WINDOW_TYPE_HINT_TOOLTIP);
+#endif
 	g_signal_connect_swapped (G_OBJECT (imhtml->tip_window), "expose_event",
 							  G_CALLBACK (gtk_imhtml_tip_paint), imhtml);
 
@@ -848,15 +852,15 @@
 	be = swap ? be : !be;
 
 	if (be)
-		return "UCS-2BE";
+		return "UTF-16BE";
 	else
-		return "UCS-2LE";
+		return "UTF-16LE";
 
 }
 
-/* Convert from UCS-2 to UTF-8, stripping the BOM if one is present.*/
+/* Convert from UTF-16LE to UTF-8, stripping the BOM if one is present.*/
 static gchar *
-ucs2_to_utf8_with_bom_check(gchar *data, guint len) {
+utf16_to_utf8_with_bom_check(gchar *data, guint len) {
 	char *fromcode = NULL;
 	GError *error = NULL;
 	guint16 c;
@@ -879,7 +883,7 @@
 		len -= 2;
 		break;
 	default:
-		fromcode = "UCS-2";
+		fromcode = "UTF-16";
 		break;
 	}
 
@@ -923,7 +927,7 @@
 		str = g_string_append_unichar(str, 0xfeff);
 		str = g_string_append(str, text);
 		str = g_string_append_unichar(str, 0x0000);
-		selection = g_convert(str->str, str->len, "UCS-2", "UTF-8", NULL, &len, NULL);
+		selection = g_convert(str->str, str->len, "UTF-16", "UTF-8", NULL, &len, NULL);
 		gtk_selection_data_set(selection_data, gdk_atom_intern("text/html", FALSE), 16, (const guchar *)selection, len);
 		g_string_free(str, TRUE);
 #else
@@ -1078,12 +1082,12 @@
 
 	if (selection_data->length >= 2 &&
 		(*(guint16 *)text == 0xfeff || *(guint16 *)text == 0xfffe)) {
-		/* This is UCS-2 */
-		char *utf8 = ucs2_to_utf8_with_bom_check(text, selection_data->length);
+		/* This is UTF-16 */
+		char *utf8 = utf16_to_utf8_with_bom_check(text, selection_data->length);
 		g_free(text);
 		text = utf8;
 		if (!text) {
-			purple_debug_warning("gtkimhtml", "g_convert from UCS-2 failed in paste_received_cb\n");
+			purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in paste_received_cb\n");
 			return;
 		}
 	}
@@ -1780,10 +1784,10 @@
 			 * http://mail.gnome.org/archives/gtk-devel-list/2001-September/msg00114.html
 			 */
 			if (sd->length >= 2 && !g_utf8_validate(text, sd->length - 1, NULL)) {
-				utf8 = ucs2_to_utf8_with_bom_check(text, sd->length);
+				utf8 = utf16_to_utf8_with_bom_check(text, sd->length);
 
 				if (!utf8) {
-					purple_debug_warning("gtkimhtml", "g_convert from UCS-2 failed in drag_rcv_cb\n");
+					purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in drag_rcv_cb\n");
 					return;
 				}
 			} else if (!(*text) || !g_utf8_validate(text, -1, NULL)) {
--- a/pidgin/gtkplugin.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/gtkplugin.c	Thu Mar 13 18:06:57 2008 +0000
@@ -81,9 +81,7 @@
 
 		config = pidgin_plugin_pref_create_frame(frame);
 
-		/* XXX According to bug #1407047 this broke saving pluging preferences, I'll look at fixing it correctly later.
-		purple_plugin_pref_frame_destroy(frame);
-		*/
+		plugin->info->prefs_info->frame = frame;
 	}
 
 	return config;
@@ -212,6 +210,12 @@
 			plugin_pref_dialogs = NULL;
 		}
 		gtk_widget_destroy(d);
+
+		if (plug->info->prefs_info && plug->info->prefs_info->frame) {
+			purple_plugin_pref_frame_destroy(plug->info->prefs_info->frame);
+			plug->info->prefs_info->frame = NULL;
+		}
+
 		break;
 	}
 }
--- a/pidgin/plugins/cap/cap.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/plugins/cap/cap.c	Thu Mar 13 18:06:57 2008 +0000
@@ -113,11 +113,8 @@
 
 	stats = g_hash_table_lookup(_buddy_stats, buddy->name);
 	if(!stats) {
-		stats = g_malloc(sizeof(CapStatistics));
+		stats = g_malloc0(sizeof(CapStatistics));
 		stats->last_message = -1;
-		stats->last_message_status_id = NULL;
-		stats->last_status_id = NULL;
-		stats->prediction = NULL;
 		stats->buddy = buddy;
 		stats->last_seen = -1;
 		stats->last_status_id = "";
--- a/pidgin/plugins/gevolution/add_buddy_dialog.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/plugins/gevolution/add_buddy_dialog.c	Thu Mar 13 18:06:57 2008 +0000
@@ -313,9 +313,11 @@
 		else
 		{
 			add_ims(dialog, contact, name, aims,    "prpl-oscar");
+			add_ims(dialog, contact, name, aims,    "prpl-aim");
 			add_ims(dialog, contact, name, jabbers, "prpl-jabber");
 			add_ims(dialog, contact, name, yahoos,  "prpl-yahoo");
 			add_ims(dialog, contact, name, msns,    "prpl-msn");
+			add_ims(dialog, contact, name, icqs,    "prpl-icq");
 			add_ims(dialog, contact, name, icqs,    "prpl-oscar");
 			add_ims(dialog, contact, name, novells, "prpl-novell");
 		}
@@ -394,10 +396,12 @@
 		}
 		else
 		{
+			add_ims(dialog, contact, name, aims,    "prpl-aim");
 			add_ims(dialog, contact, name, aims,    "prpl-oscar");
 			add_ims(dialog, contact, name, jabbers, "prpl-jabber");
 			add_ims(dialog, contact, name, yahoos,  "prpl-yahoo");
 			add_ims(dialog, contact, name, msns,    "prpl-msn");
+			add_ims(dialog, contact, name, icqs,    "prpl-icq");
 			add_ims(dialog, contact, name, icqs,    "prpl-oscar");
 			add_ims(dialog, contact, name, novells, "prpl-novell");
 		}
--- a/pidgin/plugins/gevolution/gevo-util.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/plugins/gevolution/gevo-util.c	Thu Mar 13 18:06:57 2008 +0000
@@ -109,6 +109,10 @@
 		else
 			protocol_field = E_CONTACT_IM_ICQ;
 	}
+	else if (!strcmp(protocol_id, "prpl-aim"))
+		protocol_field = E_CONTACT_IM_AIM;
+	else if (!strcmp(protocol_id, "prpl-icq"))
+		protocol_field = E_CONTACT_IM_ICQ;
 	else if (!strcmp(protocol_id, "prpl-msn"))
 		protocol_field = E_CONTACT_IM_MSN;
 	else if (!strcmp(protocol_id, "prpl-yahoo"))
--- a/pidgin/plugins/gevolution/gevolution.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/plugins/gevolution/gevolution.c	Thu Mar 13 18:06:57 2008 +0000
@@ -109,10 +109,12 @@
 
 	name = e_contact_get_const(contact, E_CONTACT_FULL_NAME);
 
+	update_ims_from_contact(contact, name, "prpl-aim",    E_CONTACT_IM_AIM);
 	update_ims_from_contact(contact, name, "prpl-oscar",  E_CONTACT_IM_AIM);
 	update_ims_from_contact(contact, name, "prpl-jabber", E_CONTACT_IM_JABBER);
 	update_ims_from_contact(contact, name, "prpl-yahoo",  E_CONTACT_IM_YAHOO);
 	update_ims_from_contact(contact, name, "prpl-msn",    E_CONTACT_IM_MSN);
+	update_ims_from_contact(contact, name, "prpl-icq",    E_CONTACT_IM_ICQ);
 	update_ims_from_contact(contact, name, "prpl-oscar",  E_CONTACT_IM_ICQ);
 	update_ims_from_contact(contact, name, "prpl-novell", E_CONTACT_IM_GROUPWISE);
 }
--- a/pidgin/plugins/gevolution/new_person_dialog.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/plugins/gevolution/new_person_dialog.c	Thu Mar 13 18:06:57 2008 +0000
@@ -148,6 +148,10 @@
 			else
 				field = E_CONTACT_IM_AIM;
 		}
+		else if (!strcmp(im_service, "prpl-aim"))
+			field = E_CONTACT_IM_AIM;
+		else if (!strcmp(im_service, "prpl-icq"))
+			field = E_CONTACT_IM_ICQ;
 		else if (!strcmp(im_service, "prpl-yahoo"))
 			field = E_CONTACT_IM_YAHOO;
 		else if (!strcmp(im_service, "prpl-jabber"))
--- a/pidgin/plugins/spellchk.c	Wed Mar 12 20:42:59 2008 +0000
+++ b/pidgin/plugins/spellchk.c	Thu Mar 13 18:06:57 2008 +0000
@@ -64,7 +64,7 @@
 	GtkTextMark *mark_insert_start;
 	GtkTextMark *mark_insert_end;
 
-	const gchar *word;
+	gchar *word;
 	gboolean inserting;
 	gboolean ignore_correction;
 	gboolean ignore_correction_on_send;
@@ -265,6 +265,7 @@
 				g_value_unset(&val1);
 				g_value_unset(&val2);
 
+				g_free(lowerword);
 				g_free(foldedword);
 				return outword;
 			}
@@ -274,6 +275,7 @@
 
 		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
 	}
+	g_free(lowerword);
 	g_free(foldedword);
 
 	return NULL;
@@ -292,6 +294,7 @@
 			G_SIGNAL_MATCH_DATA,
 			0, 0, NULL, NULL,
 			spell);
+	g_free(spell->word);
 	g_free(spell);
 }
 
@@ -440,6 +443,7 @@
 	/* Move backwards to the beginning of the word. */
 	spellchk_backward_word_start(&start);
 
+	g_free(spell->word);
 	spell->word = gtk_text_iter_get_text(&start, &end);
 
 	/* Hack because otherwise typing things like U.S. gets difficult
@@ -484,6 +488,7 @@
 	}
 	g_free(tmp);
 
+	g_free(spell->word);
 	spell->word = NULL;
 
 	return replaced;
@@ -504,6 +509,7 @@
 
 	spell->inserting = TRUE;
 
+	g_free(spell->word);
 	spell->word = NULL;
 
 	gtk_text_buffer_move_mark(buffer, spell->mark_insert_start, iter);
@@ -561,6 +567,7 @@
 	place = gtk_text_iter_get_offset(&pos);
 
 	if ((place + 1) != spell->pos) {
+		g_free(spell->word);
 		spell->word = NULL;
 		return;
 	}
@@ -574,6 +581,7 @@
 	spell->ignore_correction_on_send = TRUE;
 
 	spell->inserting = FALSE;
+	g_free(spell->word);
 	spell->word = NULL;
 }