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