changeset 29828:f14cbb6a28a7

propagate from branch 'im.pidgin.pidgin' (head d2cd6030fc66f726f61a1d7576facd0ef6ea5fe5) to branch 'im.pidgin.cpw.malu.ft_thumbnails' (head 71882b1055f5d7bf391080b3061785a661c5c8e9)
author Marcus Lundblad <ml@update.uu.se>
date Mon, 22 Mar 2010 21:45:23 +0000
parents 93f6a5d48a46 (current diff) 9f59abd49def (diff)
children 90dbc46a771f
files libpurple/protocols/jabber/data.c libpurple/protocols/jabber/message.c libpurple/protocols/jabber/si.c
diffstat 18 files changed, 245 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Mar 21 20:57:29 2010 +0000
+++ b/ChangeLog	Mon Mar 22 21:45:23 2010 +0000
@@ -4,6 +4,8 @@
 	General:
 	* Changed GTK+ minimum version requirement to 2.10.0.
 	* Changed GLib minimum version requirement to 2.12.0.
+	* Using the --disable-nls argument to configre now works properly. You
+	  will no longer be forced to have intltool to configure and build.
 
 	Pidgin:
 	* Moved the "Debugging Information" section of the About box to a
@@ -23,6 +25,9 @@
 	* Added a menu set mood globally for all mood-supporting accounts
 	  (currently XMPP and ICQ).
 	* Use standard (but small) GTK+ buttons instead of custom "X" symbol.
+	* Default binding of Ctrl+Shift+v to 'Paste as Plain Text' in
+	  conversation windows. This can be changed in .gtkrc-2.0. For example,
+	  Ctrl+v can be bound to 'Paste as Plain Text' by default.
 
 	Bonjour:
 	* Added support for IPv6. (Thanks to T_X for testing)
@@ -50,6 +55,7 @@
 	  rjoly for testing)
 	* When sending data using in-band-bytestreams, interpret the block-size
 	  attribute as the size of the BASE64-encoded representation of the data.
+	* Validate the hash on incoming BoB data objects (for custom smileys etc.).
 
 	Yahoo:
 	* Attempt to better handle transparent proxies interfering with HTTP-based
--- a/ChangeLog.API	Sun Mar 21 20:57:29 2010 +0000
+++ b/ChangeLog.API	Mon Mar 22 21:45:23 2010 +0000
@@ -31,6 +31,7 @@
 		* pidgin_dialogs_developers (should not be used by anything but Pidgin)
 		* pidgin_dialogs_translators (should not be used by anything but Pidgin)
 		* gtk_imhtmltoolbar_switch_active_conversation
+		* 'paste' signal for GtkIMHtml (more in gtkimhtml-signals.dox)
 
 version 2.6.6 (02/18/2010):
 	libpurple:
--- a/Makefile.am	Sun Mar 21 20:57:29 2010 +0000
+++ b/Makefile.am	Mon Mar 22 21:45:23 2010 +0000
@@ -71,22 +71,24 @@
 	gpg --verify pidgin-$(PACKAGE_VERSION).tar.gz.asc pidgin-$(PACKAGE_VERSION).tar.gz
 	gpg --verify pidgin-$(PACKAGE_VERSION).tar.bz2.asc pidgin-$(PACKAGE_VERSION).tar.bz2
 
+if INSTALL_I18N
+PO_DIR=po
+DESKTOP_FILE=pidgin.desktop
+
 if ENABLE_GTK
 appsdir = $(datadir)/applications
 apps_in_files = pidgin.desktop.in
 apps_DATA = $(apps_in_files:.desktop.in=.desktop)
 @INTLTOOL_DESKTOP_RULE@
 GTK_DIR=pidgin
-endif
+endif #ENABLE_GTK
+
+endif #INSTALL_I18N
 
 if ENABLE_GNT
 GNT_DIR=finch
 endif
 
-if INSTALL_I18N
-PO_DIR=po
-endif
-
 # This is phony, so that we always try to rebuild it.  If it succeeds
 # in calculating changes, it produces its target; otherwise, its
 # target does not exist.
@@ -146,5 +148,5 @@
 distuninstallcheck_listfiles = \
 	find . -type f -print | grep -v perl | grep -v Purple.3pm
 
-DISTCLEANFILES= pidgin.desktop libpurple/gconf/purple.schemas intltool-extract \
+DISTCLEANFILES= $(DESKTOP_FILE) libpurple/gconf/purple.schemas intltool-extract \
 			intltool-merge intltool-update
--- a/configure.ac	Sun Mar 21 20:57:29 2010 +0000
+++ b/configure.ac	Mon Mar 22 21:45:23 2010 +0000
@@ -112,61 +112,8 @@
 AC_PROG_LIBTOOL
 LIBTOOL="$LIBTOOL --silent"
 AC_PROG_INSTALL
-AC_PROG_INTLTOOL
 PKG_PROG_PKG_CONFIG
 AC_FUNC_ALLOCA
-GETTEXT_PACKAGE=pidgin
-AC_SUBST(GETTEXT_PACKAGE)
-
-
-# before gettexting, in case iconv matters
-case "$host_os" in
-darwin*)
-	AC_CHECK_LIB(resolv, res_query)
-
-	AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [
-		AC_CHECK_HEADER(IOKit/IOKitLib.h, [
-			AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit])
-			LIBS="$LIBS -framework IOKit -framework CoreFoundation"
-		], [])
-	], [])
-
-	AC_MSG_CHECKING([for fink])
-	if test -d /sw; then
-		AC_MSG_RESULT([found, adding /sw to search paths])
-		CPPFLAGS="$CPPFLAGS -I/sw/include"
-		LDFLAGS="$LDFLAGS -L/sw/lib"
-	else
-		AC_MSG_RESULT([not found])
-	fi
-	;;
-*)
-	;;
-esac
-
-ALL_LINGUAS="af am ar az be@latin bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr ga gl gu he hi hu hy id it ja ka km kn ko ku lo lt mk mn mr ms_MY my_MM nb ne nl nn oc or pa pl pt_BR pt ps ro ru si sk sl sq sr sr@latin sv sw ta te th tr uk ur vi xh zh_CN zh_HK zh_TW"
-AM_GLIB_GNU_GETTEXT
-
-dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
-dnl AM_GLIB_GNU_GETTEXT found it.
-
-if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x
-then
-	AC_MSG_ERROR([
-
-The msgfmt command is required to build libpurple.  If it is installed
-on your system, ensure that it is in your path.  If it is not, install
-GNU gettext to continue.
-
-If you have msgfmt installed, but for some reason this error message
-is still displayed, you have encountered what appears to be a bug in
-third-party configure macros.  Try setting the MSGFMT environment
-variable to the absolute path to your msgfmt binary and trying
-configure again, like this:
-
-MSGFMT=/path/to/msgfmt ./configure ...
-])
-fi
 
 dnl Checks for header files.
 AC_HEADER_STDC
@@ -306,6 +253,67 @@
 ])
 
 dnl #######################################################################
+dnl # Disable creation and installation of translation files
+dnl #######################################################################
+AC_ARG_ENABLE(nls, AC_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes)
+
+if test x$enable_i18n = xyes; then
+	AC_PROG_INTLTOOL
+	GETTEXT_PACKAGE=pidgin
+	AC_SUBST(GETTEXT_PACKAGE)
+
+
+	# before gettexting, in case iconv matters
+	case "$host_os" in
+	darwin*)
+		AC_CHECK_LIB(resolv, res_query)
+
+		AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [
+			AC_CHECK_HEADER(IOKit/IOKitLib.h, [
+				AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit])
+				LIBS="$LIBS -framework IOKit -framework CoreFoundation"
+			], [])
+		], [])
+
+		AC_MSG_CHECKING([for fink])
+		if test -d /sw; then
+			AC_MSG_RESULT([found, adding /sw to search paths])
+			CPPFLAGS="$CPPFLAGS -I/sw/include"
+			LDFLAGS="$LDFLAGS -L/sw/lib"
+		else
+			AC_MSG_RESULT([not found])
+		fi
+		;;
+	*)
+		;;
+	esac
+
+	ALL_LINGUAS="af am ar az be@latin bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr ga gl gu he hi hu hy id it ja ka km kn ko ku lo lt mk mn mr ms_MY my_MM nb ne nl nn oc or pa pl pt_BR pt ps ro ru si sk sl sq sr sr@latin sv sw ta te th tr uk ur vi xh zh_CN zh_HK zh_TW"
+	AM_GLIB_GNU_GETTEXT
+
+	dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
+	dnl AM_GLIB_GNU_GETTEXT found it.
+
+	if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x
+	then
+		AC_MSG_ERROR([
+
+The msgfmt command is required to build libpurple.  If it is installed
+on your system, ensure that it is in your path.  If it is not, install
+GNU gettext to continue.
+
+If you have msgfmt installed, but for some reason this error message
+is still displayed, you have encountered what appears to be a bug in
+third-party configure macros.  Try setting the MSGFMT environment
+variable to the absolute path to your msgfmt binary and trying
+configure again, like this:
+
+MSGFMT=/path/to/msgfmt ./configure ...
+	])
+	fi
+fi #enable_i18n
+
+dnl #######################################################################
 dnl # Check for GLib 2.12 (required)
 dnl #######################################################################
 PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.12.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
@@ -2443,11 +2451,6 @@
 
 AM_CONDITIONAL(INSTALL_PIXMAPS, test "x$enable_pixmaps" = "xyes")
 
-dnl #######################################################################
-dnl # Disable installation of translation files
-dnl #######################################################################
-AC_ARG_ENABLE(nls, AC_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes)
-
 AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes")
 
 dnl #######################################################################
@@ -2642,7 +2645,13 @@
 if test "x$enable_pixmaps" = "xno" ; then
 	echo
 	echo Warning: You have disabled the installation of pixmap data, but Pidgin
-	echo still requires installed pixmaps.  Be sure you know what you\'re doing.
+	echo still requires installed pixmaps.  Be sure you know what you are doing.
+fi
+if test "x$enable_i18n" = "xno" ; then
+	echo
+	echo Warning: You have disabled the building and intallation of translation
+	echo data.  This will prevent building pidgin.desktop and the GConf schemas.
+	echo Be sure you know what you are doing.
 fi
 echo
 echo configure complete, now type \'make\'
--- a/doc/gtkimhtml-signals.dox	Sun Mar 21 20:57:29 2010 +0000
+++ b/doc/gtkimhtml-signals.dox	Mon Mar 22 21:45:23 2010 +0000
@@ -6,6 +6,7 @@
   @signal format_function_clear
   @signal format_function_toggle
   @signal format_function_update
+  @paste
  @endsignals
 
  @see gtkimhtml.h
@@ -57,6 +58,17 @@
   @signaldesc Emitted when the cursor has moved and formatting has changed
   @param imhtml The GtkIMHtml emitting the signal.
   @param data   User defined data.
+
+ @signaldef paste
+ 	@signalproto
+void (*paste) (GtkIMHtml *imhtml, char *format)
+	@endsignalproto
+	@signaldef Emitted when paste from the clipboard is requested.
+	@param imhtml  The GtkIMHtml emitting the signal.
+	@param format  If 'text', then the formatting of the clipboard content
+	               will be removed before pasting. If empty or 'html', then
+		       the formatting will not be removed. Any other value for
+		       this parameter is ignored and nothing is pasted.
  @endsignaldef
 */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/gtkrc-2.0	Sun Mar 21 20:57:29 2010 +0000
+++ b/doc/gtkrc-2.0	Mon Mar 22 21:45:23 2010 +0000
@@ -81,6 +81,12 @@
 	bind "<alt>F2" { "format_toggle" (2) }
 # Ctrl-alt-shift-f3 toggles underline
 	bind "<ctrl><alt><shift>F3" { "format_toggle" (4) }
+
+# Ctrl-v to paste as plain text
+	bind "<ctrl>v" { "paste" ("text") }
+
+# Ctrl-Shift-v for normal 'Paste'
+	bind "<ctrl><shift>v" { "paste" ("html") }
 }
   
 widget "*pidgin_conv_entry" binding "my-bindings"
--- a/libpurple/gconf/Makefile.am	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/gconf/Makefile.am	Mon Mar 22 21:45:23 2010 +0000
@@ -2,14 +2,17 @@
 
 EXTRA_DIST = purple.schemas.in
 
+if GCONF_SCHEMAS_INSTALL
+
+if INSTALL_I18N
 schema_in_files = purple.schemas.in
 schema_DATA = $(schema_in_files:.schemas.in=.schemas)
 @INTLTOOL_SCHEMAS_RULE@
+endif #INSTALL_I18N
 
-if GCONF_SCHEMAS_INSTALL
 install-data-local:
 	GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(schema_DATA) 2>&1 | \
 		grep -v "^WARNING: failed to install schema" | grep -v "^Attached schema" 1>&2
 else
 install-data-local:
-endif
+endif #GCONF_SCHEMAS_INSTALL
--- a/libpurple/protocols/jabber/auth.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/auth.c	Mon Mar 22 21:45:23 2010 +0000
@@ -287,7 +287,7 @@
 
 			x = xmlnode_new_child(query, "digest");
 			s = g_strdup_printf("%s%s", js->stream_id, pw);
-			hash = jabber_calculate_data_sha1sum(s, strlen(s));
+			hash = jabber_calculate_data_hash(s, strlen(s), "sha1");
 			xmlnode_insert_data(x, hash, -1);
 			g_free(hash);
 			g_free(s);
--- a/libpurple/protocols/jabber/buddy.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Mon Mar 22 21:45:23 2010 +0000
@@ -516,7 +516,8 @@
 		binval = xmlnode_new_child(photo, "BINVAL");
 		enc = purple_base64_encode(avatar_data, avatar_len);
 
-		js->avatar_hash = jabber_calculate_data_sha1sum(avatar_data, avatar_len);
+		js->avatar_hash =
+			jabber_calculate_data_hash(avatar_data, avatar_len, "sha1");
 
 		xmlnode_insert_data(binval, enc, -1);
 		g_free(enc);
@@ -936,7 +937,7 @@
 			g_free(bintext);
 
 			if (data) {
-				vcard_hash = jabber_calculate_data_sha1sum(data, size);
+				vcard_hash = jabber_calculate_data_hash(data, size, "sha1");
 				g_free(data);
 			}
 		}
@@ -1185,7 +1186,7 @@
 
 						purple_notify_user_info_add_pair(user_info, (photo ? _("Photo") : _("Logo")), img_text);
 
-						hash = jabber_calculate_data_sha1sum(data, size);
+						hash = jabber_calculate_data_hash(data, size, "sha1");
 						purple_buddy_icons_set_for_user(account, bare_jid, data, size, hash);
 						g_free(hash);
 						g_free(img_text);
--- a/libpurple/protocols/jabber/data.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/data.c	Mon Mar 22 21:45:23 2010 +0000
@@ -39,7 +39,7 @@
 	gboolean ephemeral, JabberStream *js)
 {
 	JabberData *data = g_new0(JabberData, 1);
-	gchar *checksum = jabber_calculate_data_sha1sum(rawdata, size);
+	gchar *checksum = jabber_calculate_data_hash(rawdata, size, "sha1");
 	gchar cid[256];
 
 	g_snprintf(cid, sizeof(cid), "sha1+%s@bob.xmpp.org", checksum);
@@ -55,6 +55,69 @@
 	return data;
 }
 
+
+static gboolean
+jabber_data_has_valid_hash(const JabberData *data)
+{
+	const gchar *cid = jabber_data_get_cid(data);
+	gchar **cid_parts = g_strsplit(cid, "@", -1);
+	gchar **iter;
+	int num_parts = 0;
+
+	purple_debug_info("jabber", "validating BoB hash %s\n", cid);
+	
+	for (iter = cid_parts; *iter != NULL ; iter++) {
+		num_parts++;
+	}
+
+	if (num_parts == 2 && purple_strequal(cid_parts[1], "bob.xmpp.org")) {
+		gchar **sub_parts = g_strsplit(cid_parts[0], "+", -1);
+
+		num_parts = 0;
+		for (iter = sub_parts ; *iter != NULL ; iter++) {
+			num_parts++;
+		}
+
+		if (num_parts == 2) {
+			const gchar *hash_algo = sub_parts[0];
+			const gchar *hash_value = sub_parts[1];
+			gchar *digest =
+				jabber_calculate_data_hash(jabber_data_get_data(data),
+				    jabber_data_get_size(data), hash_algo);
+
+			purple_debug_info("jabber", "BoB expecting hash: %s\n", digest);
+			
+			if (digest) {
+				gboolean result = purple_strequal(digest, hash_value);
+
+				if (!result) {
+					purple_debug_error("jabber", "invalid BoB hash\n");
+				}
+				g_free(digest);
+				return result;
+			} else {
+				purple_debug_info("jabber", "unknown BoB hash algo\n");
+				return FALSE;
+			}
+		} else {
+			return TRUE;
+		}
+	} else {
+		return TRUE;
+	}
+}
+
+static void
+jabber_data_delete(gpointer cbdata)
+{
+	JabberData *data = cbdata;
+	g_free(data->cid);
+	g_free(data->type);
+	g_free(data->data);
+	g_free(data);
+}
+
+
 JabberData *
 jabber_data_create_from_xml(xmlnode *tag)
 {
@@ -97,6 +160,11 @@
 	data->cid = g_strdup(cid);
 	data->type = g_strdup(type);
 
+	if (!jabber_data_has_valid_hash(data)) {
+		jabber_data_delete(data);
+		return NULL;
+	}
+
 	return data;
 }
 
@@ -212,8 +280,12 @@
 			if (!ephemeral) {
 				jabber_data_associate_remote(data);
 			}
-			/* TODO: validate hash */
 			cb(data, alt, userdata);
+		} else {
+			/* could not validate hash when creating data from XML */
+			purple_debug_info("jabber",
+			    "hash validation failed on requested BoB object\n");
+			cb(NULL, alt, userdata);
 		}
 	} else if (item_not_found) {
 		purple_debug_info("jabber",
--- a/libpurple/protocols/jabber/jabber.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Mar 22 21:45:23 2010 +0000
@@ -980,8 +980,8 @@
 	image = purple_buddy_icons_find_account_icon(account);
 	if (image != NULL) {
 		js->initial_avatar_hash =
-			jabber_calculate_data_sha1sum(purple_imgstore_get_data(image),
-					purple_imgstore_get_size(image));
+			jabber_calculate_data_hash(purple_imgstore_get_data(image),
+					purple_imgstore_get_size(image), "sha1");
 		purple_imgstore_unref(image);
 	}
 
--- a/libpurple/protocols/jabber/jutil.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/jutil.c	Mon Mar 22 21:45:23 2010 +0000
@@ -728,17 +728,17 @@
 	return NULL;
 }
 
-/* The same as purple_util_get_image_checksum, but guaranteed to remain SHA1 */
 char *
-jabber_calculate_data_sha1sum(gconstpointer data, size_t len)
+jabber_calculate_data_hash(gconstpointer data, size_t len, 
+    const gchar *hash_algo)
 {
 	PurpleCipherContext *context;
 	static gchar digest[41];
 
-	context = purple_cipher_context_new_by_name("sha1", NULL);
+	context = purple_cipher_context_new_by_name(hash_algo, NULL);
 	if (context == NULL)
 	{
-		purple_debug_error("jabber", "Could not find sha1 cipher\n");
+		purple_debug_error("jabber", "Could not find %s cipher\n", hash_algo);
 		g_return_val_if_reached(NULL);
 	}
 
@@ -746,7 +746,8 @@
 	purple_cipher_context_append(context, data, len);
 	if (!purple_cipher_context_digest_to_str(context, sizeof(digest), digest, NULL))
 	{
-		purple_debug_error("jabber", "Failed to get SHA-1 digest.\n");
+		purple_debug_error("jabber", "Failed to get digest for %s cipher.\n",
+		    hash_algo);
 		g_return_val_if_reached(NULL);
 	}
 	purple_cipher_context_destroy(context);
--- a/libpurple/protocols/jabber/jutil.h	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/jutil.h	Mon Mar 22 21:45:23 2010 +0000
@@ -85,5 +85,6 @@
 /* show attr (presence stanza) -> state */
 JabberBuddyState jabber_buddy_show_get_state(const char *id);
 
-char *jabber_calculate_data_sha1sum(gconstpointer data, size_t len);
+char *jabber_calculate_data_hash(gconstpointer data, size_t len,
+    const gchar *hash_algo);
 #endif /* PURPLE_JABBER_JUTIL_H_ */
--- a/libpurple/protocols/jabber/message.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/message.c	Mon Mar 22 21:45:23 2010 +0000
@@ -474,7 +474,10 @@
 		if (!data && cid != NULL) {
 			/* we haven't cached this already, let's add it */
 			JabberData *new_data = jabber_data_create_from_xml(data_tag);
-			jabber_data_associate_remote(new_data);
+
+			if (new_data) {
+				jabber_data_associate_remote(new_data);
+			}
 		}
 	}
 }
--- a/libpurple/protocols/jabber/presence.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/presence.c	Mon Mar 22 21:45:23 2010 +0000
@@ -457,7 +457,7 @@
 
 			data = purple_base64_decode(text, &size);
 			if (data) {
-				gchar *hash = jabber_calculate_data_sha1sum(data, size);
+				gchar *hash = jabber_calculate_data_hash(data, size, "sha1");
 				purple_buddy_icons_set_for_user(js->gc->account, from, data,
 				                                size, hash);
 				g_free(hash);
--- a/libpurple/protocols/jabber/si.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/si.c	Mon Mar 22 21:45:23 2010 +0000
@@ -290,7 +290,7 @@
 				jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
 
 		/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
-		hash = jabber_calculate_data_sha1sum(dstaddr, strlen(dstaddr));
+		hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
 
 		jsx->connect_data = purple_proxy_connect_socks5(NULL, jsx->gpi,
 				hash, 0,
@@ -475,7 +475,7 @@
 			jsx->js->user->resource, xfer->who);
 
 	/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
-	hash = jabber_calculate_data_sha1sum(dstaddr, strlen(dstaddr));
+	hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
 
 	if(strncmp(hash, jsx->rxqueue + 5, 40) ||
 			jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
--- a/libpurple/protocols/jabber/useravatar.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/libpurple/protocols/jabber/useravatar.c	Mon Mar 22 21:45:23 2010 +0000
@@ -149,8 +149,9 @@
 			char *lengthstring, *widthstring, *heightstring;
 
 			/* compute the sha1 hash */
-			char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img),
-			                                           purple_imgstore_get_size(img));
+			char *hash = jabber_calculate_data_hash(purple_imgstore_get_data(img),
+			                                        purple_imgstore_get_size(img),
+			    									"sha1");
 			char *base64avatar = purple_base64_encode(purple_imgstore_get_data(img),
 			                                          purple_imgstore_get_size(img));
 
--- a/pidgin/gtkimhtml.c	Sun Mar 21 20:57:29 2010 +0000
+++ b/pidgin/gtkimhtml.c	Mon Mar 22 21:45:23 2010 +0000
@@ -152,6 +152,7 @@
 	MESSAGE_SEND,
 	UNDO,
 	REDO,
+	PASTE,
 	LAST_SIGNAL
 };
 static guint signals [LAST_SIGNAL] = { 0 };
@@ -1343,6 +1344,15 @@
 	return FALSE;
 }
 
+static void
+imhtml_paste_cb(GtkIMHtml *imhtml, const char *str)
+{
+	if (!str || !*str || !strcmp(str, "html"))
+		g_signal_emit_by_name(imhtml, "paste_clipboard");
+	else if (!strcmp(str, "text"))
+		paste_unformatted_cb(NULL, imhtml);
+}
+
 static void imhtml_toggle_format(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons)
 {
 	/* since this function is the handler for the formatting keystrokes,
@@ -1513,24 +1523,31 @@
 					     NULL,
 					     0, g_cclosure_marshal_VOID__VOID,
 					     G_TYPE_NONE, 0);
-        signals [UNDO] = g_signal_new ("undo",
-                        		      G_TYPE_FROM_CLASS (klass),
-		                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                		              G_STRUCT_OFFSET (GtkIMHtmlClass, undo),
-		                              NULL,
-		                              NULL,
-                		              gtksourceview_marshal_VOID__VOID,
-		                              G_TYPE_NONE,
-		                              0);
-        signals [REDO] = g_signal_new ("redo",
-                        		      G_TYPE_FROM_CLASS (klass),
-		                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-		                              G_STRUCT_OFFSET (GtkIMHtmlClass, redo),
-		                              NULL,
-		                              NULL,
-		                              gtksourceview_marshal_VOID__VOID,
-		                              G_TYPE_NONE,
-		                              0);
+	signals[PASTE] = g_signal_new("paste",
+					     G_TYPE_FROM_CLASS(gobject_class),
+					     G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+						 0,
+					     NULL,
+					     0, g_cclosure_marshal_VOID__STRING,
+					     G_TYPE_NONE, 1, G_TYPE_STRING);
+	signals [UNDO] = g_signal_new ("undo",
+			G_TYPE_FROM_CLASS (klass),
+			G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+			G_STRUCT_OFFSET (GtkIMHtmlClass, undo),
+			NULL,
+			NULL,
+			gtksourceview_marshal_VOID__VOID,
+			G_TYPE_NONE,
+			0);
+	signals [REDO] = g_signal_new ("redo",
+			G_TYPE_FROM_CLASS (klass),
+			G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+			G_STRUCT_OFFSET (GtkIMHtmlClass, redo),
+			NULL,
+			NULL,
+			gtksourceview_marshal_VOID__VOID,
+			G_TYPE_NONE,
+			0);
 
 
 
@@ -1615,10 +1632,10 @@
 	gtk_binding_entry_add_signal (binding_set, GDK_r, GDK_CONTROL_MASK, "format_function_clear", 0);
 	gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "message_send", 0);
 	gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "message_send", 0);
-        gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0);
-        gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
-        gtk_binding_entry_add_signal (binding_set, GDK_F14, 0, "undo", 0);
-
+	gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_F14, 0, "undo", 0);
+	gtk_binding_entry_add_signal(binding_set, GDK_v, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "paste", 1, G_TYPE_STRING, "text");
 }
 
 static void gtk_imhtml_init (GtkIMHtml *imhtml)
@@ -1689,6 +1706,7 @@
 	g_signal_connect(G_OBJECT(imhtml), "paste-clipboard", G_CALLBACK(paste_clipboard_cb), NULL);
 	g_signal_connect_after(G_OBJECT(imhtml), "realize", G_CALLBACK(imhtml_realized_remove_primary), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "unrealize", G_CALLBACK(imhtml_destroy_add_primary), NULL);
+	g_signal_connect(G_OBJECT(imhtml), "paste", G_CALLBACK(imhtml_paste_cb), NULL);
 
 #ifndef _WIN32
 	g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set",