comparison libpurple/protocols/qq/char_conv.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 48f3837a9625
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
1 /**
2 * @file char_conv.c
3 *
4 * gaim
5 *
6 * Gaim is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #include "debug.h"
26 #include "internal.h"
27
28 #include "char_conv.h"
29 #include "packet_parse.h"
30 #include "qq.h"
31 #include "utils.h"
32
33 #define QQ_SMILEY_AMOUNT 96
34
35 #define UTF8 "UTF-8"
36 #define QQ_CHARSET_ZH_CN "GBK"
37 #define QQ_CHARSET_ENG "ISO-8859-1"
38
39 #define QQ_NULL_MSG "(NULL)" /* return this if conversion fails */
40 #define QQ_NULL_SMILEY "(SM)" /* return this if smiley conversion fails */
41
42 /* a debug function */
43 void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
44
45 const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
46 0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
47 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x73,
48 0x74, 0x75, 0x76, 0x77, 0x8a, 0x8b, 0x8c, 0x8d,
49 0x8e, 0x8f, 0x78, 0x79, 0x7a, 0x7b, 0x90, 0x91,
50 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
51 0x59, 0x5a, 0x5c, 0x58, 0x57, 0x55, 0x7c, 0x7d,
52 0x7e, 0x7f, 0x9a, 0x9b, 0x60, 0x67, 0x9c, 0x9d,
53 0x9e, 0x5e, 0x9f, 0x89, 0x80, 0x81, 0x82, 0x62,
54 0x63, 0x64, 0x65, 0x66, 0x83, 0x68, 0x84, 0x85,
55 0x86, 0x87, 0x6b, 0x6e, 0x6f, 0x70, 0x88, 0xa0,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x56, 0x5b, 0x5d,
57 0x5f, 0x61, 0x69, 0x6a, 0x6c, 0x6d, 0x71, 0x72
58 };
59
60
61 const gchar *gaim_smiley_map[QQ_SMILEY_AMOUNT] = {
62 "/jy", "/pz", "/se", "/fd", "/dy", "/ll", "/hx", "/bz",
63 "/shui", "/dk ", "/gg", "/fn", "/tp", "/cy", "/wx", "/ng",
64 "/kuk", "/feid", "/zk", "/tu", "/tx", "/ka", "/by", "/am",
65 "/jie", "/kun", "/jk", "/lh", "/hanx", "/db", "/fendou",
66 "/zhm",
67 "/yiw", "/xu", "/yun", "/zhem", "/shuai", "/kl", "/qiao",
68 "/zj",
69 "/shan", "/fad", "/aiq", "/tiao", "/zhao", "/mm", "/zt",
70 "/maom",
71 "/xg", "/yb", "/qianc", "/dp", "/bei", "/dg", "/shd",
72 "/zhd",
73 "/dao", "/zq", "/yy", "/bb", "/gf", "/fan", "/yw", "/mg",
74 "/dx", "/wen", "/xin", "/xs", "/hy", "/lw", "/dh", "/sj",
75 "/yj", "/ds", "/ty", "/yl", "/qiang", "/ruo", "/ws",
76 "/shl",
77 "/dd", "/mn", "/hl", "/mamao", "/qz", "/fw", "/oh", "/bj",
78 "/qsh", "/xig", "/xy", "/duoy", "/xr", "/xixing", "/nv",
79 "/nan"
80 };
81
82 /* these functions parse font-attr */
83 static gchar _get_size(gchar font_attr)
84 {
85 return font_attr & 0x1f;
86 }
87
88 static gboolean _check_bold(gchar font_attr)
89 {
90 return (font_attr & 0x20) ? TRUE : FALSE;
91 }
92
93 static gboolean _check_italic(gchar font_attr)
94 {
95 return (font_attr & 0x40) ? TRUE : FALSE;
96 }
97
98 static gboolean _check_underline(gchar font_attr)
99 {
100 return (font_attr & 0x80) ? TRUE : FALSE;
101 }
102
103 /* convert a string from from_charset to to_charset, using g_convert */
104 static gchar *_my_convert(const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset)
105 {
106 GError *error = NULL;
107 gchar *ret;
108 gsize byte_read, byte_write;
109
110 g_return_val_if_fail(str != NULL && to_charset != NULL && from_charset != NULL, g_strdup(QQ_NULL_MSG));
111
112 ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error);
113
114 if (error == NULL)
115 return ret; /* conversion is OK */
116 else { /* conversion error */
117 gchar *failed = hex_dump_to_str((guint8 *) str, (len == -1) ? strlen(str) : len);
118 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "%s\n", error->message);
119 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Dump failed text\n%s", failed);
120 g_free(failed);
121 g_error_free(error);
122 return g_strdup(QQ_NULL_MSG);
123 }
124 }
125
126 /* take the input as a pascal string and return a converted c-string in UTF-8
127 * returns the number of bytes read, return -1 if fatal error
128 * the converted UTF-8 will be saved in ret */
129 gint convert_as_pascal_string(guint8 *data, gchar **ret, const gchar *from_charset)
130 {
131 guint8 len;
132
133 g_return_val_if_fail(data != NULL && from_charset != NULL, -1);
134
135 len = data[0];
136 *ret = _my_convert((gchar *) (data + 1), (gssize) len, UTF8, from_charset);
137
138 return len + 1;
139 }
140
141 /* convert QQ formatted msg to Gaim formatted msg (and UTF-8) */
142 gchar *qq_encode_to_gaim(guint8 *data, gint len, const gchar *msg)
143 {
144 GString *encoded;
145 guint8 font_attr, font_size, color[3], bar, *cursor;
146 gboolean is_bold, is_italic, is_underline;
147 guint16 charset_code;
148 gchar *font_name, *color_code, *msg_utf8, *tmp, *ret;
149
150 cursor = data;
151 _qq_show_packet("QQ_MESG recv for font style", data, len);
152
153 read_packet_b(data, &cursor, len, &font_attr);
154 read_packet_data(data, &cursor, len, color, 3); /* red,green,blue */
155 color_code = g_strdup_printf("#%02x%02x%02x", color[0], color[1], color[2]);
156
157 read_packet_b(data, &cursor, len, &bar); /* skip, not sure of its use */
158 read_packet_w(data, &cursor, len, &charset_code);
159
160 tmp = g_strndup((gchar *) cursor, data + len - cursor);
161 font_name = qq_to_utf8(tmp, QQ_CHARSET_DEFAULT);
162 g_free(tmp);
163
164 font_size = _get_size(font_attr);
165 is_bold = _check_bold(font_attr);
166 is_italic = _check_italic(font_attr);
167 is_underline = _check_underline(font_attr);
168
169 /* Although there is charset returned from QQ msg, it can't be used.
170 * For example, if a user send a Chinese message from English Windows
171 * the charset_code in QQ msg is 0x0000, not 0x8602.
172 * Therefore, it is better to use uniform conversion.
173 * By default, we use GBK, which includes all character of SC, TC, and EN. */
174 msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
175 encoded = g_string_new("");
176
177 /* Henry: The range QQ sends rounds from 8 to 22, where a font size
178 * of 10 is equal to 3 in html font tag */
179 g_string_append_printf(encoded,
180 "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
181 color_code, font_name, font_size / 3);
182 gaim_debug(GAIM_DEBUG_INFO, "QQ_MESG",
183 "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
184 color_code, font_name, font_size / 3);
185 g_string_append(encoded, msg_utf8);
186
187 if (is_bold) {
188 g_string_prepend(encoded, "<b>");
189 g_string_append(encoded, "</b>");
190 }
191 if (is_italic) {
192 g_string_prepend(encoded, "<i>");
193 g_string_append(encoded, "</i>");
194 }
195 if (is_underline) {
196 g_string_prepend(encoded, "<u>");
197 g_string_append(encoded, "</u>");
198 }
199
200 g_string_append(encoded, "</font></font></font>");
201 ret = encoded->str;
202
203 g_free(msg_utf8);
204 g_free(font_name);
205 g_free(color_code);
206 g_string_free(encoded, FALSE);
207
208 return ret;
209 }
210
211 /* two convenience methods, using _my_convert */
212 gchar *utf8_to_qq(const gchar *str, const gchar *to_charset)
213 {
214 return _my_convert(str, -1, to_charset, UTF8);
215 }
216
217 gchar *qq_to_utf8(const gchar *str, const gchar *from_charset)
218 {
219 return _my_convert(str, -1, UTF8, from_charset);
220 }
221
222 /* QQ uses binary code for smiley, while gaim uses strings.
223 * There is a mapping relation between these two. */
224 gchar *qq_smiley_to_gaim(gchar *text)
225 {
226 gint index;
227 gchar qq_smiley, *cur_seg, **segments, *ret;
228 GString *converted;
229
230 converted = g_string_new("");
231 segments = split_data((guint8 *) text, strlen(text), "\x14", 0);
232 g_string_append(converted, segments[0]);
233
234 while ((*(++segments)) != NULL) {
235 cur_seg = *segments;
236 qq_smiley = cur_seg[0];
237 for (index = 0; index < QQ_SMILEY_AMOUNT; index++) {
238 if (qq_smiley_map[index] == qq_smiley)
239 break;
240 }
241 if (index >= QQ_SMILEY_AMOUNT) {
242 g_string_append(converted, QQ_NULL_SMILEY);
243 } else {
244 g_string_append(converted, gaim_smiley_map[index]);
245 g_string_append(converted, (cur_seg + 1));
246 }
247 }
248
249 ret = converted->str;
250 g_string_free(converted, FALSE);
251 return ret;
252 }
253
254 /* convert smiley from gaim style to qq binary code */
255 gchar *gaim_smiley_to_qq(gchar *text)
256 {
257 gchar *begin, *cursor, *ret;
258 gint index;
259 GString *converted;
260
261 converted = g_string_new(text);
262
263 for (index = 0; index < QQ_SMILEY_AMOUNT; index++) {
264 begin = cursor = converted->str;
265 while ((cursor = g_strstr_len(cursor, -1, gaim_smiley_map[index]))) {
266 g_string_erase(converted, (cursor - begin), strlen(gaim_smiley_map[index]));
267 g_string_insert_c(converted, (cursor - begin), 0x14);
268 g_string_insert_c(converted, (cursor - begin + 1), qq_smiley_map[index]);
269 cursor++;
270 }
271 }
272 g_string_append_c(converted, 0x20); /* important for last smiiley */
273
274 ret = converted->str;
275 g_string_free(converted, FALSE);
276 return ret;
277 }