comparison libpurple/protocols/oscar/oscar.c @ 17632: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 43be5361c4dc
children 40d51793f2d7
comparison
equal deleted inserted replaced
17631:91e92cb0d875 17632:36ebcb33e2eb
296 296
297 return ret; 297 return ret;
298 } 298 }
299 299
300 gchar * 300 gchar *
301 oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen) 301 oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen)
302 { 302 {
303 gchar *utf8 = NULL; 303 gchar *utf8 = NULL;
304 304
305 if ((encoding == NULL) || encoding[0] == '\0') { 305 if ((encoding == NULL) || encoding[0] == '\0') {
306 purple_debug_info("oscar", "Empty encoding, assuming UTF-8\n"); 306 purple_debug_info("oscar", "Empty encoding, assuming UTF-8\n");
309 } else if (!g_ascii_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1") || 309 } else if (!g_ascii_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1") ||
310 !g_ascii_strcasecmp(encoding, "us-ascii")) 310 !g_ascii_strcasecmp(encoding, "us-ascii"))
311 { 311 {
312 utf8 = g_convert(text, textlen, "UTF-8", "Windows-1252", NULL, NULL, NULL); 312 utf8 = g_convert(text, textlen, "UTF-8", "Windows-1252", NULL, NULL, NULL);
313 } else if (!g_ascii_strcasecmp(encoding, "unicode-2-0")) { 313 } else if (!g_ascii_strcasecmp(encoding, "unicode-2-0")) {
314 utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", NULL, NULL, NULL); 314 /* Some official ICQ clients are apparently total crack,
315 * and have been known to save a UTF-8 string converted
316 * from the locale character set to UCS-2 (not from UTF-8
317 * to UCS-2!) in the away message. This hack should find
318 * and do something (un)reasonable with that, and not
319 * mess up too much else. */
320 const gchar *charset = purple_account_get_string(account, "encoding", NULL);
321 if (charset) {
322 gsize len;
323 utf8 = g_convert(text, textlen, charset, "UCS-2BE", &len, NULL, NULL);
324 if (!utf8 || len != textlen || !g_utf8_validate(utf8, -1, NULL)) {
325 g_free(utf8);
326 utf8 = NULL;
327 } else {
328 purple_debug_info("oscar", "Used broken ICQ fallback encoding\n");
329 }
330 }
331 if (!utf8)
332 utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", NULL, NULL, NULL);
315 } else if (g_ascii_strcasecmp(encoding, "utf-8")) { 333 } else if (g_ascii_strcasecmp(encoding, "utf-8")) {
316 purple_debug_warning("oscar", "Unrecognized character encoding \"%s\", " 334 purple_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
317 "attempting to convert to UTF-8 anyway\n", encoding); 335 "attempting to convert to UTF-8 anyway\n", encoding);
318 utf8 = g_convert(text, textlen, "UTF-8", encoding, NULL, NULL, NULL); 336 utf8 = g_convert(text, textlen, "UTF-8", encoding, NULL, NULL, NULL);
319 } 337 }
1772 */ 1790 */
1773 if (info->status != NULL) 1791 if (info->status != NULL)
1774 { 1792 {
1775 have_status_message = TRUE; 1793 have_status_message = TRUE;
1776 if (info->status[0] != '\0') 1794 if (info->status[0] != '\0')
1777 message = oscar_encoding_to_utf8(info->status_encoding, 1795 message = oscar_encoding_to_utf8(account, info->status_encoding,
1778 info->status, info->status_len); 1796 info->status, info->status_len);
1779 } 1797 }
1780 1798
1781 if (info->flags & AIM_FLAG_WIRELESS) 1799 if (info->flags & AIM_FLAG_WIRELESS)
1782 { 1800 {
1788 if (have_status_message) 1806 if (have_status_message)
1789 { 1807 {
1790 if ((status_id == OSCAR_STATUS_ID_AVAILABLE) && (info->itmsurl != NULL)) 1808 if ((status_id == OSCAR_STATUS_ID_AVAILABLE) && (info->itmsurl != NULL))
1791 { 1809 {
1792 char *itmsurl; 1810 char *itmsurl;
1793 itmsurl = oscar_encoding_to_utf8(info->itmsurl_encoding, 1811 itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
1794 info->itmsurl, info->itmsurl_len); 1812 info->itmsurl, info->itmsurl_len);
1795 purple_prpl_got_user_status(account, info->sn, status_id, 1813 purple_prpl_got_user_status(account, info->sn, status_id,
1796 "message", message, "itmsurl", itmsurl, NULL); 1814 "message", message, "itmsurl", itmsurl, NULL);
1797 g_free(itmsurl); 1815 g_free(itmsurl);
1798 } 1816 }
2045 { 2063 {
2046 if (args->encoding != NULL) 2064 if (args->encoding != NULL)
2047 { 2065 {
2048 char *encoding = NULL; 2066 char *encoding = NULL;
2049 encoding = oscar_encoding_extract(args->encoding); 2067 encoding = oscar_encoding_extract(args->encoding);
2050 message = oscar_encoding_to_utf8(encoding, args->msg, args->msglen); 2068 message = oscar_encoding_to_utf8(account, encoding, args->msg,
2069 args->msglen);
2051 g_free(encoding); 2070 g_free(encoding);
2052 } else { 2071 } else {
2053 if (g_utf8_validate(args->msg, args->msglen, NULL)) 2072 if (g_utf8_validate(args->msg, args->msglen, NULL))
2054 message = g_strdup(args->msg); 2073 message = g_strdup(args->msg);
2055 } 2074 }
2063 if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) { 2082 if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) {
2064 g_free(message); 2083 g_free(message);
2065 return 1; 2084 return 1;
2066 } 2085 }
2067 encoding = args->encoding ? oscar_encoding_extract(args->encoding) : NULL; 2086 encoding = args->encoding ? oscar_encoding_extract(args->encoding) : NULL;
2068 utf8name = oscar_encoding_to_utf8(encoding, 2087 utf8name = oscar_encoding_to_utf8(account, encoding,
2069 args->info.chat.roominfo.name, 2088 args->info.chat.roominfo.name,
2070 args->info.chat.roominfo.namelen); 2089 args->info.chat.roominfo.namelen);
2071 g_free(encoding); 2090 g_free(encoding);
2072 2091
2073 tmp = extract_name(utf8name); 2092 tmp = extract_name(utf8name);
2873 2892
2874 /* Available message */ 2893 /* Available message */
2875 if ((userinfo->status != NULL) && !(userinfo->flags & AIM_FLAG_AWAY)) 2894 if ((userinfo->status != NULL) && !(userinfo->flags & AIM_FLAG_AWAY))
2876 { 2895 {
2877 if (userinfo->status[0] != '\0') 2896 if (userinfo->status[0] != '\0')
2878 tmp = oscar_encoding_to_utf8(userinfo->status_encoding, 2897 tmp = oscar_encoding_to_utf8(account, userinfo->status_encoding,
2879 userinfo->status, userinfo->status_len); 2898 userinfo->status, userinfo->status_len);
2880 #if defined (_WIN32) || defined (__APPLE__) 2899 #if defined (_WIN32) || defined (__APPLE__)
2881 if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) { 2900 if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
2882 gchar *itmsurl, *tmp2; 2901 gchar *itmsurl, *tmp2;
2883 itmsurl = oscar_encoding_to_utf8(userinfo->itmsurl_encoding, 2902 itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
2884 userinfo->itmsurl, userinfo->itmsurl_len); 2903 userinfo->itmsurl, userinfo->itmsurl_len);
2885 tmp2 = g_strdup_printf("<a href=\"%s\">%s</a>", 2904 tmp2 = g_strdup_printf("<a href=\"%s\">%s</a>",
2886 itmsurl, tmp); 2905 itmsurl, tmp);
2887 g_free(tmp); 2906 g_free(tmp);
2888 tmp = tmp2; 2907 tmp = tmp2;
2894 } 2913 }
2895 2914
2896 /* Away message */ 2915 /* Away message */
2897 if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) { 2916 if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
2898 tmp = oscar_encoding_extract(userinfo->away_encoding); 2917 tmp = oscar_encoding_extract(userinfo->away_encoding);
2899 away_utf8 = oscar_encoding_to_utf8(tmp, userinfo->away, userinfo->away_len); 2918 away_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->away,
2919 userinfo->away_len);
2900 g_free(tmp); 2920 g_free(tmp);
2901 if (away_utf8 != NULL) { 2921 if (away_utf8 != NULL) {
2902 tmp = purple_str_sub_away_formatters(away_utf8, purple_account_get_username(account)); 2922 tmp = purple_str_sub_away_formatters(away_utf8, purple_account_get_username(account));
2903 purple_notify_user_info_add_section_break(user_info); 2923 purple_notify_user_info_add_section_break(user_info);
2904 oscar_user_info_add_pair(user_info, NULL, tmp); 2924 oscar_user_info_add_pair(user_info, NULL, tmp);
2908 } 2928 }
2909 2929
2910 /* Info */ 2930 /* Info */
2911 if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) { 2931 if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
2912 tmp = oscar_encoding_extract(userinfo->info_encoding); 2932 tmp = oscar_encoding_extract(userinfo->info_encoding);
2913 info_utf8 = oscar_encoding_to_utf8(tmp, userinfo->info, userinfo->info_len); 2933 info_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->info,
2934 userinfo->info_len);
2914 g_free(tmp); 2935 g_free(tmp);
2915 if (info_utf8 != NULL) { 2936 if (info_utf8 != NULL) {
2916 tmp = purple_str_sub_away_formatters(info_utf8, purple_account_get_username(account)); 2937 tmp = purple_str_sub_away_formatters(info_utf8, purple_account_get_username(account));
2917 purple_notify_user_info_add_section_break(user_info); 2938 purple_notify_user_info_add_section_break(user_info);
2918 oscar_user_info_add_pair(user_info, _("Profile"), tmp); 2939 oscar_user_info_add_pair(user_info, _("Profile"), tmp);
2928 } 2949 }
2929 2950
2930 static int purple_got_infoblock(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) 2951 static int purple_got_infoblock(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
2931 { 2952 {
2932 PurpleConnection *gc = od->gc; 2953 PurpleConnection *gc = od->gc;
2954 PurpleAccount *account = purple_connection_get_account(gc);
2933 PurpleBuddy *b; 2955 PurpleBuddy *b;
2934 PurplePresence *presence; 2956 PurplePresence *presence;
2935 PurpleStatus *status; 2957 PurpleStatus *status;
2936 gchar *message = NULL; 2958 gchar *message = NULL;
2937 2959
2940 2962
2941 va_start(ap, fr); 2963 va_start(ap, fr);
2942 userinfo = va_arg(ap, aim_userinfo_t *); 2964 userinfo = va_arg(ap, aim_userinfo_t *);
2943 va_end(ap); 2965 va_end(ap);
2944 2966
2945 b = purple_find_buddy(purple_connection_get_account(gc), userinfo->sn); 2967 b = purple_find_buddy(account, userinfo->sn);
2946 if (b == NULL) 2968 if (b == NULL)
2947 return 1; 2969 return 1;
2948 2970
2949 if (!aim_sn_is_icq(userinfo->sn)) 2971 if (!aim_sn_is_icq(userinfo->sn))
2950 { 2972 {
2960 if (!purple_status_is_available(status) && purple_status_is_online(status)) 2982 if (!purple_status_is_available(status) && purple_status_is_online(status))
2961 { 2983 {
2962 if ((userinfo->flags & AIM_FLAG_AWAY) && 2984 if ((userinfo->flags & AIM_FLAG_AWAY) &&
2963 (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) { 2985 (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
2964 gchar *charset = oscar_encoding_extract(userinfo->away_encoding); 2986 gchar *charset = oscar_encoding_extract(userinfo->away_encoding);
2965 message = oscar_encoding_to_utf8(charset, userinfo->away, userinfo->away_len); 2987 message = oscar_encoding_to_utf8(account, charset,
2988 userinfo->away,
2989 userinfo->away_len);
2966 g_free(charset); 2990 g_free(charset);
2967 purple_status_set_attr_string(status, "message", message); 2991 purple_status_set_attr_string(status, "message", message);
2968 g_free(message); 2992 g_free(message);
2969 } 2993 }
2970 else 2994 else
3156 return 1; 3180 return 1;
3157 } 3181 }
3158 3182
3159 static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { 3183 static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3160 PurpleConnection *gc = od->gc; 3184 PurpleConnection *gc = od->gc;
3185 PurpleAccount *account = purple_connection_get_account(gc);
3161 struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn); 3186 struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
3162 gchar *utf8; 3187 gchar *utf8;
3163 va_list ap; 3188 va_list ap;
3164 aim_userinfo_t *info; 3189 aim_userinfo_t *info;
3165 int len; 3190 int len;
3174 len = va_arg(ap, int); 3199 len = va_arg(ap, int);
3175 msg = va_arg(ap, char *); 3200 msg = va_arg(ap, char *);
3176 charset = va_arg(ap, char *); 3201 charset = va_arg(ap, char *);
3177 va_end(ap); 3202 va_end(ap);
3178 3203
3179 utf8 = oscar_encoding_to_utf8(charset, msg, len); 3204 utf8 = oscar_encoding_to_utf8(account, charset, msg, len);
3180 if (utf8 == NULL) 3205 if (utf8 == NULL)
3181 /* The conversion failed! */ 3206 /* The conversion failed! */
3182 utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]")); 3207 utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]"));
3183 serv_got_chat_in(gc, ccon->id, info->sn, 0, utf8, time((time_t)NULL)); 3208 serv_got_chat_in(gc, ccon->id, info->sn, 0, utf8, time((time_t)NULL));
3184 g_free(utf8); 3209 g_free(utf8);