changeset 17871:36ebcb33e2eb

This fixes a bustination of the official ICQ client in at least some locales. For away (and possibly other) messages, apparently the official ICQ (5.1?) client of some locales converts messages which are stored in UTF-8 from a locale-native character set to UCS-2BE; this results in something which, when decoded "correctly", is gibberish. Instead, we first try decoding from UCS-2BE to the locale-specific character set, and if that validates as UTF-8, we display it, instead. Since UTF-8 is relatively picky, hopefully this won't break too many sane clients.
author Ethan Blanton <elb@pidgin.im>
date Thu, 14 Jun 2007 18:20:53 +0000
parents 91e92cb0d875
children 81d42bfe31fa
files libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h
diffstat 2 files changed, 39 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/oscar/oscar.c	Thu Jun 14 16:17:07 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Jun 14 18:20:53 2007 +0000
@@ -298,7 +298,7 @@
 }
 
 gchar *
-oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen)
+oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen)
 {
 	gchar *utf8 = NULL;
 
@@ -311,7 +311,25 @@
 	{
 		utf8 = g_convert(text, textlen, "UTF-8", "Windows-1252", NULL, NULL, NULL);
 	} else if (!g_ascii_strcasecmp(encoding, "unicode-2-0")) {
-		utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", NULL, NULL, NULL);
+		/* Some official ICQ clients are apparently total crack,
+		 * and have been known to save a UTF-8 string converted
+		 * from the locale character set to UCS-2 (not from UTF-8
+		 * to UCS-2!) in the away message.  This hack should find
+		 * and do something (un)reasonable with that, and not
+		 * mess up too much else. */
+		const gchar *charset = purple_account_get_string(account, "encoding", NULL);
+		if (charset) {
+			gsize len;
+			utf8 = g_convert(text, textlen, charset, "UCS-2BE", &len, NULL, NULL);
+			if (!utf8 || len != textlen || !g_utf8_validate(utf8, -1, NULL)) {
+				g_free(utf8);
+				utf8 = NULL;
+			} else {
+				purple_debug_info("oscar", "Used broken ICQ fallback encoding\n");
+			}
+		}
+		if (!utf8)
+			utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", NULL, NULL, NULL);
 	} else if (g_ascii_strcasecmp(encoding, "utf-8")) {
 		purple_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
 						   "attempting to convert to UTF-8 anyway\n", encoding);
@@ -1774,7 +1792,7 @@
 	{
 		have_status_message = TRUE;
 		if (info->status[0] != '\0')
-			message = oscar_encoding_to_utf8(info->status_encoding,
+			message = oscar_encoding_to_utf8(account, info->status_encoding,
 											 info->status, info->status_len);
 	}
 
@@ -1790,7 +1808,7 @@
 		if ((status_id == OSCAR_STATUS_ID_AVAILABLE) && (info->itmsurl != NULL))
 		{
 			char *itmsurl;
-			itmsurl = oscar_encoding_to_utf8(info->itmsurl_encoding,
+			itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
 					info->itmsurl, info->itmsurl_len);
 			purple_prpl_got_user_status(account, info->sn, status_id,
 					"message", message, "itmsurl", itmsurl, NULL);
@@ -2047,7 +2065,8 @@
 		{
 			char *encoding = NULL;
 			encoding = oscar_encoding_extract(args->encoding);
-			message = oscar_encoding_to_utf8(encoding, args->msg, args->msglen);
+			message = oscar_encoding_to_utf8(account, encoding, args->msg,
+			                                 args->msglen);
 			g_free(encoding);
 		} else {
 			if (g_utf8_validate(args->msg, args->msglen, NULL))
@@ -2065,7 +2084,7 @@
 			return 1;
 		}
 		encoding = args->encoding ? oscar_encoding_extract(args->encoding) : NULL;
-		utf8name = oscar_encoding_to_utf8(encoding,
+		utf8name = oscar_encoding_to_utf8(account, encoding,
 				args->info.chat.roominfo.name,
 				args->info.chat.roominfo.namelen);
 		g_free(encoding);
@@ -2875,12 +2894,12 @@
 	if ((userinfo->status != NULL) && !(userinfo->flags & AIM_FLAG_AWAY))
 	{
 		if (userinfo->status[0] != '\0')
-			tmp = oscar_encoding_to_utf8(userinfo->status_encoding,
+			tmp = oscar_encoding_to_utf8(account, userinfo->status_encoding,
 											 userinfo->status, userinfo->status_len);
 #if defined (_WIN32) || defined (__APPLE__)
 		if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
 			gchar *itmsurl, *tmp2;
-			itmsurl = oscar_encoding_to_utf8(userinfo->itmsurl_encoding,
+			itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
 					userinfo->itmsurl, userinfo->itmsurl_len);
 			tmp2 = g_strdup_printf("<a href=\"%s\">%s</a>",
 					itmsurl, tmp);
@@ -2896,7 +2915,8 @@
 	/* Away message */
 	if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
 		tmp = oscar_encoding_extract(userinfo->away_encoding);
-		away_utf8 = oscar_encoding_to_utf8(tmp, userinfo->away, userinfo->away_len);
+		away_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->away,
+		                                   userinfo->away_len);
 		g_free(tmp);
 		if (away_utf8 != NULL) {
 			tmp = purple_str_sub_away_formatters(away_utf8, purple_account_get_username(account));
@@ -2910,7 +2930,8 @@
 	/* Info */
 	if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
 		tmp = oscar_encoding_extract(userinfo->info_encoding);
-		info_utf8 = oscar_encoding_to_utf8(tmp, userinfo->info, userinfo->info_len);
+		info_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->info,
+		                                   userinfo->info_len);
 		g_free(tmp);
 		if (info_utf8 != NULL) {
 			tmp = purple_str_sub_away_formatters(info_utf8, purple_account_get_username(account));
@@ -2930,6 +2951,7 @@
 static int purple_got_infoblock(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
 {
 	PurpleConnection *gc = od->gc;
+	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleBuddy *b;
 	PurplePresence *presence;
 	PurpleStatus *status;
@@ -2942,7 +2964,7 @@
 	userinfo = va_arg(ap, aim_userinfo_t *);
 	va_end(ap);
 
-	b = purple_find_buddy(purple_connection_get_account(gc), userinfo->sn);
+	b = purple_find_buddy(account, userinfo->sn);
 	if (b == NULL)
 		return 1;
 
@@ -2962,7 +2984,9 @@
 		if ((userinfo->flags & AIM_FLAG_AWAY) &&
 			(userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
 			gchar *charset = oscar_encoding_extract(userinfo->away_encoding);
-			message = oscar_encoding_to_utf8(charset, userinfo->away, userinfo->away_len);
+			message = oscar_encoding_to_utf8(account, charset,
+			                                 userinfo->away,
+			                                 userinfo->away_len);
 			g_free(charset);
 			purple_status_set_attr_string(status, "message", message);
 			g_free(message);
@@ -3158,6 +3182,7 @@
 
 static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	PurpleConnection *gc = od->gc;
+	PurpleAccount *account = purple_connection_get_account(gc);
 	struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
 	gchar *utf8;
 	va_list ap;
@@ -3176,7 +3201,7 @@
 	charset = va_arg(ap, char *);
 	va_end(ap);
 
-	utf8 = oscar_encoding_to_utf8(charset, msg, len);
+	utf8 = oscar_encoding_to_utf8(account, charset, msg, len);
 	if (utf8 == NULL)
 		/* The conversion failed! */
 		utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]"));
--- a/libpurple/protocols/oscar/oscar.h	Thu Jun 14 16:17:07 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Thu Jun 14 18:20:53 2007 +0000
@@ -934,7 +934,7 @@
 /* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *sn, guint16 type2);
 void aim_icbm_makecookie(guchar* cookie);
 gchar *oscar_encoding_extract(const char *encoding);
-gchar *oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen);
+gchar *oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen);
 gchar *purple_plugin_oscar_decode_im_part(PurpleAccount *account, const char *sourcesn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen);