changeset 24647:34b02adafd9b

merge of 'dd536145a4036177cabc4e4e1ac83dcdf1008a33' and 'fdb452e89c3d4140034ff12f06a8c3daf350ae5d'
author John Bailey <rekkanoryo@rekkanoryo.org>
date Thu, 11 Dec 2008 22:36:47 +0000
parents 36851084b5f2 (diff) 7fd6e5a72bf1 (current diff)
children bf2ca2c5ac40
files libpurple/plugins/perl/common/Account.xs libpurple/plugins/perl/common/AccountOpts.xs libpurple/plugins/perl/common/Conversation.xs libpurple/plugins/perl/common/Prefs.xs libpurple/plugins/perl/common/Roomlist.xs libpurple/plugins/perl/common/Status.xs pidgin/plugins/perl/common/GtkIMHtml.xs
diffstat 29 files changed, 163 insertions(+), 175 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Thu Dec 11 22:36:29 2008 +0000
+++ b/COPYRIGHT	Thu Dec 11 22:36:47 2008 +0000
@@ -207,6 +207,7 @@
 Benjamin Kahn
 Anders Kaseorg
 Praveen Karadakal
+Jaromír Karmazín
 John Kelm
 Jochen Kemnade
 Akuke Kok
--- a/ChangeLog	Thu Dec 11 22:36:29 2008 +0000
+++ b/ChangeLog	Thu Dec 11 22:36:47 2008 +0000
@@ -23,6 +23,11 @@
 	* On MSN, the Games and Office media can now be set and displayed (in
 	  addition to the previous Music media). The Media status text now shows
 	  the album, if possible. 
+	* Fix use of av_len in perl bindings to fix some off-by-one bugs (Paul
+	  Aurich)
+	* On ICQ, advertise the ICQ 6 typing capability.  This should fix the
+	  reports of typing notifications not working with third-party clients
+	  (Jaromír Karmazín)
 
 	Gadu-Gadu:
 	* Fix some problems with Gadu-Gadu buddy icons (Adam Strzelecki)
@@ -42,6 +47,10 @@
 	* Send "client-accepts-full-bind-result" attribute during SASL login.
 	  This will fix Google Talk login failures if the user configures the
 	  wrong domain for his/her account.
+	* Support new <metadata/> element to indicate no XEP-0084 User Avatar
+	  (Paul Aurich)
+	* Fix SHA1 avatar checksum errors that occur when one of the bytes in a
+	  checksum begins with 0 (Paul Aurich)
 	
 	Zephyr:
 	* Enable auto-reply, to emulate 'zaway' (Toby Schaffer)
--- a/libpurple/account.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/account.c	Thu Dec 11 22:36:47 2008 +0000
@@ -75,6 +75,7 @@
 	gpointer userdata;
 	PurpleAccountRequestAuthorizationCb auth_cb;
 	PurpleAccountRequestAuthorizationCb deny_cb;
+	guint ref;
 } PurpleAccountRequestInfo;
 
 static PurpleAccountUiOps *account_ui_ops = NULL;
@@ -1211,6 +1212,18 @@
 		ui_ops->request_add(account, remote_user, id, alias, message);
 }
 
+static PurpleAccountRequestInfo *
+purple_account_request_info_unref(PurpleAccountRequestInfo *info)
+{
+	if (--info->ref)
+		return info;
+
+	/* TODO: This will leak info->user_data, but there is no callback to just clean that up */
+	g_free(info->user);
+	g_free(info);
+	return NULL;
+}
+
 static void
 purple_account_request_close_info(PurpleAccountRequestInfo *info)
 {
@@ -1221,11 +1234,7 @@
 	if (ops != NULL && ops->close_account_request != NULL)
 		ops->close_account_request(info->ui_handle);
 
-	/* TODO: This will leak info->user_data, but there is no callback to just clean that up */
-
-	g_free(info->user);
-	g_free(info);
-
+	purple_account_request_info_unref(info);
 }
 
 void
@@ -1278,8 +1287,7 @@
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-granted", info->account, info->user);
 
-	g_free(info->user);
-	g_free(info);
+	purple_account_request_info_unref(info);
 }
 
 static void
@@ -1294,8 +1302,7 @@
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-denied", info->account, info->user);
 
-	g_free(info->user);
-	g_free(info);
+	purple_account_request_info_unref(info);
 }
 
 void *
@@ -1332,11 +1339,18 @@
 		info->deny_cb   = deny_cb;
 		info->userdata  = user_data;
 		info->user      = g_strdup(remote_user);
+		info->ref       = 2;  /* We hold an extra ref to make sure info remains valid
+		                         if any of the callbacks are called synchronously. We
+		                         unref it after the function call */
+
 		info->ui_handle = ui_ops->request_authorize(account, remote_user, id, alias, message,
 							    on_list, request_auth_cb, request_deny_cb, info);
 
-		handles = g_list_append(handles, info);
-		return info->ui_handle;
+		info = purple_account_request_info_unref(info);
+		if (info) {
+			handles = g_list_append(handles, info);
+			return info->ui_handle;
+		}
 	}
 
 	return NULL;
--- a/libpurple/plugins/perl/common/Account.xs	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/plugins/perl/common/Account.xs	Thu Dec 11 22:36:47 2008 +0000
@@ -107,7 +107,7 @@
     t_GL = NULL;
     t_len = av_len((AV *)SvRV(status_types));
 
-    for (i = 0; i < t_len; i++)
+    for (i = 0; i <= t_len; i++)
         t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(status_types), i, 0)));
 
     purple_account_set_status_types(account, t_GL);
@@ -209,7 +209,7 @@
     t_GL = NULL;
     t_len = av_len((AV *)SvRV(list));
 
-    for (i = 0; i < t_len; i++)
+    for (i = 0; i <= t_len; i++)
         t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(list), i, 0)));
 
     purple_account_add_buddies(account, t_GL);
@@ -238,13 +238,13 @@
     t_GL1 = NULL;
     t_len = av_len((AV *)SvRV(A));
 
-    for (i = 0; i < t_len; i++)
+    for (i = 0; i <= t_len; i++)
         t_GL1 = g_list_append(t_GL1, SvPVutf8_nolen(*av_fetch((AV *)SvRV(A), i, 0)));
 
     t_GL2 = NULL;
     t_len = av_len((AV *)SvRV(B));
 
-    for (i = 0; i < t_len; i++)
+    for (i = 0; i <= t_len; i++)
         t_GL2 = g_list_append(t_GL2, SvPVutf8_nolen(*av_fetch((AV *)SvRV(B), i, 0)));
 
     purple_account_remove_buddies(account, t_GL1, t_GL2);
--- a/libpurple/plugins/perl/common/AccountOpts.xs	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/plugins/perl/common/AccountOpts.xs	Thu Dec 11 22:36:47 2008 +0000
@@ -44,7 +44,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(values));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(values), i, 0)));
 
 	RETVAL  = purple_account_option_list_new(text, pref_name, t_GL);
@@ -132,7 +132,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(values));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(values), i, 0)));
 
 	purple_account_option_set_list(option, t_GL);
--- a/libpurple/plugins/perl/common/Conversation.xs	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/plugins/perl/common/Conversation.xs	Thu Dec 11 22:36:47 2008 +0000
@@ -336,7 +336,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(users));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(users), i, 0)));
 
 	for (l = purple_conv_chat_set_users(chat, t_GL); l != NULL; l = l->next) {
@@ -374,7 +374,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(ignored));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(ignored), i, 0)));
 
 	for (l = purple_conv_chat_set_ignored(chat, t_GL); l != NULL; l = l->next) {
@@ -431,19 +431,19 @@
 	t_GL_users = NULL;
 	t_len = av_len((AV *)SvRV(users));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL_users = g_list_append(t_GL_users, SvPVutf8_nolen(*av_fetch((AV *)SvRV(users), i, 0)));
 
 	t_GL_flags = NULL;
 	t_len = av_len((AV *)SvRV(flags));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL_flags = g_list_append(t_GL_flags, SvPVutf8_nolen(*av_fetch((AV *)SvRV(flags), i, 0)));
 
 	t_GL_extra_msgs = NULL;
 	t_len = av_len((AV *)SvRV(extra_msgs));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL_extra_msgs = g_list_append(t_GL_extra_msgs, SvPVutf8_nolen(*av_fetch((AV *)SvRV(extra_msgs), i, 0)));
 
 	purple_conv_chat_add_users(chat, t_GL_users, t_GL_extra_msgs, t_GL_flags, new_arrivals);
--- a/libpurple/plugins/perl/common/Prefs.xs	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/plugins/perl/common/Prefs.xs	Thu Dec 11 22:36:47 2008 +0000
@@ -53,7 +53,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(value));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(value), i, 0)));
 
 	purple_prefs_add_string_list(name, t_GL);
@@ -75,7 +75,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(value));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(value), i, 0)));
 
 	purple_prefs_add_path_list(name, t_GL);
@@ -204,7 +204,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(value));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(value), i, 0)));
 
 	purple_prefs_set_string_list(name, t_GL);
@@ -226,7 +226,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(value));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(value), i, 0)));
 
 	purple_prefs_set_path_list(name, t_GL);
--- a/libpurple/plugins/perl/common/Roomlist.xs	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/plugins/perl/common/Roomlist.xs	Thu Dec 11 22:36:47 2008 +0000
@@ -80,7 +80,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(fields));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(fields), i, 0)));
 
 	purple_roomlist_set_fields(list, t_GL);
--- a/libpurple/plugins/perl/common/Status.xs	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/plugins/perl/common/Status.xs	Thu Dec 11 22:36:47 2008 +0000
@@ -85,7 +85,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(source_list));
 
-	for (i = 0; i < t_len; i++) {
+	for (i = 0; i <= t_len; i++) {
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(source_list), i, 0)));
 	}
 	purple_presence_add_list(presence, t_GL);
@@ -381,7 +381,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(status_types));
 
-	for (i = 0; i < t_len; i++) {
+	for (i = 0; i <= t_len; i++) {
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(status_types), i, 0)));
 	}
 	RETVAL = (PurpleStatusType *)purple_status_type_find_with_id(t_GL, id);
--- a/libpurple/protocols/jabber/auth.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/jabber/auth.c	Thu Dec 11 22:36:47 2008 +0000
@@ -613,9 +613,7 @@
 	} else if(!strcmp(type, "result")) {
 		query = xmlnode_get_child(packet, "query");
 		if(js->stream_id && xmlnode_get_child(query, "digest")) {
-			unsigned char hashval[20];
-			char *s, h[41], *p;
-			int i;
+			char *s, *hash;
 
 			iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
 			query = xmlnode_get_child(iq->node, "query");
@@ -626,14 +624,9 @@
 
 			x = xmlnode_new_child(query, "digest");
 			s = g_strdup_printf("%s%s", js->stream_id, pw);
-
-			purple_cipher_digest_region("sha1", (guchar *)s, strlen(s),
-									  sizeof(hashval), hashval, NULL);
-
-			p = h;
-			for(i=0; i<20; i++, p+=2)
-				snprintf(p, 3, "%02x", hashval[i]);
-			xmlnode_insert_data(x, h, -1);
+			hash = jabber_calculate_data_sha1sum(s, strlen(s));
+			xmlnode_insert_data(x, hash, -1);
+			g_free(hash);
 			g_free(s);
 			jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
 			jabber_iq_send(iq);
--- a/libpurple/protocols/jabber/buddy.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Thu Dec 11 22:36:47 2008 +0000
@@ -19,7 +19,6 @@
  *
  */
 #include "internal.h"
-#include "cipher.h"
 #include "debug.h"
 #include "imgstore.h"
 #include "prpl.h"
@@ -451,9 +450,6 @@
 		gsize avatar_len;
 		xmlnode *photo, *binval, *type;
 		gchar *enc;
-		int i;
-		unsigned char hashval[20];
-		char *p, hash[41];
 
 		if(!vc_node) {
 			vc_node = xmlnode_new("vCard");
@@ -473,16 +469,7 @@
 		binval = xmlnode_new_child(photo, "BINVAL");
 		enc = purple_base64_encode(avatar_data, avatar_len);
 
-		purple_cipher_digest_region("sha1", avatar_data,
-								  avatar_len, sizeof(hashval),
-								  hashval, NULL);
-
-		purple_imgstore_unref(img);
-
-		p = hash;
-		for(i=0; i<20; i++, p+=2)
-			snprintf(p, 3, "%02x", hashval[i]);
-		js->avatar_hash = g_strdup(hash);
+		js->avatar_hash = jabber_calculate_data_sha1sum(avatar_data, avatar_len);
 
 		xmlnode_insert_data(binval, enc, -1);
 		g_free(enc);
@@ -545,19 +532,9 @@
 				char *lengthstring, *widthstring, *heightstring;
 				
 				/* compute the sha1 hash */
-				PurpleCipherContext *ctx;
-				unsigned char digest[20];
-				char *hash;
+				char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
 				char *base64avatar;
 				
-				ctx = purple_cipher_context_new_by_name("sha1", NULL);
-				purple_cipher_context_append(ctx, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
-				purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
-				purple_cipher_context_destroy(ctx);
-				
-				/* convert digest to a string */
-				hash = g_strdup_printf("%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x",digest[0],digest[1],digest[2],digest[3],digest[4],digest[5],digest[6],digest[7],digest[8],digest[9],digest[10],digest[11],digest[12],digest[13],digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]);
-				
 				publish = xmlnode_new("publish");
 				xmlnode_set_attrib(publish,"node",AVATARNAMESPACEDATA);
 				
@@ -1407,31 +1384,25 @@
 						(bintext = xmlnode_get_data(child))) {
 					gsize size;
 					guchar *data;
-					int i;
-					unsigned char hashval[20];
-					char *p, hash[41];
 					gboolean photo = (strcmp(child->name, "PHOTO") == 0);
 
 					data = purple_base64_decode(bintext, &size);
 					if (data) {
 						char *img_text;
+						char *hash;
 
 						jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_add_with_id(g_memdup(data, size), size, "logo.png")));
 						img_text = g_strdup_printf("<img id='%d'>", GPOINTER_TO_INT(jbi->vcard_imgids->data));
 
 						purple_notify_user_info_add_pair(user_info, (photo ? _("Photo") : _("Logo")), img_text);
 
-						purple_cipher_digest_region("sha1", (guchar *)data, size,
-								sizeof(hashval), hashval, NULL);
-						p = hash;
-						for(i=0; i<20; i++, p+=2)
-							snprintf(p, 3, "%02x", hashval[i]);
-
+						hash = jabber_calculate_data_sha1sum(data, size);
 						purple_buddy_icons_set_for_user(js->gc->account, bare_jid,
 								data, size, hash);
-						g_free(bintext);
+						g_free(hash);
 						g_free(img_text);
 					}
+					g_free(bintext);
 				}
 			}
 			g_free(text);
@@ -1525,9 +1496,12 @@
 		purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
 	} else {
 		xmlnode *info, *goodinfo = NULL;
-		
+		gboolean has_children = FALSE;
+
 		/* iterate over all info nodes to get one we can use */
 		for(info = metadata->child; info; info = info->next) {
+			if(info->type == XMLNODE_TYPE_TAG)
+				has_children = TRUE;
 			if(info->type == XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) {
 				const char *type = xmlnode_get_attrib(info,"type");
 				const char *id = xmlnode_get_attrib(info,"id");
@@ -1542,7 +1516,9 @@
 					goodinfo = info;
 			}
 		}
-		if(goodinfo) {
+		if(has_children == FALSE) {
+			purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
+		} else if(goodinfo) {
 			const char *url = xmlnode_get_attrib(goodinfo, "url");
 			const char *id = xmlnode_get_attrib(goodinfo,"id");
 			
--- a/libpurple/protocols/jabber/jutil.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/jabber/jutil.c	Thu Dec 11 22:36:47 2008 +0000
@@ -20,7 +20,9 @@
  */
 #include "internal.h"
 #include "account.h"
+#include "cipher.h"
 #include "conversation.h"
+#include "debug.h"
 #include "server.h"
 #include "util.h"
 #include "xmlnode.h"
@@ -236,3 +238,29 @@
 	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)
+{
+	PurpleCipherContext *context;
+	static gchar digest[41];
+
+	context = purple_cipher_context_new_by_name("sha1", NULL);
+	if (context == NULL)
+	{
+		purple_debug_error("jabber", "Could not find sha1 cipher\n");
+		g_return_val_if_reached(NULL);
+	}
+
+	/* Hash the data */
+	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");
+		g_return_val_if_reached(NULL);
+	}
+	purple_cipher_context_destroy(context);
+
+	return g_strdup(digest);
+}
+
--- a/libpurple/protocols/jabber/jutil.h	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/jabber/jutil.h	Thu Dec 11 22:36:47 2008 +0000
@@ -42,4 +42,5 @@
 
 PurpleConversation *jabber_find_unnormalized_conv(const char *name, PurpleAccount *account);
 
+char *jabber_calculate_data_sha1sum(gconstpointer data, size_t len);
 #endif /* _PURPLE_JABBER_JUTIL_H_ */
--- a/libpurple/protocols/jabber/presence.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/jabber/presence.c	Thu Dec 11 22:36:47 2008 +0000
@@ -21,7 +21,6 @@
 #include "internal.h"
 
 #include "account.h"
-#include "cipher.h"
 #include "conversation.h"
 #include "debug.h"
 #include "notify.h"
@@ -349,19 +348,12 @@
 				(( (binval = xmlnode_get_child(photo, "BINVAL")) &&
 				(text = xmlnode_get_data(binval))) ||
 				(text = xmlnode_get_data(photo)))) {
-			unsigned char hashval[20];
-			char hash[41], *p;
-			int i;
+			char *hash;
 
 			data = purple_base64_decode(text, &size);
-
-			purple_cipher_digest_region("sha1", data, size,
-					sizeof(hashval), hashval, NULL);
-			p = hash;
-			for(i=0; i<20; i++, p+=2)
-				snprintf(p, 3, "%02x", hashval[i]);
-
+			hash = jabber_calculate_data_sha1sum(data, size);
 			purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash);
+			g_free(hash);
 			g_free(text);
 		}
 	}
--- a/libpurple/protocols/jabber/si.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/jabber/si.c	Thu Dec 11 22:36:47 2008 +0000
@@ -23,7 +23,6 @@
 #include "internal.h"
 
 #include "blist.h"
-#include "cipher.h"
 #include "debug.h"
 #include "ft.h"
 #include "request.h"
@@ -183,9 +182,6 @@
 {
 	JabberSIXfer *jsx = xfer->data;
 	JabberBytestreamsStreamhost *streamhost;
-	char *dstaddr, *p;
-	int i;
-	unsigned char hashval[20];
 	JabberID *dstjid;
 
 	if(!jsx->streamhosts) {
@@ -221,6 +217,7 @@
 	/* TODO: Deal with zeroconf */
 
 	if(dstjid != NULL && streamhost->host && streamhost->port > 0) {
+		char *dstaddr, *hash;
 		jsx->gpi = purple_proxy_info_new();
 		purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5);
 		purple_proxy_info_set_host(jsx->gpi, streamhost->host);
@@ -234,17 +231,13 @@
 			dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource,
 				jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
 
-		purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
-				sizeof(hashval), hashval, NULL);
-		g_free(dstaddr);
-		dstaddr = g_malloc(41);
-		p = dstaddr;
-		for(i=0; i<20; i++, p+=2)
-			snprintf(p, 3, "%02x", hashval[i]);
+		/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
+		hash = jabber_calculate_data_sha1sum(dstaddr, strlen(dstaddr));
 
 		jsx->connect_data = purple_proxy_connect_socks5(NULL, jsx->gpi,
-				dstaddr, 0,
+				hash, 0,
 				jabber_si_bytestreams_connect_cb, xfer);
+		g_free(hash);
 		g_free(dstaddr);
 
 		/* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */
@@ -361,11 +354,9 @@
 {
 	PurpleXfer *xfer = data;
 	JabberSIXfer *jsx = xfer->data;
-	int i;
 	char buffer[256];
 	int len;
-	char *dstaddr, *p;
-	unsigned char hashval[20];
+	char *dstaddr, *hash;
 	const char *host;
 
 	purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n");
@@ -421,23 +412,20 @@
 			jsx->js->user->node, jsx->js->user->domain,
 			jsx->js->user->resource, xfer->who);
 
-	purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
-				    sizeof(hashval), hashval, NULL);
-	g_free(dstaddr);
-	dstaddr = g_malloc(41);
-	p = dstaddr;
-	for(i=0; i<20; i++, p+=2)
-		snprintf(p, 3, "%02x", hashval[i]);
+	/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
+	hash = jabber_calculate_data_sha1sum(dstaddr, strlen(dstaddr));
 
-	if(jsx->rxqueue[4] != 40 || strncmp(dstaddr, jsx->rxqueue+5, 40) ||
+	if(jsx->rxqueue[4] != 40 || strncmp(hash, jsx->rxqueue+5, 40) ||
 			jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
 		purple_debug_error("jabber", "someone connected with the wrong info!\n");
 		close(source);
 		purple_xfer_cancel_remote(xfer);
+		g_free(hash);
 		g_free(dstaddr);
 		return;
 	}
 
+	g_free(hash);
 	g_free(dstaddr);
 
 	g_free(jsx->rxqueue);
--- a/libpurple/protocols/myspace/myspace.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Thu Dec 11 22:36:47 2008 +0000
@@ -1334,28 +1334,21 @@
 {
 	MsimSession *session;
 	time_t delta;
-	gchar *errmsg;
 
 	session = (MsimSession *)data;
 
 	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 
 	delta = time(NULL) - session->last_comm;
+
 	/* purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); */
 	if (delta >= MSIM_KEEPALIVE_INTERVAL) {
-	        errmsg = g_strdup_printf(ngettext("Connection to server lost (no data received within %d second)",
-						  "Connection to server lost (no data received within %d seconds)",
-						  (int)delta),
-					 (int)delta);
-
-		purple_debug_info("msim", "msim_check_alive: %s > interval of %d, presumed dead\n",
-				errmsg, MSIM_KEEPALIVE_INTERVAL);
+		purple_debug_info("msim",
+				"msim_check_alive: %zu > interval of %d, presumed dead\n",
+				delta, MSIM_KEEPALIVE_INTERVAL);
 		purple_connection_error_reason(session->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg);
-
-		purple_notify_error(session->gc, NULL, errmsg, NULL);
-
-		g_free(errmsg);
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Lost connection with server"));
 
 		return FALSE;
 	}
--- a/libpurple/protocols/oscar/family_locate.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Thu Dec 11 22:36:47 2008 +0000
@@ -203,11 +203,9 @@
 	 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
 	  0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
 
-	/*
-	{OSCAR_CAPABILITY_ICQ2GO,
+	{OSCAR_CAPABILITY_TYPING,
 	 {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
 	  0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
-	*/
 
 	/*
 	 * Chat is oddball.
--- a/libpurple/protocols/oscar/oscar.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Dec 11 22:36:47 2008 +0000
@@ -68,7 +68,7 @@
 
 static OscarCapability purple_caps = (OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM |
 									  OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE |
-									  OSCAR_CAPABILITY_SHORTCAPS);
+									  OSCAR_CAPABILITY_SHORTCAPS | OSCAR_CAPABILITY_TYPING);
 
 static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
 static guint8 features_icq[] = {0x01, 0x06};
--- a/libpurple/protocols/oscar/oscar.h	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Thu Dec 11 22:36:47 2008 +0000
@@ -362,8 +362,9 @@
 	OSCAR_CAPABILITY_LIVEVIDEO            = 0x02000000,
 	OSCAR_CAPABILITY_CAMERA               = 0x04000000,
 	OSCAR_CAPABILITY_ICHAT_SCREENSHARE    = 0x08000000,
-	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x10000000,
-	OSCAR_CAPABILITY_LAST                 = 0x20000000
+	OSCAR_CAPABILITY_TYPING               = 0x10000000,
+	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x20000000,
+	OSCAR_CAPABILITY_LAST                 = 0x40000000
 } OscarCapability;
 
 /*
--- a/libpurple/protocols/qq/buddy_info.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Thu Dec 11 22:36:47 2008 +0000
@@ -504,8 +504,6 @@
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
 	const gchar *icon_path = purple_account_get_buddy_icon_path(account);
-	gchar **segments;
-	gint index;
 
 	g_return_if_fail(icon_path != NULL);
 
@@ -514,12 +512,6 @@
 	 *  purple_imgstore_get_filename is always new file
 	 *  QQ buddy may set custom icon if level is over 16 */
 	purple_debug_info("QQ", "Change my icon to %s\n", icon_path);
-	segments = g_strsplit_set(icon_path, G_DIR_SEPARATOR_S, 0);
-	for (index = 0; segments[index] != NULL; index++) {
-		purple_debug_info("QQ", "Split to %s\n", segments[index]);
-	}
-
-	g_strfreev(segments);
 }
 
 gchar *qq_get_icon_name(gint face)
--- a/libpurple/protocols/qq/buddy_opt.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Thu Dec 11 22:36:47 2008 +0000
@@ -317,9 +317,9 @@
 	add_req->auth_len = 0;
 
 	who = uid_to_purple_name(uid);
-	msg = g_strdup_printf(_("%u needs Q&A"), uid);
-	purple_request_input(gc, _("Add buddy Q&A"), msg,
-			_("Input answer here"),
+	msg = g_strdup_printf(_("%u requires verification"), uid);
+	purple_request_input(gc, _("Add buddy question"), msg,
+			_("Enter answer here"),
 			NULL,
 			TRUE, FALSE, NULL,
 			_("Send"), G_CALLBACK(add_buddy_question_cb),
@@ -616,7 +616,7 @@
 	qq_buddy_req *add_req = (qq_buddy_req *)data;
 	gchar *who = uid_to_purple_name(add_req->uid);
 	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
-			NULL, _("Sorry, You are not my style."), TRUE, FALSE, NULL,
+			NULL, _("Sorry, you're not my style."), TRUE, FALSE, NULL,
 			_("OK"), G_CALLBACK(buddy_add_deny_reason_cb),
 			_("Cancel"), G_CALLBACK(buddy_add_deny_noreason_cb),
 			purple_connection_get_account(add_req->gc), who, NULL,
@@ -661,9 +661,9 @@
 	}
 
 	who = uid_to_purple_name(uid);
-	msg = g_strdup_printf(_("%u needs authentication"), uid);
+	msg = g_strdup_printf(_("%u needs authorization"), uid);
 	purple_request_input(gc, _("Add buddy authorize"), msg,
-			_("Input request here"),
+			_("Enter request here"),
 			_("Would you be my friend?"),
 			TRUE, FALSE, NULL,
 			_("Send"), G_CALLBACK(add_buddy_auth_cb),
--- a/libpurple/protocols/qq/group.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/group.c	Thu Dec 11 22:36:47 2008 +0000
@@ -116,7 +116,7 @@
 	return qd->roomlist;
 }
 
-/* free roomlist space, I have no idea when this one is called ... */
+/* free roomlist space, I have no idea when this one is called... */
 void qq_roomlist_cancel(PurpleRoomlist *list)
 {
 	qq_data *qd;
--- a/libpurple/protocols/qq/group_join.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Thu Dec 11 22:36:47 2008 +0000
@@ -219,11 +219,11 @@
 
 	rmd = qq_room_data_find(gc, id);
 	if (rmd != NULL) {
-		msg = g_strdup_printf(_("Succeeded joining Qun %s (%u)"), rmd->title_utf8, rmd->ext_id);
+		msg = g_strdup_printf(_("Successfully joined Qun %s (%u)"), rmd->title_utf8, rmd->ext_id);
 		qq_got_message(gc, msg);
 		g_free(msg);
 	} else {
-		qq_got_message(gc, _("Succeeded joining Qun"));
+		qq_got_message(gc, _("Successfully joined Qun"));
 	}
 }
 
--- a/libpurple/protocols/qq/group_opt.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Thu Dec 11 22:36:47 2008 +0000
@@ -122,7 +122,7 @@
 
 	who = uid_to_purple_name(add_req->member);
 	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
-			NULL, _("Sorry, you are not our style ..."), TRUE, FALSE, NULL,
+			NULL, _("Sorry, you are not our style"), TRUE, FALSE, NULL,
 			_("OK"), G_CALLBACK(member_join_deny_reason_cb),
 			_("Cancel"), G_CALLBACK(member_join_deny_noreason_cb),
 			purple_connection_get_account(add_req->gc), who, NULL,
@@ -204,7 +204,7 @@
 
 	purple_debug_info("QQ", "Succeed in modify members for room %u\n", rmd->ext_id);
 
-	qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun member"), now);
+	qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun members"), now);
 }
 
 void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd)
@@ -347,7 +347,7 @@
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			    _("You have successfully created a Qun"),
-			    _("Would you like to set up the detail information now?"),
+			    _("Would you like to set up detailed information now?"),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
 				add_req, 2,
@@ -519,7 +519,7 @@
 		rmd->my_role = QQ_ROOM_ROLE_YES;
 	}
 
-	msg = g_strdup_printf(_("<b>Joinning Qun %u is approved by admin %u for %s</b>"),
+	msg = g_strdup_printf(_("<b>Joining Qun %u is approved by admin %u for %s</b>"),
 			ext_id, admin_uid, reason);
 	now = time(NULL);
 	qq_room_got_chat_in(gc, id, 0, msg, now);
--- a/libpurple/protocols/qq/qq.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Thu Dec 11 22:36:47 2008 +0000
@@ -496,7 +496,7 @@
 	icon_path = qq_get_icon_path(icon_name);
 	g_free(icon_name);
 
-	purple_debug_info("QQ", "Change prev icon %s to ...\n", icon_path);
+	purple_debug_info("QQ", "Change prev icon %s to...\n", icon_path);
 	purple_request_file(action, _("Select icon..."), icon_path,
 			FALSE,
 			G_CALLBACK(qq_change_icon_cb), NULL,
--- a/libpurple/protocols/qq/qq_base.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Thu Dec 11 22:36:47 2008 +0000
@@ -424,7 +424,7 @@
 			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
 					">>> [default] decrypt and dump");
 			error = g_strdup_printf(
-						_("Unknown reply code when login (0x%02X)"),
+						_("Unknown reply code when logging in (0x%02X)"),
 						ret );
 			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
 			break;
@@ -464,14 +464,16 @@
 	qq_data *qd;
 	gchar **segments;
 
-	g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
+	g_return_val_if_fail(data != NULL, FALSE);
+	g_return_val_if_fail(data_len != 0, FALSE);
 
 	qd = (qq_data *) gc->proto_data;
 
 	/* qq_show_packet("Keep alive reply packet", data, len); */
 
 	/* the last one is 60, don't know what it is */
-	if (NULL == (segments = split_data(data, data_len, "\x1f", 6)))
+	segments = split_data(data, data_len, "\x1f", 6);
+	if (segments == NULL)
 			return TRUE;
 
 	/* segments[0] and segment[1] are all 0x30 ("0") */
@@ -479,7 +481,7 @@
 	if(0 == qd->online_total) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Keep alive error"));
+				_("Lost connection with server"));
 	}
 	qd->my_ip.s_addr = inet_addr(segments[3]);
 	qd->my_port = strtol(segments[4], NULL, 10);
@@ -528,7 +530,7 @@
 	if(0 == qd->online_total) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Keep alive error"));
+				_("Lost connection with server"));
 	}
 
 	bytes += qq_getIP(&qd->my_ip, data + bytes);
@@ -572,7 +574,7 @@
 	if(0 == qd->online_total) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Keep alive error"));
+				_("Lost connection with server"));
 	}
 
 	bytes += qq_getIP(&qd->my_ip, data + bytes);
@@ -653,7 +655,7 @@
 	if (data_len < 15) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
-				_("Can not decrypt get server reply"));
+				_("Could not decrypt server reply"));
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
@@ -745,7 +747,7 @@
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
 
-	purple_connection_update_progress(gc, _("Requesting captcha ..."), 3, QQ_CONNECT_STEPS);
+	purple_connection_update_progress(gc, _("Requesting captcha"), 3, QQ_CONNECT_STEPS);
 }
 
 static void request_token_ex_code(PurpleConnection *gc,
@@ -790,7 +792,7 @@
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
 
-	purple_connection_update_progress(gc, _("Checking code of captcha ..."), 3, QQ_CONNECT_STEPS);
+	purple_connection_update_progress(gc, _("Checking captcha"), 3, QQ_CONNECT_STEPS);
 }
 
 typedef struct {
@@ -813,7 +815,7 @@
 
 	purple_connection_error_reason(captcha_req->gc,
 			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
-			_("Failed captcha verify"));
+			_("Failed captcha verification"));
 }
 
 static void captcha_input_ok_cb(qq_captcha_request *captcha_req,
@@ -872,8 +874,8 @@
 	purple_request_field_group_add_field(group, field);
 
 	purple_request_fields(account,
-		_("QQ Captcha Verifying"),
-		_("QQ Captcha Verifying"),
+		_("QQ Captcha Verification"),
+		_("QQ Captcha Verification"),
 		_("Enter the text from the image"),
 		fields,
 		_("OK"), G_CALLBACK(captcha_input_ok_cb),
@@ -1111,7 +1113,7 @@
 			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
 					">>> [default] decrypt and dump");
 			error = g_strdup_printf(
-						_("Unknown reply code when checking password (0x%02X)"),
+						_("Unknown reply when checking password (0x%02X)"),
 						ret );
 			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
 			break;
@@ -1257,7 +1259,7 @@
 				/* Missing get server before login*/
 			default:
 				error = g_strdup_printf(
-						_("Unknown reply code when login (0x%02X):\n%s"),
+						_("Unknown reply code when logging in (0x%02X):\n%s"),
 						ret, msg_utf8);
 				break;
 		}
@@ -1446,7 +1448,7 @@
 				break;
 			default:
 				error = g_strdup_printf(
-						_("Unknown reply code when login (0x%02X):\n%s"),
+						_("Unknown reply code when logging in (0x%02X):\n%s"),
 						ret, msg_utf8);
 				break;
 		}
--- a/libpurple/protocols/qq/qq_network.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Thu Dec 11 22:36:47 2008 +0000
@@ -376,7 +376,7 @@
 			/* No worries */
 			return;
 
-		error_msg = g_strdup_printf(_("Lost connection with server: %d, %s"), errno, g_strerror(errno));
+		error_msg = g_strdup_printf(_("Lost connection with server:\n%s"), g_strerror(errno));
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				error_msg);
@@ -773,12 +773,12 @@
 	set_all_keys( gc );
 
 	if (qd->client_version >= 2007) {
-		purple_connection_update_progress(gc, _("Get server ..."), 2, QQ_CONNECT_STEPS);
+		purple_connection_update_progress(gc, _("Getting server"), 2, QQ_CONNECT_STEPS);
 		qq_request_get_server(gc);
 		return;
 	}
 
-	purple_connection_update_progress(gc, _("Request token"), 2, QQ_CONNECT_STEPS);
+	purple_connection_update_progress(gc, _("Requesting token"), 2, QQ_CONNECT_STEPS);
 	qq_request_token(gc);
 }
 
@@ -935,7 +935,7 @@
 		return FALSE;
 	}
 
-	purple_connection_update_progress(gc, _("Connecting to server ..."), 1, QQ_CONNECT_STEPS);
+	purple_connection_update_progress(gc, _("Connecting to server"), 1, QQ_CONNECT_STEPS);
 
 	purple_debug_info("QQ", "Connect to %s:%d\n", server, port);
 
@@ -986,7 +986,7 @@
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	purple_debug_info("QQ", "Disconnecting ...\n");
+	purple_debug_info("QQ", "Disconnecting...\n");
 
 	if (qd->network_watcher > 0) {
 		purple_debug_info("QQ", "Remove network watcher\n");
--- a/libpurple/protocols/qq/qq_process.c	Thu Dec 11 22:36:29 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Thu Dec 11 22:36:47 2008 +0000
@@ -87,7 +87,7 @@
 
 	if (data[0] != 0) {
 		purple_debug_warning("QQ", "Failed sent IM\n");
-		purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
+		purple_notify_error(gc, _("Error"), _("Unable to send message."), NULL);
 		return;
 	}
 
@@ -948,7 +948,7 @@
 		qq_show_packet("Can not decrypted", rcved, rcved_len);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
-				_("Can not decrypt login reply"));
+				_("Could not decrypt login reply"));
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
--- a/pidgin/plugins/perl/common/GtkIMHtml.xs	Thu Dec 11 22:36:29 2008 +0000
+++ b/pidgin/plugins/perl/common/GtkIMHtml.xs	Thu Dec 11 22:36:47 2008 +0000
@@ -173,7 +173,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(unused));
 
-	for (i = 0; i < t_len; i++) {
+	for (i = 0; i <= t_len; i++) {
 		STRLEN t_sl;
 		t_GL = g_slist_append(t_GL, SvPV(*av_fetch((AV *)SvRV(unused), i, 0), t_sl));
 	}