Mercurial > pidgin
comparison libpurple/protocols/qq/im.c @ 24618:682eb95a6aa7
Rewrite smiley convert function
Use qsort and bsearch to convert purple smiley to qq
Rewrite IM format put/get function
Use new smiley map come from EVA and pidgin theme
Support long IM message
author | Hu Yong <ccpaging@gmail.com> |
---|---|
date | Tue, 11 Nov 2008 07:16:23 +0000 |
parents | b6f09c1c79ce |
children | 8084892eb422 |
comparison
equal
deleted
inserted
replaced
24617:8c3cfad8658a | 24618:682eb95a6aa7 |
---|---|
40 #include "packet_parse.h" | 40 #include "packet_parse.h" |
41 #include "qq_network.h" | 41 #include "qq_network.h" |
42 #include "send_file.h" | 42 #include "send_file.h" |
43 #include "utils.h" | 43 #include "utils.h" |
44 | 44 |
45 #define DEFAULT_FONT_NAME_LEN 4 | 45 #define QQ_MSG_IM_MAX 700 /* max length of IM */ |
46 | |
47 enum { | |
48 QQ_IM_TEXT = 0x01, | |
49 QQ_IM_AUTO_REPLY = 0x02 | |
50 }; | |
46 | 51 |
47 enum | 52 enum |
48 { | 53 { |
49 QQ_NORMAL_IM_TEXT = 0x000b, | 54 QQ_NORMAL_IM_TEXT = 0x000b, |
50 QQ_NORMAL_IM_FILE_REQUEST_TCP = 0x0001, | 55 QQ_NORMAL_IM_FILE_REQUEST_TCP = 0x0001, |
61 QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL = 0x85, | 66 QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL = 0x85, |
62 QQ_NORMAL_IM_FILE_EX_NOTIFY_IP = 0x87 | 67 QQ_NORMAL_IM_FILE_EX_NOTIFY_IP = 0x87 |
63 }; | 68 }; |
64 | 69 |
65 typedef struct _qq_im_header qq_im_header; | 70 typedef struct _qq_im_header qq_im_header; |
66 typedef struct _qq_recv_extended_im_text qq_recv_extended_im_text; | |
67 | |
68 struct _qq_im_header { | 71 struct _qq_im_header { |
69 /* this is the common part of normal_text */ | 72 /* this is the common part of normal_text */ |
70 guint16 version_from; | 73 guint16 version_from; |
71 guint32 uid_from; | 74 guint32 uid_from; |
72 guint32 uid_to; | 75 guint32 uid_to; |
73 guint8 session_md5[QQ_KEY_LENGTH]; | 76 guint8 session_md5[QQ_KEY_LENGTH]; |
74 guint16 im_type; | 77 guint16 im_type; |
75 }; | 78 }; |
76 | 79 |
77 #define QQ_SEND_IM_AFTER_MSG_HEADER_LEN 8 | |
78 #define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5" | |
79 | |
80 guint8 *qq_get_send_im_tail(const gchar *font_color, | |
81 const gchar *font_size, | |
82 const gchar *font_name, | |
83 gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len) | |
84 { | |
85 gchar *s1; | |
86 unsigned char *rgb; | |
87 gint font_name_len; | |
88 guint8 *send_im_tail; | |
89 const guint8 simsun[] = { 0xcb, 0xce, 0xcc, 0xe5 }; | |
90 | |
91 if (font_name) { | |
92 font_name_len = strlen(font_name); | |
93 } else { | |
94 font_name_len = DEFAULT_FONT_NAME_LEN; | |
95 font_name = (const gchar *) simsun; | |
96 } | |
97 | |
98 send_im_tail = g_new0(guint8, tail_len); | |
99 | |
100 g_strlcpy((gchar *) (send_im_tail + QQ_SEND_IM_AFTER_MSG_HEADER_LEN), | |
101 font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN); | |
102 send_im_tail[tail_len - 1] = (guint8) tail_len; | |
103 | |
104 send_im_tail[0] = 0x00; | |
105 if (font_size) { | |
106 send_im_tail[1] = (guint8) (atoi(font_size) * 3 + 1); | |
107 } else { | |
108 send_im_tail[1] = 10; | |
109 } | |
110 if (is_bold) | |
111 send_im_tail[1] |= 0x20; | |
112 if (is_italic) | |
113 send_im_tail[1] |= 0x40; | |
114 if (is_underline) | |
115 send_im_tail[1] |= 0x80; | |
116 | |
117 if (font_color) { | |
118 s1 = g_strndup(font_color + 1, 6); | |
119 /* Henry: maybe this is a bug of purple, the string should have | |
120 * the length of odd number @_@ | |
121 * George Ang: This BUG maybe fixed by Purple. adding new byte | |
122 * would cause a crash. | |
123 */ | |
124 /* s2 = g_strdup_printf("%sH", s1); */ | |
125 rgb = purple_base16_decode(s1, NULL); | |
126 g_free(s1); | |
127 /* g_free(s2); */ | |
128 if (rgb) | |
129 { | |
130 memcpy(send_im_tail + 2, rgb, 3); | |
131 g_free(rgb); | |
132 } else { | |
133 send_im_tail[2] = send_im_tail[3] = send_im_tail[4] = 0; | |
134 } | |
135 } else { | |
136 send_im_tail[2] = send_im_tail[3] = send_im_tail[4] = 0; | |
137 } | |
138 | |
139 send_im_tail[5] = 0x00; | |
140 send_im_tail[6] = 0x86; | |
141 send_im_tail[7] = 0x22; /* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */ | |
142 /* qq_show_packet("QQ_MESG", send_im_tail, tail_len); */ | |
143 return (guint8 *) send_im_tail; | |
144 } | |
145 | |
146 /* read the common parts of the normal_im, | 80 /* read the common parts of the normal_im, |
147 * returns the bytes read if succeed, or -1 if there is any error */ | 81 * returns the bytes read if succeed, or -1 if there is any error */ |
148 static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len) | 82 static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len) |
149 { | 83 { |
150 gint bytes; | 84 gint bytes; |
157 bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes); | 91 bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes); |
158 bytes += qq_get16(&(im_header->im_type), data + bytes); | 92 bytes += qq_get16(&(im_header->im_type), data + bytes); |
159 return bytes; | 93 return bytes; |
160 } | 94 } |
161 | 95 |
162 void qq_got_attention(PurpleConnection *gc, const gchar *msg) | 96 typedef struct _qq_emoticon qq_emoticon; |
97 struct _qq_emoticon { | |
98 guint8 symbol; | |
99 gchar *name; | |
100 }; | |
101 | |
102 static gboolean emoticons_is_sorted = FALSE; | |
103 /* Map for purple smiley convert to qq, need qsort */ | |
104 static qq_emoticon emoticons_std[] = { | |
105 {0x4f, "/:)"}, {0x4f, "/wx"}, {0x4f, "/small_smile"}, | |
106 {0x42, "/:~"}, {0x42, "/pz"}, {0x42, "/curl_lip"}, | |
107 {0x43, "/:*"}, {0x43, "/se"}, {0x43, "/desire"}, | |
108 {0x44, "/:|"}, {0x44, "/fd"}, {0x44, "/dazed"}, | |
109 {0x45, "/8-)"}, {0x45, "/dy"}, {0x45, "/revel"}, | |
110 {0x46, "/:<"}, {0x46, "/ll"}, {0x46, "/cry"}, | |
111 {0x47, "/:$"}, {0x47, "/hx"}, {0x47, "/bashful"}, | |
112 {0x48, "/:x"}, {0x48, "/bz"}, {0x48, "/shut_mouth"}, | |
113 {0x8f, "/|-)"}, {0x8f, "/kun"}, {0x8f, "/sleepy"}, | |
114 {0x49, "/:z"}, {0x49, "/shui"}, {0x49, "/sleep"}, /* after sleepy */ | |
115 {0x4a, "/:'"}, {0x4a, "/dk"}, {0x4a, "/weep"}, | |
116 {0x4b, "/:-|"}, {0x4b, "/gg"}, {0x4b, "/embarassed"}, | |
117 {0x4c, "/:@"}, {0x4c, "/fn"}, {0x4c, "/pissed_off"}, | |
118 {0x4d, "/:P"}, {0x4d, "/tp"}, {0x4d, "/act_up"}, | |
119 {0x4e, "/:D"}, {0x4e, "/cy"}, {0x4e, "/toothy_smile"}, | |
120 {0x41, "/:O"}, {0x41, "/jy"}, {0x41, "/surprised"}, | |
121 {0x73, "/:("}, {0x73, "/ng"}, {0x73, "/sad"}, | |
122 {0x74, "/:+"}, {0x74, "/kuk"}, {0x74, "/cool"}, | |
123 {0xa1, "/--b"}, {0xa1, "/lengh"}, | |
124 {0x76, "/:Q"}, {0x76, "/zk"}, {0x76, "/crazy"}, | |
125 {0x8a, "/;P"}, {0x8a, "/tx"}, {0x8a, "/titter"}, | |
126 {0x8b, "/;-D"}, {0x8b, "/ka"}, {0x8b, "/cute"}, | |
127 {0x8c, "/;d"}, {0x8c, "/by"}, {0x8c, "/disdain"}, | |
128 {0x8d, "/;o"}, {0x8d, "/am"}, {0x8d, "/arrogant"}, | |
129 {0x8e, "/:g"}, {0x8e, "/jie"}, {0x8e, "/starving"}, | |
130 {0x78, "/:!"}, {0x78, "/jk"}, {0x78, "/terror"}, | |
131 {0x79, "/:L"}, {0x79, "/lh"}, {0x79, "/sweat"}, | |
132 {0x7a, "/:>"}, {0x7a, "/hanx"}, {0x7a, "/smirk"}, | |
133 {0x7b, "/:;"}, {0x7b, "/db"}, {0x7b, "/soldier"}, | |
134 {0x90, "/;f"}, {0x90, "/fendou"}, {0x90, "/struggle"}, | |
135 {0x91, "/:-S"}, {0x91, "/zhm"}, {0x91, "/curse"}, | |
136 {0x92, "/?"}, {0x92, "/yiw"}, {0x92, "/question"}, | |
137 {0x93, "/;x"}, {0x93, "/xu"}, {0x93, "/shh"}, | |
138 {0x94, "/;@"}, {0x94, "/yun"}, {0x94, "/dizzy"}, | |
139 {0x95, "/:8"}, {0x95, "/zhem"}, {0x95, "/excrutiating"}, | |
140 {0x96, "/;!"}, {0x96, "/shuai"}, {0x96, "/freaked_out"}, | |
141 {0x97, "/!!!"}, {0x97, "/kl"}, {0x97, "/skeleton"}, | |
142 {0x98, "/xx"}, {0x98, "/qiao"}, {0x98, "/hammer"}, | |
143 {0x99, "/bye"}, {0x99, "/zj"}, {0x99, "/bye"}, | |
144 {0xa2, "/wipe"}, {0xa2, "/ch"}, | |
145 {0xa3, "/dig"}, {0xa3, "/kb"}, | |
146 {0xa4, "/handclap"},{0xa4, "/gz"}, | |
147 {0xa5, "/&-("}, {0xa5, "/qd"}, | |
148 {0xa6, "/B-)"}, {0xa6, "/huaix"}, | |
149 {0xa7, "/<@"}, {0xa7, "/zhh"}, | |
150 {0xa8, "/@>"}, {0xa8, "/yhh"}, | |
151 {0xa9, "/:-O"}, {0xa9, "/hq"}, | |
152 {0xaa, "/>-|"}, {0xaa, "/bs"}, | |
153 {0xab, "/P-("}, {0xab, "/wq"}, | |
154 {0xac, "/:'|"}, {0xac, "/kk"}, | |
155 {0xad, "/X-)"}, {0xad, "/yx"}, | |
156 {0xae, "/:*"}, {0xae, "/qq"}, | |
157 {0xaf, "/@x"}, {0xaf, "/xia"}, | |
158 {0xb0, "/8*"}, {0xb0, "/kel"}, | |
159 {0xb1, "/pd"}, {0xb1, "/cd"}, | |
160 {0x61, "/<W>"}, {0x61, "/xig"}, {0x61, "/watermelon"}, | |
161 {0xb2, "/beer"}, {0xb2, "/pj"}, | |
162 {0xb3, "/basketb"}, {0xb3, "/lq"}, | |
163 {0xb4, "/oo"}, {0xb4, "/pp"}, | |
164 {0x80, "/coffee"}, {0x80, "/kf"}, | |
165 {0x81, "/eat"}, {0x81, "/fan"}, | |
166 {0x62, "/rose"}, {0x62, "/mg"}, | |
167 {0x63, "/fade"}, {0x63, "/dx"}, {0x63, "/wilt"}, | |
168 {0xb5, "/showlove"},{0xb5, "/sa"}, /* after sad */ | |
169 {0x65, "/heart"}, {0x65, "/xin"}, | |
170 {0x66, "/break"}, {0x66, "/xs"}, {0x66, "/broken_heart"}, | |
171 {0x67, "/cake"}, {0x67, "/dg"}, | |
172 {0x9c, "/li"}, {0x9c, "/shd"}, {0x9c, "/lightning"}, | |
173 {0x9d, "/bome"}, {0x9d, "/zhd"}, {0x9d, "/bomb"}, | |
174 {0x9e, "/kn"}, {0x9e, "/dao"}, {0x9e, "/knife"}, | |
175 {0x5e, "/footb"}, {0x5e, "/zq"}, {0x5e, "/soccer"}, | |
176 {0xb6, "/ladybug"}, {0xb6, "/pc"}, | |
177 {0x89, "/shit"}, {0x89, "/bb"}, | |
178 {0x6e, "/moon"}, {0x6e, "/yl"}, | |
179 {0x6b, "/sun"}, {0x6b, "/ty"}, | |
180 {0x68, "/gift"}, {0x68, "/lw"}, | |
181 {0x7f, "/hug"}, {0x7f, "/yb"}, | |
182 {0x6f, "/strong"}, {0x6f, "/qiang"}, {0x6f, "/thumbs_up"}, | |
183 {0x70, "/weak"}, {0x70, "/ruo"}, {0x70, "/thumbs_down"}, | |
184 {0x88, "/share"}, {0x88, "/ws"}, {0x88, "/handshake"}, | |
185 {0xb7, "/@)"}, {0xb7, "/bq"}, | |
186 {0xb8, "/jj"}, {0xb8, "/gy"}, | |
187 {0xb9, "/@@"}, {0xb9, "/qt"}, | |
188 {0xba, "/bad"}, {0xba, "/cj"}, | |
189 {0xbb, "/loveu"}, {0xbb, "/aini"}, | |
190 {0xbc, "/no"}, {0xbc, "/bu"}, | |
191 {0xbd, "/ok"}, {0xbd, "/hd"}, | |
192 {0x5c, "/love"}, {0x5c, "/aiq"}, /* after loveu */ | |
193 {0x56, "/<L>"}, {0x56, "/fw"}, {0x56, "/blow_kiss"}, | |
194 {0x58, "/jump"}, {0x58, "/tiao"}, | |
195 {0x5a, "/shake"}, {0x5a, "/fad"}, /* after fade */ | |
196 {0x5b, "/<O>"}, {0x5b, "/oh"}, {0x5b, "/angry"}, | |
197 {0xbe, "/circle"}, {0xbe, "/zhq"}, | |
198 {0xbf, "/kotow"}, {0xbf, "/kt"}, | |
199 {0xc0, "/turn"}, {0xc0, "/ht"}, | |
200 {0x77, "/:t"}, {0x77, "/tu"}, {0x77, "/vomit"}, /* after turn */ | |
201 {0xa0, "/victory"}, {0xa0, "/shl"}, {0xa0, "/v"}, /* end of v */ | |
202 {0xc1, "/skip"}, {0xc1, "/tsh"}, | |
203 {0xc2, "/oY"}, {0xc2, "/hsh"}, | |
204 {0xc3, "/#-O"}, {0xc3, "/jd"}, | |
205 {0xc4, "/hiphop"}, {0xc4, "/jw"}, | |
206 {0xc5, "/kiss"}, {0xc5, "/xw"}, | |
207 {0xc6, "/<&"}, {0xc6, "/ztj"}, | |
208 {0x7c, "/pig"}, {0x7c, "/zt"}, /* after ztj */ | |
209 {0xc7, "/&>"}, {0xc7, "/ytj"}, /* must be end of "&" */ | |
210 {0x75, "/:#"}, {0x75, "/feid"}, {0x75, "/SARS"}, | |
211 {0x59, "/go"}, {0x59, "/shan"}, | |
212 {0x57, "/find"}, {0x57, "/zhao"}, {0x57, "/search"}, | |
213 {0x55, "/&"}, {0x55, "/mm"}, {0x55, "/beautiful_eyebrows"}, | |
214 {0x7d, "/cat"}, {0x7d, "/maom"}, | |
215 {0x7e, "/dog"}, {0x7e, "/xg"}, | |
216 {0x9a, "/$"}, {0x9a, "/qianc"}, {0x9a, "/money"}, | |
217 {0x9b, "/(!)"}, {0x9b, "/dp"}, {0x9b, "/lightbulb"}, | |
218 {0x60, "/cup"}, {0x60, "/bei"}, | |
219 {0x9f, "/music"}, {0x9f, "/yy"}, | |
220 {0x82, "/pill"}, {0x82, "/yw"}, | |
221 {0x64, "/kiss"}, {0x64, "/wen"}, | |
222 {0x83, "/meeting"}, {0x83, "/hy"}, | |
223 {0x84, "/phone"}, {0x84, "/dh"}, | |
224 {0x85, "/time"}, {0x85, "/sj"}, | |
225 {0x86, "/email"}, {0x86, "/yj"}, | |
226 {0x87, "/tv"}, {0x87, "/ds"}, | |
227 {0x50, "/<D>"}, {0x50, "/dd"}, | |
228 {0x51, "/<J>"}, {0x51, "/mn"}, {0x51, "/beauty"}, | |
229 {0x52, "/<H>"}, {0x52, "/hl"}, | |
230 {0x53, "/<M>"}, {0x53, "/mamao"}, | |
231 {0x54, "/<QQ>"}, {0x54, "/qz"}, {0x54, "/qq"}, | |
232 {0x5d, "/<B>"}, {0x5d, "/bj"}, {0x5d, "/baijiu"}, | |
233 {0x5f, "/<U>"}, {0x5f, "/qsh"}, {0x5f, "/soda"}, | |
234 {0x69, "/<!!>"}, {0x69, "/xy"}, {0x69, "/rain"}, | |
235 {0x6a, "/<~>"}, {0x6a, "/duoy"}, {0x6a, "/cloudy"}, | |
236 {0x6c, "/<Z>"}, {0x6c, "/xr"}, {0x6c, "/snowman"}, | |
237 {0x6d, "/<*>"}, {0x6d, "/xixing"}, {0x6d, "/star"}, /* after starving */ | |
238 {0x71, "/<00>"}, {0x71, "/nv"}, {0x71, "/woman"}, | |
239 {0x72, "/<11>"}, {0x72, "/nan"}, {0x72, "/man"}, | |
240 {0, NULL} | |
241 }; | |
242 gint emoticons_std_num = sizeof(emoticons_std) / sizeof(qq_emoticon) - 1; | |
243 | |
244 /* Map for purple smiley convert to qq, need qsort */ | |
245 static qq_emoticon emoticons_ext[] = { | |
246 {0xc7, "/&>"}, {0xa5, "/&-("}, | |
247 {0xbb, "/loveu"}, | |
248 {0x63, "/fade"}, | |
249 {0x8f, "/sleepy"}, {0x73, "/sad"}, {0x8e, "/starving"}, | |
250 {0xc0, "/turn"}, | |
251 {0xa0, "/victory"}, {0x77, "/vomit"}, | |
252 {0xc6, "/ztj"}, | |
253 {0, NULL} | |
254 }; | |
255 gint emoticons_ext_num = sizeof(emoticons_ext) / sizeof(qq_emoticon) - 1; | |
256 | |
257 /* Map for qq smiley convert to purple */ | |
258 static qq_emoticon emoticons_sym[] = { | |
259 {0x41, "/jy"}, | |
260 {0x42, "/pz"}, | |
261 {0x43, "/se"}, | |
262 {0x44, "/fd"}, | |
263 {0x45, "/dy"}, | |
264 {0x46, "/ll"}, | |
265 {0x47, "/hx"}, | |
266 {0x48, "/bz"}, | |
267 {0x49, "/shui"}, | |
268 {0x4a, "/dk"}, | |
269 {0x4b, "/gg"}, | |
270 {0x4c, "/fn"}, | |
271 {0x4d, "/tp"}, | |
272 {0x4e, "/cy"}, | |
273 {0x4f, "/wx"}, | |
274 {0x50, "/dd"}, | |
275 {0x51, "/mn"}, | |
276 {0x52, "/hl"}, | |
277 {0x53, "/mamao"}, | |
278 {0x54, "/qz"}, | |
279 {0x55, "/mm"}, | |
280 {0x56, "/fw"}, | |
281 {0x57, "/zhao"}, | |
282 {0x58, "/tiao"}, | |
283 {0x59, "/shan"}, | |
284 {0x5a, "/fad"}, | |
285 {0x5b, "/oh"}, | |
286 {0x5c, "/aiq"}, | |
287 {0x5d, "/bj"}, | |
288 {0x5e, "/zq"}, | |
289 {0x5f, "/qsh"}, | |
290 {0x60, "/bei"}, | |
291 {0x61, "/xig"}, | |
292 {0x62, "/mg"}, | |
293 {0x63, "/dx"}, | |
294 {0x64, "/wen"}, | |
295 {0x65, "/xin"}, | |
296 {0x66, "/xs"}, | |
297 {0x67, "/dg"}, | |
298 {0x68, "/lw"}, | |
299 {0x6a, "/duoy"}, | |
300 {0x6b, "/ty"}, | |
301 {0x6c, "/xr"}, | |
302 {0x6d, "/xixing"}, | |
303 {0x6e, "/yl"}, | |
304 {0x6f, "/qiang"}, | |
305 {0x70, "/ruo"}, | |
306 {0x71, "/nv"}, | |
307 {0x72, "/nan"}, | |
308 {0x73, "/ng"}, | |
309 {0x74, "/kuk"}, | |
310 {0x75, "/feid"}, | |
311 {0x76, "/zk"}, | |
312 {0x77, "/tu"}, | |
313 {0x78, "/jk"}, | |
314 {0x79, "/lh"}, | |
315 {0x7a, "/hanx"}, | |
316 {0x7b, "/db"}, | |
317 {0x7c, "/zt"}, | |
318 {0x7d, "/maom"}, | |
319 {0x7e, "/xg"}, | |
320 {0x7f, "/yb"}, | |
321 {0x80, "/coffee"}, | |
322 {0x81, "/fan"}, | |
323 {0x82, "/yw"}, | |
324 {0x83, "/hy"}, | |
325 {0x84, "/dh"}, | |
326 {0x85, "/sj"}, | |
327 {0x86, "/yj"}, | |
328 {0x87, "/ds"}, | |
329 {0x69, "/xy"}, | |
330 {0x88, "/ws"}, | |
331 {0x89, "/bb"}, | |
332 {0x8a, "/tx"}, | |
333 {0x8b, "/ka"}, | |
334 {0x8c, "/by"}, | |
335 {0x8d, "/am"}, | |
336 {0x8e, "/jie"}, | |
337 {0x8f, "/kun"}, | |
338 {0x90, "/fendou"}, | |
339 {0x91, "/zhm"}, | |
340 {0x92, "/yiw"}, | |
341 {0x93, "/xu"}, | |
342 {0x94, "/yun"}, | |
343 {0x95, "/zhem"}, | |
344 {0x96, "/shuai"}, | |
345 {0x97, "/kl"}, | |
346 {0x98, "/qiao"}, | |
347 {0x99, "/zj"}, | |
348 {0x9a, "/qianc"}, | |
349 {0x9b, "/dp"}, | |
350 {0x9c, "/shd"}, | |
351 {0x9d, "/zhd"}, | |
352 {0x9e, "/dao"}, | |
353 {0x9f, "/yy"}, | |
354 {0xa0, "/shl"}, | |
355 {0xa1, "/:L"}, | |
356 {0xa2, "/ch"}, | |
357 {0xa3, "/kb"}, | |
358 {0xa4, "/gz"}, | |
359 {0xa5, "/qd"}, | |
360 {0xa6, "/huaix"}, | |
361 {0xa7, "/zhh"}, | |
362 {0xa8, "/yhh"}, | |
363 {0xa9, "/hq"}, | |
364 {0xaa, "/bs"}, | |
365 {0xab, "/wq"}, | |
366 {0xac, "/kk"}, | |
367 {0xad, "/yx"}, | |
368 {0xae, "/qq"}, | |
369 {0xaf, "/xia"}, | |
370 {0xb0, "/kel"}, | |
371 {0xb1, "/cd"}, | |
372 {0xb2, "/pj"}, | |
373 {0xb3, "/lq"}, | |
374 {0xb4, "/pp"}, | |
375 {0xb5, "/sa"}, | |
376 {0xb6, "/pc"}, | |
377 {0xb7, "/bq"}, | |
378 {0xb8, "/gy"}, | |
379 {0xb9, "/qt"}, | |
380 {0xba, "/cj"}, | |
381 {0xbb, "/aini"}, | |
382 {0xbc, "/bu"}, | |
383 {0xbd, "/hd"}, | |
384 {0xbe, "/zhq"}, | |
385 {0xbf, "/kt"}, | |
386 {0xc0, "/ht"}, | |
387 {0xc1, "/tsh"}, | |
388 {0xc2, "/hsh"}, | |
389 {0xc3, "/jd"}, | |
390 {0xc4, "/jw"}, | |
391 {0xc5, "/xw"}, | |
392 {0xc6, "/ztj"}, | |
393 {0xc7, "/ytj"}, | |
394 {0, NULL} | |
395 }; | |
396 gint emoticons_sym_num = sizeof(emoticons_sym) / sizeof(qq_emoticon) - 1;; | |
397 | |
398 static int emoticon_cmp(const void *k1, const void *k2) | |
399 { | |
400 const qq_emoticon *e1 = (const qq_emoticon *) k1; | |
401 const qq_emoticon *e2 = (const qq_emoticon *) k2; | |
402 if (e1->symbol == 0) { | |
403 /* purple_debug_info("QQ", "emoticon_cmp len %d\n", strlen(e2->name)); */ | |
404 return strncmp(e1->name, e2->name, strlen(e2->name)); | |
405 } | |
406 if (e2->symbol == 0) { | |
407 /* purple_debug_info("QQ", "emoticon_cmp len %d\n", strlen(e1->name)); */ | |
408 return strncmp(e1->name, e2->name, strlen(e1->name)); | |
409 } | |
410 return strcmp(e1->name, e2->name); | |
411 } | |
412 | |
413 static void emoticon_try_sort() | |
414 { | |
415 if (emoticons_is_sorted) | |
416 return; | |
417 | |
418 purple_debug_info("QQ", "qsort stand emoticons\n"); | |
419 qsort(emoticons_std, emoticons_std_num, sizeof(qq_emoticon), emoticon_cmp); | |
420 purple_debug_info("QQ", "qsort extend emoticons\n"); | |
421 qsort(emoticons_ext, emoticons_ext_num, sizeof(qq_emoticon), emoticon_cmp); | |
422 } | |
423 | |
424 static qq_emoticon *emoticon_find(gchar *name) | |
425 { | |
426 qq_emoticon *ret = NULL; | |
427 qq_emoticon key; | |
428 | |
429 g_return_val_if_fail(name != NULL, NULL); | |
430 emoticon_try_sort(); | |
431 | |
432 key.name = name; | |
433 key.symbol = 0; | |
434 | |
435 /* purple_debug_info("QQ", "bsearch emoticon %.20s\n", name); */ | |
436 ret = (qq_emoticon *)bsearch(&key, emoticons_ext, emoticons_ext_num, | |
437 sizeof(qq_emoticon), emoticon_cmp); | |
438 if (ret != NULL) { | |
439 return ret; | |
440 } | |
441 ret = (qq_emoticon *)bsearch(&key, emoticons_std, emoticons_std_num, | |
442 sizeof(qq_emoticon), emoticon_cmp); | |
443 return ret; | |
444 } | |
445 | |
446 static gchar *emoticon_get(guint8 symbol) | |
447 { | |
448 g_return_val_if_fail(symbol >= emoticons_sym[0].symbol, NULL); | |
449 g_return_val_if_fail(symbol <= emoticons_sym[emoticons_sym_num - 2].symbol, NULL); | |
450 | |
451 return emoticons_sym[symbol - emoticons_sym[0].symbol].name; | |
452 } | |
453 | |
454 /* convert qq emote icon to purple sytle | |
455 Notice: text is in qq charset, GB18030 or utf8 */ | |
456 gchar *qq_emoticon_to_purple(gchar *text) | |
457 { | |
458 gchar *ret; | |
459 GString *converted; | |
460 gchar **segments; | |
461 gboolean have_smiley; | |
462 gchar *purple_smiley; | |
463 gchar *cur; | |
464 guint8 symbol; | |
465 | |
466 segments = g_strsplit_set(text, "\x14\x15", 0); | |
467 if(segments == NULL) { | |
468 return g_strdup(""); | |
469 } | |
470 | |
471 converted = g_string_new(""); | |
472 have_smiley = FALSE; | |
473 g_string_append(converted, segments[0]); | |
474 while ((*(++segments)) != NULL) { | |
475 have_smiley = TRUE; | |
476 | |
477 cur = *segments; | |
478 symbol = (guint8)cur[0]; | |
479 | |
480 purple_smiley = emoticon_get(symbol); | |
481 if (purple_smiley == NULL) { | |
482 purple_debug_info("QQ", "Not found smiley of 0x%02X\n", symbol); | |
483 g_string_append(converted, "<IMG ID=\"0\">"); | |
484 } else { | |
485 purple_debug_info("QQ", "Found 0x%02X smiley is %s\n", symbol, purple_smiley); | |
486 g_string_append(converted, purple_smiley); | |
487 g_string_append(converted, cur + 1); | |
488 } | |
489 } | |
490 | |
491 if (!have_smiley) { | |
492 g_string_prepend(converted, "<font sml=\"none\">"); | |
493 g_string_append(converted, "</font>"); | |
494 } | |
495 ret = converted->str; | |
496 g_string_free(converted, FALSE); | |
497 return ret; | |
498 } | |
499 | |
500 void qq_im_fmt_free(qq_im_format *fmt) | |
501 { | |
502 g_return_if_fail(fmt != NULL); | |
503 if (fmt->font) g_free(fmt->font); | |
504 g_free(fmt); | |
505 } | |
506 | |
507 qq_im_format *qq_im_fmt_new_by_purple(const gchar *msg) | |
508 { | |
509 qq_im_format *fmt; | |
510 const gchar *start, *end, *last; | |
511 GData *attribs; | |
512 gchar *tmp; | |
513 unsigned char *rgb; | |
514 const gchar simsun[] = { 0xcb, 0xce, 0xcc, 0xe5, 0}; /* simsun in Chinese */ | |
515 | |
516 g_return_val_if_fail(msg != NULL, NULL); | |
517 | |
518 fmt = g_new0(qq_im_format, 1); | |
519 memset(fmt, 0, sizeof(qq_im_format)); | |
520 fmt->font_len = strlen(simsun); | |
521 fmt->font = g_strdup(simsun); | |
522 fmt->attr = 10; | |
523 | |
524 last = msg; | |
525 while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { | |
526 tmp = g_datalist_get_data(&attribs, "face"); | |
527 if (tmp && strlen(tmp) > 0) { | |
528 if (fmt->font) g_free(fmt->font); | |
529 fmt->font_len = strlen(tmp); | |
530 fmt->font = g_strdup(tmp); | |
531 } | |
532 | |
533 tmp = g_datalist_get_data(&attribs, "size"); | |
534 if (tmp) { | |
535 fmt->attr = atoi(tmp) * 3 + 1; | |
536 fmt->attr &= 0x0f; | |
537 } | |
538 | |
539 tmp = g_datalist_get_data(&attribs, "color"); | |
540 if (tmp && strlen(tmp) > 1) { | |
541 rgb = purple_base16_decode(tmp + 1, NULL); | |
542 g_memmove(fmt->rgb, rgb, 3); | |
543 g_free(rgb); | |
544 } | |
545 | |
546 g_datalist_clear(&attribs); | |
547 last = end + 1; | |
548 } | |
549 | |
550 if (purple_markup_find_tag("b", msg, &start, &end, &attribs)) { | |
551 fmt->attr |= 0x20; | |
552 g_datalist_clear(&attribs); | |
553 } | |
554 | |
555 if (purple_markup_find_tag("i", msg, &start, &end, &attribs)) { | |
556 fmt->attr |= 0x40; | |
557 g_datalist_clear(&attribs); | |
558 } | |
559 | |
560 if (purple_markup_find_tag("u", msg, &start, &end, &attribs)) { | |
561 fmt->attr |= 0x80; | |
562 g_datalist_clear(&attribs); | |
563 } | |
564 | |
565 return fmt; | |
566 } | |
567 | |
568 static qq_im_format *im_format_new_by_qq(guint8 *data, gint len) | |
569 { | |
570 qq_im_format *fmt; | |
571 gint bytes; | |
572 guint16 charset; | |
573 | |
574 fmt = g_new0(qq_im_format, 1); | |
575 memset(fmt, 0, sizeof(qq_im_format)); | |
576 fmt->attr = 10; | |
577 | |
578 g_return_val_if_fail(data != NULL && len > 0, fmt); | |
579 | |
580 /* qq_show_packet("IM format", data, len); */ | |
581 bytes = 0; | |
582 bytes += qq_get8(&fmt->attr, data + bytes); | |
583 bytes += qq_getdata(fmt->rgb, sizeof(fmt->rgb), data + bytes); /* red,green,blue */ | |
584 bytes += 1; /* skip 0x00 */ | |
585 bytes += qq_get16(&charset, data + bytes); | |
586 | |
587 g_return_val_if_fail(len - bytes > 0, fmt); | |
588 fmt->font_len = len - bytes; | |
589 fmt->font = g_strndup((gchar *)data + bytes, fmt->font_len); | |
590 return fmt; | |
591 } | |
592 | |
593 gint qq_put_im_tail(guint8 *buf, qq_im_format *fmt) | |
594 { | |
595 gint bytes; | |
596 | |
597 g_return_val_if_fail(buf != NULL && fmt != NULL, 0); | |
598 | |
599 bytes = 0; | |
600 bytes += qq_put8(buf + bytes, fmt->attr); | |
601 bytes += qq_putdata(buf + bytes, fmt->rgb, sizeof(fmt->rgb)); | |
602 bytes += qq_put8(buf + bytes, 0); | |
603 /* encoding, 0x8602=GB, 0x0000=EN, define BIG5 support here */ | |
604 bytes += qq_put16(buf + bytes, 0x8602); | |
605 if (fmt->font != NULL && fmt->font_len > 0) { | |
606 bytes += qq_putdata(buf + bytes, (guint8 *)fmt->font, fmt->font_len); | |
607 } else { | |
608 purple_debug_warning("QQ", "Font name is empty\n"); | |
609 } | |
610 bytes += qq_put8(buf + bytes, 0x0d); | |
611 /* qq_show_packet("IM tail", buf, bytes); */ | |
612 return bytes; | |
613 } | |
614 | |
615 /* convert qq format to purple | |
616 Notice: text is in qq charset, GB18030 or utf8 */ | |
617 gchar *qq_format_to_purple(guint8 *data, gint len, gchar *text) | |
618 { | |
619 qq_im_format *fmt; | |
620 GString *converted; | |
621 gchar *ret; | |
622 gint size; | |
623 | |
624 fmt = im_format_new_by_qq(data, len); | |
625 | |
626 converted = g_string_new(text); | |
627 | |
628 g_string_append_printf(converted, "<font color=\"#%02x%02x%02x\">", | |
629 fmt->rgb[0], fmt->rgb[1], fmt->rgb[2]); | |
630 g_string_append(converted, "</font>"); | |
631 | |
632 if (fmt->font != NULL) { | |
633 g_string_append_printf(converted, "<font face=\"%s\"", fmt->font); | |
634 g_string_append(converted, "</font>"); | |
635 } | |
636 size = (fmt->attr & 0x1f) / 3; | |
637 if (size >= 0) { | |
638 g_string_append_printf(converted, "<font size=\"%s\"", fmt->font); | |
639 g_string_append(converted, "</font>"); | |
640 } | |
641 if (fmt->attr & 0x20) { | |
642 /* bold */ | |
643 g_string_prepend(converted, "<b>"); | |
644 g_string_append(converted, "</b>"); | |
645 } | |
646 if (fmt->attr & 0x40) { | |
647 /* italic */ | |
648 g_string_prepend(converted, "<i>"); | |
649 g_string_append(converted, "</i>"); | |
650 } | |
651 if (fmt->attr & 0x80) { | |
652 /* underline */ | |
653 g_string_prepend(converted, "<u>"); | |
654 g_string_append(converted, "</u>"); | |
655 } | |
656 | |
657 qq_im_fmt_free(fmt); | |
658 ret = converted->str; | |
659 g_string_free(converted, FALSE); | |
660 return ret; | |
661 } | |
662 | |
663 void qq_got_message(PurpleConnection *gc, const gchar *msg) | |
163 { | 664 { |
164 qq_data *qd; | 665 qq_data *qd; |
165 gchar *from; | 666 gchar *from; |
166 time_t now = time(NULL); | 667 time_t now = time(NULL); |
167 | 668 |
178 } | 679 } |
179 | 680 |
180 /* process received normal text IM */ | 681 /* process received normal text IM */ |
181 static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) | 682 static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) |
182 { | 683 { |
684 qq_data *qd; | |
183 guint16 purple_msg_type; | 685 guint16 purple_msg_type; |
184 gchar *who; | 686 gchar *who; |
185 gchar *msg_with_purple_smiley; | 687 gchar *msg_smiley, *msg_fmt, *msg_utf8; |
186 gchar *msg_utf8_encoded; | 688 gint bytes; |
187 qq_data *qd; | 689 PurpleBuddy *buddy; |
188 gint bytes = 0; | |
189 PurpleBuddy *b; | |
190 qq_buddy_data *bd; | 690 qq_buddy_data *bd; |
191 | 691 |
192 struct { | 692 struct { |
193 /* now comes the part for text only */ | 693 /* now comes the part for text only */ |
194 guint16 msg_seq; | 694 guint16 msg_seq; |
195 guint32 send_time; | 695 guint32 send_time; |
196 guint16 sender_icon; | 696 guint16 sender_icon; |
197 guint8 unknown2[3]; | 697 guint8 unknown2[3]; |
198 guint8 is_there_font_attr; | 698 guint8 has_font_attr; |
199 guint8 unknown3[4]; | 699 guint8 fragment_count; |
700 guint8 fragment_index; | |
701 guint16 msg_id; | |
200 guint8 msg_type; | 702 guint8 msg_type; |
201 gchar *msg; /* no fixed length, ends with 0x00 */ | 703 gchar *msg; /* no fixed length, ends with 0x00 */ |
202 guint8 *font_attr; | 704 guint8 *font_attr; |
203 gint font_attr_len; | 705 gint font_attr_len; |
204 } im_text; | 706 } im_text; |
207 g_return_if_fail(im_header != NULL); | 709 g_return_if_fail(im_header != NULL); |
208 | 710 |
209 qd = (qq_data *) gc->proto_data; | 711 qd = (qq_data *) gc->proto_data; |
210 memset(&im_text, 0, sizeof(im_text)); | 712 memset(&im_text, 0, sizeof(im_text)); |
211 | 713 |
212 /* push data into im_text */ | 714 /* qq_show_packet("IM text", data, len); */ |
715 bytes = 0; | |
213 bytes += qq_get16(&(im_text.msg_seq), data + bytes); | 716 bytes += qq_get16(&(im_text.msg_seq), data + bytes); |
214 bytes += qq_get32(&(im_text.send_time), data + bytes); | 717 bytes += qq_get32(&(im_text.send_time), data + bytes); |
215 bytes += qq_get16(&(im_text.sender_icon), data + bytes); | 718 bytes += qq_get16(&(im_text.sender_icon), data + bytes); |
216 bytes += qq_getdata((guint8 *) & (im_text.unknown2), 3, data + bytes); | 719 bytes += qq_getdata(im_text.unknown2, sizeof(im_text.unknown2), data + bytes); /* 0x(00 00 00)*/ |
217 bytes += qq_get8(&(im_text.is_there_font_attr), data + bytes); | 720 bytes += qq_get8(&(im_text.has_font_attr), data + bytes); |
218 /** | 721 bytes += qq_get8(&(im_text.fragment_count), data + bytes); |
219 * from lumaqq for unknown3 | 722 bytes += qq_get8(&(im_text.fragment_index), data + bytes); |
220 * totalFragments = buf.get() & 255; | 723 bytes += qq_get16(&(im_text.msg_id), data + bytes); |
221 * fragmentSequence = buf.get() & 255; | |
222 * messageId = buf.getChar(); | |
223 */ | |
224 bytes += qq_getdata((guint8 *) & (im_text.unknown3), 4, data + bytes); | |
225 bytes += qq_get8(&(im_text.msg_type), data + bytes); | 724 bytes += qq_get8(&(im_text.msg_type), data + bytes); |
226 | 725 purple_debug_info("QQ", "IM, font attr %d, fragment %d-%d, id %u, type %d\n", |
227 /* we need to check if this is auto-reply | 726 im_text.has_font_attr, im_text.fragment_count, im_text.fragment_index, |
228 * QQ2003iii build 0304, returns the msg without font_attr | 727 im_text.msg_id, im_text.msg_type); |
229 * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */ | 728 |
230 if (im_text.msg_type == QQ_IM_AUTO_REPLY) { | 729 if (im_text.has_font_attr) { |
231 im_text.is_there_font_attr = 0x00; /* indeed there is no this flag */ | 730 im_text.msg = g_strdup((gchar *)(data + bytes)); |
731 bytes += strlen(im_text.msg) + 1; /* length decided by strlen! will it cause a crash? */ | |
732 im_text.font_attr_len = len - bytes; | |
733 im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len); | |
734 qq_show_packet("IM tail", im_text.font_attr, im_text.font_attr_len); | |
735 } else { | |
232 im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); | 736 im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); |
233 } else { /* it is normal mesasge */ | 737 } |
234 if (im_text.is_there_font_attr) { | 738 qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg) ); |
235 im_text.msg = g_strdup((gchar *)(data + bytes)); | |
236 bytes += strlen(im_text.msg) + 1; /* length decided by strlen! will it cause a crash? */ | |
237 im_text.font_attr_len = len - bytes; | |
238 im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len); | |
239 } else /* not im_text.is_there_font_attr */ | |
240 im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); | |
241 } /* if im_text.msg_type */ | |
242 | |
243 who = uid_to_purple_name(im_header->uid_from); | 739 who = uid_to_purple_name(im_header->uid_from); |
244 b = purple_find_buddy(gc->account, who); | 740 buddy = purple_find_buddy(gc->account, who); |
245 if (b == NULL) { | 741 if (buddy == NULL) { |
246 /* create no-auth buddy */ | 742 /* create no-auth buddy */ |
247 b = qq_buddy_new(gc, im_header->uid_from); | 743 buddy = qq_buddy_new(gc, im_header->uid_from); |
248 } | 744 } |
249 bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data; | 745 bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data; |
250 if (bd != NULL) { | 746 if (bd != NULL) { |
251 bd->client_tag = im_header->version_from; | 747 bd->client_tag = im_header->version_from; |
252 } | 748 bd->face = im_text.sender_icon; |
253 | 749 qq_update_buddy_icon(gc->account, who, bd->face); |
254 purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0; | 750 } |
255 | 751 |
256 msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg); | 752 purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) |
257 msg_utf8_encoded = im_text.is_there_font_attr ? | 753 ? PURPLE_MESSAGE_AUTO_RESP : 0; |
258 qq_encode_to_purple(im_text.font_attr, | 754 |
259 im_text.font_attr_len, | 755 msg_smiley = qq_emoticon_to_purple(im_text.msg); |
260 msg_with_purple_smiley, qd->client_version) | 756 if (im_text.font_attr != NULL) { |
261 : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); | 757 msg_fmt = qq_format_to_purple(im_text.font_attr, im_text.font_attr_len, |
758 msg_smiley); | |
759 msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); | |
760 g_free(msg_fmt); | |
761 } else { | |
762 msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); | |
763 } | |
764 g_free(msg_smiley); | |
262 | 765 |
263 /* send encoded to purple, note that we use im_text.send_time, | 766 /* send encoded to purple, note that we use im_text.send_time, |
264 * not the time we receive the message | 767 * not the time we receive the message |
265 * as it may have been delayed when I am not online. */ | 768 * as it may have been delayed when I am not online. */ |
266 serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time); | 769 purple_debug_info("QQ", "IM from %u: %s\n", im_header->uid_from,msg_utf8); |
267 | 770 serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time); |
268 g_free(msg_utf8_encoded); | 771 |
269 g_free(msg_with_purple_smiley); | 772 g_free(msg_utf8); |
270 g_free(who); | 773 g_free(who); |
271 g_free(im_text.msg); | 774 g_free(im_text.msg); |
272 if (im_text.font_attr) g_free(im_text.font_attr); | 775 if (im_text.font_attr) g_free(im_text.font_attr); |
273 } | 776 } |
274 | 777 |
275 /* process received extended (2007) text IM */ | 778 /* process received extended (2007) text IM */ |
276 static void process_extend_im_text( | 779 static void process_extend_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) |
277 PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) | 780 { |
278 { | 781 qq_data *qd; |
279 guint16 purple_msg_type; | 782 guint16 purple_msg_type; |
280 gchar *who; | 783 gchar *who; |
281 gchar *msg_with_purple_smiley; | 784 gchar *msg_smiley, *msg_fmt, *msg_utf8; |
282 gchar *msg_utf8_encoded; | 785 PurpleBuddy *buddy; |
283 qq_data *qd; | |
284 PurpleBuddy *b; | |
285 qq_buddy_data *bd; | 786 qq_buddy_data *bd; |
286 gint bytes, text_len; | 787 gint bytes, text_len; |
287 | 788 |
288 struct { | 789 struct { |
289 /* now comes the part for text only */ | 790 /* now comes the part for text only */ |
290 guint16 sessionId; | 791 guint16 msg_seq; |
291 guint32 send_time; | 792 guint32 send_time; |
292 guint16 senderHead; | 793 guint16 sender_icon; |
293 guint32 flag; | 794 guint32 has_font_attr; |
294 guint8 unknown2[8]; | 795 guint8 unknown2[8]; |
295 guint8 fragmentCount; | 796 guint8 fragment_count; |
296 guint8 fragmentIndex; | 797 guint8 fragment_index; |
297 guint16 messageId; | 798 guint16 msg_id; |
298 guint8 replyType; | 799 guint8 msg_type; |
299 gchar *msg; /* no fixed length, ends with 0x00 */ | 800 gchar *msg; /* no fixed length, ends with 0x00 */ |
300 guint8 fromMobileQQ; | 801 guint8 fromMobileQQ; |
301 | 802 |
302 guint8 is_there_font_attr; | |
303 guint8 *font_attr; | 803 guint8 *font_attr; |
304 gint8 font_attr_len; | 804 gint8 font_attr_len; |
305 } im_text; | 805 } im_text; |
306 | 806 |
307 g_return_if_fail (data != NULL && len > 0); | 807 g_return_if_fail (data != NULL && len > 0); |
308 g_return_if_fail(im_header != NULL); | 808 g_return_if_fail(im_header != NULL); |
309 | 809 |
310 qd = (qq_data *) gc->proto_data; | 810 qd = (qq_data *) gc->proto_data; |
311 memset(&im_text, 0, sizeof(im_text)); | 811 memset(&im_text, 0, sizeof(im_text)); |
312 | 812 |
313 /* push data into im_text */ | 813 /* qq_show_packet("Extend IM text", data, len); */ |
314 bytes = 0; | 814 bytes = 0; |
315 bytes += qq_get16(&(im_text.sessionId), data + bytes); | 815 bytes += qq_get16(&(im_text.msg_seq), data + bytes); |
316 bytes += qq_get32(&(im_text.send_time), data + bytes); | 816 bytes += qq_get32(&(im_text.send_time), data + bytes); |
317 bytes += qq_get16(&(im_text.senderHead), data + bytes); | 817 bytes += qq_get16(&(im_text.sender_icon), data + bytes); |
318 bytes += qq_get32(&(im_text.flag), data + bytes); | 818 bytes += qq_get32(&(im_text.has_font_attr), data + bytes); |
319 | 819 |
320 bytes += qq_getdata(im_text.unknown2, 8, data + bytes); | 820 bytes += qq_getdata(im_text.unknown2, sizeof(im_text.unknown2), data + bytes); |
321 bytes += qq_get8(&(im_text.fragmentCount), data + bytes); | 821 bytes += qq_get8(&(im_text.fragment_count), data + bytes); |
322 bytes += qq_get8(&(im_text.fragmentIndex), data + bytes); | 822 bytes += qq_get8(&(im_text.fragment_index), data + bytes); |
323 | 823 |
324 bytes += qq_get16(&(im_text.messageId), data + bytes); | 824 bytes += qq_get16(&(im_text.msg_id), data + bytes); |
325 bytes += qq_get8(&(im_text.replyType), data + bytes); | 825 bytes += qq_get8(&(im_text.msg_type), data + bytes); |
826 | |
827 purple_debug_info("QQ", "IM, font attr %d, fragment %d-%d, id %u, type %d\n", | |
828 im_text.has_font_attr, im_text.fragment_count, im_text.fragment_index, | |
829 im_text.msg_id, im_text.msg_type); | |
326 | 830 |
327 im_text.font_attr_len = data[len-1] & 0xff; | 831 im_text.font_attr_len = data[len-1] & 0xff; |
328 | 832 |
329 text_len = len - bytes - im_text.font_attr_len; | 833 text_len = len - bytes - im_text.font_attr_len; |
330 im_text.msg = g_strndup((gchar *)(data + bytes), text_len); | 834 im_text.msg = g_strndup((gchar *)(data + bytes), text_len); |
336 purple_debug_error("QQ", "Failed to get IM's font attribute len %d\n", | 840 purple_debug_error("QQ", "Failed to get IM's font attribute len %d\n", |
337 im_text.font_attr_len); | 841 im_text.font_attr_len); |
338 return; | 842 return; |
339 } | 843 } |
340 | 844 |
341 if(im_text.fragmentCount == 0) | 845 if(im_text.fragment_count == 0) im_text.fragment_count = 1; |
342 im_text.fragmentCount = 1; | |
343 | 846 |
344 // Filter tail space | 847 // Filter tail space |
345 if(im_text.fragmentIndex == im_text.fragmentCount -1) | 848 if(im_text.fragment_index == im_text.fragment_count -1) |
346 { | 849 { |
347 gint real_len = text_len; | 850 gint real_len = text_len; |
348 while(real_len > 0 && im_text.msg[real_len - 1] == 0x20) | 851 while(real_len > 0 && im_text.msg[real_len - 1] == 0x20) |
349 real_len --; | 852 real_len --; |
350 | 853 |
351 text_len = real_len; | 854 text_len = real_len; |
352 // Null string instaed of space | 855 // Null string instead of space |
353 im_text.msg[text_len] = 0; | 856 im_text.msg[text_len] = 0; |
354 } | 857 } |
355 | 858 |
356 who = uid_to_purple_name(im_header->uid_from); | 859 who = uid_to_purple_name(im_header->uid_from); |
357 b = purple_find_buddy(gc->account, who); | 860 buddy = purple_find_buddy(gc->account, who); |
358 if (b == NULL) { | 861 if (buddy == NULL) { |
359 /* create no-auth buddy */ | 862 /* create no-auth buddy */ |
360 b = qq_buddy_new(gc, im_header->uid_from); | 863 buddy = qq_buddy_new(gc, im_header->uid_from); |
361 } | 864 } |
362 bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data; | 865 bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data; |
363 if (bd != NULL) { | 866 if (bd != NULL) { |
364 bd->client_tag = im_header->version_from; | 867 bd->client_tag = im_header->version_from; |
868 bd->face = im_text.sender_icon; | |
869 qq_update_buddy_icon(gc->account, who, bd->face); | |
365 } | 870 } |
366 | 871 |
367 purple_msg_type = 0; | 872 purple_msg_type = 0; |
368 | 873 |
369 msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg); | 874 msg_smiley = qq_emoticon_to_purple(im_text.msg); |
370 msg_utf8_encoded = im_text.font_attr ? | 875 if (im_text.font_attr != NULL) { |
371 qq_encode_to_purple(im_text.font_attr, | 876 msg_fmt = qq_format_to_purple(im_text.font_attr, im_text.font_attr_len, |
372 im_text.font_attr_len, | 877 msg_smiley); |
373 msg_with_purple_smiley, qd->client_version) | 878 msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); |
374 : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); | 879 g_free(msg_fmt); |
880 } else { | |
881 msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); | |
882 } | |
883 g_free(msg_smiley); | |
375 | 884 |
376 /* send encoded to purple, note that we use im_text.send_time, | 885 /* send encoded to purple, note that we use im_text.send_time, |
377 * not the time we receive the message | 886 * not the time we receive the message |
378 * as it may have been delayed when I am not online. */ | 887 * as it may have been delayed when I am not online. */ |
379 serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time); | 888 serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time); |
380 | 889 |
381 g_free(msg_utf8_encoded); | 890 g_free(msg_utf8); |
382 g_free(msg_with_purple_smiley); | |
383 g_free(who); | 891 g_free(who); |
384 g_free(im_text.msg); | 892 g_free(im_text.msg); |
385 if (im_text.font_attr) g_free(im_text.font_attr); | 893 if (im_text.font_attr) g_free(im_text.font_attr); |
386 } | 894 } |
387 | 895 |
398 purple_debug_error("QQ", "Fail read im header, len %d\n", len); | 906 purple_debug_error("QQ", "Fail read im header, len %d\n", len); |
399 qq_show_packet ("IM Header", data, len); | 907 qq_show_packet ("IM Header", data, len); |
400 return; | 908 return; |
401 } | 909 } |
402 purple_debug_info("QQ", | 910 purple_debug_info("QQ", |
403 "Got IM to %d, type: %02X from: %d ver: %s (%04X)\n", | 911 "Got IM to %u, type: %02X from: %u ver: %s (%04X)\n", |
404 im_header.uid_to, im_header.im_type, im_header.uid_from, | 912 im_header.uid_to, im_header.im_type, im_header.uid_from, |
405 qq_get_ver_desc(im_header.version_from), im_header.version_from); | 913 qq_get_ver_desc(im_header.version_from), im_header.version_from); |
406 | 914 |
407 switch (im_header.im_type) { | 915 switch (im_header.im_type) { |
408 case QQ_NORMAL_IM_TEXT: | 916 case QQ_NORMAL_IM_TEXT: |
459 purple_debug_error("QQ", "Fail read im header, len %d\n", len); | 967 purple_debug_error("QQ", "Fail read im header, len %d\n", len); |
460 qq_show_packet ("IM Header", data, len); | 968 qq_show_packet ("IM Header", data, len); |
461 return; | 969 return; |
462 } | 970 } |
463 purple_debug_info("QQ", | 971 purple_debug_info("QQ", |
464 "Got Extend IM to %d, type: %02X from: %d ver: %s (%04X)\n", | 972 "Got Extend IM to %u, type: %02X from: %u ver: %s (%04X)\n", |
465 im_header.uid_to, im_header.im_type, im_header.uid_from, | 973 im_header.uid_to, im_header.im_type, im_header.uid_from, |
466 qq_get_ver_desc(im_header.version_from), im_header.version_from); | 974 qq_get_ver_desc(im_header.version_from), im_header.version_from); |
467 | 975 |
468 switch (im_header.im_type) { | 976 switch (im_header.im_type) { |
469 case QQ_NORMAL_IM_TEXT: | 977 case QQ_NORMAL_IM_TEXT: |
470 process_extend_im_text(gc, data + bytes, len - bytes, &im_header); | 978 process_extend_im_text(gc, data + bytes, len - bytes, &im_header); |
471 break; | 979 break; |
472 case QQ_NORMAL_IM_FILE_REJECT_UDP: | 980 case QQ_NORMAL_IM_FILE_REJECT_UDP: |
473 qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc); | 981 qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc); |
474 break; | 982 break; |
475 case QQ_NORMAL_IM_FILE_APPROVE_UDP: | 983 case QQ_NORMAL_IM_FILE_APPROVE_UDP: |
476 qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc); | 984 qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc); |
477 break; | 985 break; |
478 case QQ_NORMAL_IM_FILE_REQUEST_UDP: | 986 case QQ_NORMAL_IM_FILE_REQUEST_UDP: |
479 qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc); | 987 qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc); |
480 break; | 988 break; |
481 case QQ_NORMAL_IM_FILE_CANCEL: | 989 case QQ_NORMAL_IM_FILE_CANCEL: |
482 qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc); | 990 qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc); |
483 break; | 991 break; |
484 case QQ_NORMAL_IM_FILE_NOTIFY: | 992 case QQ_NORMAL_IM_FILE_NOTIFY: |
485 qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc); | 993 qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc); |
486 break; | 994 break; |
487 default: | 995 case QQ_NORMAL_IM_FILE_REQUEST_TCP: |
488 /* a simple process here, maybe more later */ | 996 /* Check ReceivedFileIM::parseContents in eva*/ |
489 qq_show_packet ("Unknow", data + bytes, len - bytes); | 997 /* some client use this function for detect invisable buddy*/ |
490 break; | 998 case QQ_NORMAL_IM_FILE_APPROVE_TCP: |
999 case QQ_NORMAL_IM_FILE_REJECT_TCP: | |
1000 case QQ_NORMAL_IM_FILE_PASV: | |
1001 case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP: | |
1002 case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT: | |
1003 case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL: | |
1004 case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP: | |
1005 qq_show_packet ("Not support", data, len); | |
1006 break; | |
1007 default: | |
1008 /* a simple process here, maybe more later */ | |
1009 qq_show_packet ("Unknow", data + bytes, len - bytes); | |
1010 break; | |
491 } | 1011 } |
492 } | 1012 } |
493 | 1013 |
494 /* send an IM to uid_to */ | 1014 /* send an IM to uid_to */ |
495 void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type) | 1015 static void request_send_im(PurpleConnection *gc, guint32 uid_to, gint type, |
1016 qq_im_format *fmt, gchar *msg, guint16 id, guint8 frag_count, guint8 frag_index) | |
496 { | 1017 { |
497 qq_data *qd; | 1018 qq_data *qd; |
498 guint8 *raw_data, *send_im_tail; | 1019 guint8 raw_data[MAX_PACKET_SIZE - 16]; |
499 guint16 im_type; | 1020 guint16 im_type; |
500 gint msg_len, raw_len, font_name_len, tail_len, bytes; | 1021 gint bytes; |
501 time_t now; | 1022 time_t now; |
502 gchar *msg_filtered; | |
503 GData *attribs; | |
504 gchar *font_size = NULL, *font_color = NULL, *font_name = NULL, *tmp; | |
505 gboolean is_bold = FALSE, is_italic = FALSE, is_underline = FALSE; | |
506 const gchar *start, *end, *last; | |
507 | 1023 |
508 qd = (qq_data *) gc->proto_data; | 1024 qd = (qq_data *) gc->proto_data; |
509 im_type = QQ_NORMAL_IM_TEXT; | 1025 im_type = QQ_NORMAL_IM_TEXT; |
510 | 1026 |
511 last = msg; | 1027 /* purple_debug_info("QQ", "Send IM %d-%d\n", frag_count, frag_index); */ |
512 while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { | |
513 tmp = g_datalist_get_data(&attribs, "size"); | |
514 if (tmp) { | |
515 if (font_size) | |
516 g_free(font_size); | |
517 font_size = g_strdup(tmp); | |
518 } | |
519 tmp = g_datalist_get_data(&attribs, "color"); | |
520 if (tmp) { | |
521 if (font_color) | |
522 g_free(font_color); | |
523 font_color = g_strdup(tmp); | |
524 } | |
525 tmp = g_datalist_get_data(&attribs, "face"); | |
526 if (tmp) { | |
527 if (font_name) | |
528 g_free(font_name); | |
529 font_name = g_strdup(tmp); | |
530 } | |
531 | |
532 g_datalist_clear(&attribs); | |
533 last = end + 1; | |
534 } | |
535 | |
536 if (purple_markup_find_tag("b", msg, &start, &end, &attribs)) { | |
537 is_bold = TRUE; | |
538 g_datalist_clear(&attribs); | |
539 } | |
540 | |
541 if (purple_markup_find_tag("i", msg, &start, &end, &attribs)) { | |
542 is_italic = TRUE; | |
543 g_datalist_clear(&attribs); | |
544 } | |
545 | |
546 if (purple_markup_find_tag("u", msg, &start, &end, &attribs)) { | |
547 is_underline = TRUE; | |
548 g_datalist_clear(&attribs); | |
549 } | |
550 | |
551 purple_debug_info("QQ_MESG", "send mesg: %s\n", msg); | |
552 msg_filtered = purple_markup_strip_html(msg); | |
553 msg_len = strlen(msg_filtered); | |
554 now = time(NULL); | |
555 | |
556 font_name_len = (font_name) ? strlen(font_name) : DEFAULT_FONT_NAME_LEN; | |
557 tail_len = font_name_len + QQ_SEND_IM_AFTER_MSG_HEADER_LEN + 1; | |
558 | |
559 raw_len = QQ_SEND_IM_BEFORE_MSG_LEN + msg_len + tail_len; | |
560 raw_data = g_newa(guint8, raw_len); | |
561 bytes = 0; | 1028 bytes = 0; |
562 | |
563 /* 000-003: receiver uid */ | 1029 /* 000-003: receiver uid */ |
564 bytes += qq_put32(raw_data + bytes, qd->uid); | 1030 bytes += qq_put32(raw_data + bytes, qd->uid); |
565 /* 004-007: sender uid */ | 1031 /* 004-007: sender uid */ |
566 bytes += qq_put32(raw_data + bytes, uid_to); | 1032 bytes += qq_put32(raw_data + bytes, uid_to); |
567 /* 008-009: sender client version */ | 1033 /* 008-009: sender client version */ |
571 /* 014-017: sender uid */ | 1037 /* 014-017: sender uid */ |
572 bytes += qq_put32(raw_data + bytes, uid_to); | 1038 bytes += qq_put32(raw_data + bytes, uid_to); |
573 /* 018-033: md5 of (uid+session_key) */ | 1039 /* 018-033: md5 of (uid+session_key) */ |
574 bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); | 1040 bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); |
575 /* 034-035: message type */ | 1041 /* 034-035: message type */ |
576 bytes += qq_put16(raw_data + bytes, im_type); | 1042 bytes += qq_put16(raw_data + bytes, QQ_NORMAL_IM_TEXT); |
577 /* 036-037: sequence number */ | 1043 /* 036-037: sequence number */ |
578 bytes += qq_put16(raw_data + bytes, qd->send_seq); | 1044 bytes += qq_put16(raw_data + bytes, qd->send_seq); |
579 /* 038-041: send time */ | 1045 /* 038-041: send time */ |
1046 now = time(NULL); | |
580 bytes += qq_put32(raw_data + bytes, (guint32) now); | 1047 bytes += qq_put32(raw_data + bytes, (guint32) now); |
581 /* 042-043: sender icon */ | 1048 /* 042-043: sender icon */ |
582 bytes += qq_put16(raw_data + bytes, qd->my_icon); | 1049 bytes += qq_put16(raw_data + bytes, qd->my_icon); |
583 /* 044-046: always 0x00 */ | 1050 /* 044-046: always 0x00 */ |
584 bytes += qq_put16(raw_data + bytes, 0x0000); | 1051 bytes += qq_put16(raw_data + bytes, 0x0000); |
585 bytes += qq_put8(raw_data + bytes, 0x00); | 1052 bytes += qq_put8(raw_data + bytes, 0x00); |
586 /* 047-047: we use font attr */ | 1053 /* 047-047: always use font attr */ |
587 bytes += qq_put8(raw_data + bytes, 0x01); | 1054 bytes += qq_put8(raw_data + bytes, 0x01); |
588 /* 048-051: always 0x00 */ | 1055 /* 048-051: always 0x00 */ |
589 bytes += qq_put32(raw_data + bytes, 0x00000000); | 1056 /* Fixme: frag_count, frag_index not working now */ |
1057 bytes += qq_put8(raw_data + bytes, 0/*frag_count*/); | |
1058 bytes += qq_put8(raw_data + bytes, 0/*frag_index*/); | |
1059 bytes += qq_put16(raw_data + bytes, id); | |
590 /* 052-052: text message type (normal/auto-reply) */ | 1060 /* 052-052: text message type (normal/auto-reply) */ |
591 bytes += qq_put8(raw_data + bytes, type); | 1061 bytes += qq_put8(raw_data + bytes, type); |
592 /* 053- : msg ends with 0x00 */ | 1062 /* 053- : msg ends with 0x00 */ |
593 bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len); | 1063 bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); |
594 send_im_tail = qq_get_send_im_tail(font_color, font_size, font_name, is_bold, | 1064 bytes += qq_put8(raw_data + bytes, 0); |
595 is_italic, is_underline, tail_len); | 1065 bytes += qq_put_im_tail(raw_data + bytes, fmt); |
596 /* qq_show_packet("qq_get_send_im_tail", send_im_tail, tail_len); */ | 1066 |
597 bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len); | 1067 /* qq_show_packet("QQ_CMD_SEND_IM", raw_data, bytes); */ |
598 | 1068 qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); |
599 /* qq_show_packet("QQ_CMD_SEND_IM, raw_data, bytes); */ | 1069 } |
600 | 1070 |
601 if (bytes == raw_len) /* create packet OK */ | 1071 static void im_convert_and_merge(GString *dest, GString *append) |
602 qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); | 1072 { |
603 else | 1073 gchar *converted; |
604 purple_debug_error("QQ", | 1074 g_return_if_fail(dest != NULL && append != NULL); |
605 "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes); | 1075 |
606 | 1076 if (append->str == NULL || append->len <= 0) { |
607 if (font_color) | 1077 return; |
608 g_free(font_color); | 1078 } |
609 if (font_size) | 1079 /* purple_debug_info("QQ", "Append:\n%s\n", append->str); */ |
610 g_free(font_size); | 1080 converted = utf8_to_qq(append->str, QQ_CHARSET_DEFAULT); |
611 g_free(send_im_tail); | 1081 g_string_append(dest, converted); |
612 g_free(msg_filtered); | 1082 g_string_set_size(append, 0); |
613 } | 1083 g_free(converted); |
614 | 1084 } |
615 | 1085 |
616 | 1086 GSList *qq_im_get_segments(gchar *msg_stripped, gboolean is_smiley_none) |
1087 { | |
1088 GSList *string_list = NULL; | |
1089 GString *new_string; | |
1090 GString *append_utf8; | |
1091 gchar *start, *p; | |
1092 gint count, len; | |
1093 qq_emoticon *emoticon; | |
1094 | |
1095 g_return_val_if_fail(msg_stripped != NULL, NULL); | |
1096 | |
1097 start = msg_stripped; | |
1098 count = 0; | |
1099 new_string = g_string_new(""); | |
1100 append_utf8 = g_string_new(""); | |
1101 while (*start) { | |
1102 p = start; | |
1103 | |
1104 /* Convert emoticon */ | |
1105 if (!is_smiley_none && *p == '/') { | |
1106 if (new_string->len + append_utf8->len + 2 > QQ_MSG_IM_MAX) { | |
1107 /* enough chars to send */ | |
1108 im_convert_and_merge(new_string, append_utf8); | |
1109 g_string_append_c(new_string, 0x20); /* always for last smiley */ | |
1110 string_list = g_slist_append(string_list, strdup(new_string->str)); | |
1111 g_string_set_size(new_string, 0); | |
1112 continue; | |
1113 } | |
1114 emoticon = emoticon_find(p); | |
1115 if (emoticon != NULL) { | |
1116 purple_debug_info("QQ", "found emoticon %s as 0x%02X\n", | |
1117 emoticon->name, emoticon->symbol); | |
1118 /* QQ emoticon code prevent converting from utf8 to QQ charset | |
1119 * convert append_utf8 to QQ charset | |
1120 * merge the result to dest | |
1121 * append qq QQ emoticon code to dest */ | |
1122 im_convert_and_merge(new_string, append_utf8); | |
1123 g_string_append_c(new_string, 0x14); | |
1124 g_string_append_c(new_string, emoticon->symbol); | |
1125 start += strlen(emoticon->name); | |
1126 continue; | |
1127 } else { | |
1128 purple_debug_info("QQ", "Not found emoticon %.20s\n", p); | |
1129 } | |
1130 } | |
1131 | |
1132 /* Get next char */ | |
1133 start = g_utf8_next_char(p); | |
1134 len = start - p; | |
1135 if (new_string->len + append_utf8->len + len > QQ_MSG_IM_MAX) { | |
1136 /* enough chars to send */ | |
1137 im_convert_and_merge(new_string, append_utf8); | |
1138 g_string_append_c(new_string, 0x20); /* always for last smiley */ | |
1139 string_list = g_slist_append(string_list, strdup(new_string->str)); | |
1140 g_string_set_size(new_string, 0); | |
1141 } | |
1142 g_string_append_len(append_utf8, p, len); | |
1143 } | |
1144 | |
1145 if (new_string->len + append_utf8->len > 0) { | |
1146 im_convert_and_merge(new_string, append_utf8); | |
1147 g_string_append_c(new_string, 0x20); /* always for last smiley */ | |
1148 string_list = g_slist_append(string_list, strdup(new_string->str)); | |
1149 } | |
1150 g_string_free(new_string, TRUE); | |
1151 g_string_free(append_utf8, TRUE); | |
1152 return string_list; | |
1153 } | |
1154 | |
1155 gboolean qq_im_smiley_none(const gchar *msg) | |
1156 { | |
1157 const gchar *start, *end, *last; | |
1158 GData *attribs; | |
1159 gchar *tmp; | |
1160 gboolean ret = FALSE; | |
1161 | |
1162 g_return_val_if_fail(msg != NULL, TRUE); | |
1163 | |
1164 last = msg; | |
1165 while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { | |
1166 tmp = g_datalist_get_data(&attribs, "sml"); | |
1167 if (tmp && strlen(tmp) > 0) { | |
1168 if (strcmp(tmp, "none") == 0) { | |
1169 ret = TRUE; | |
1170 break; | |
1171 } | |
1172 } | |
1173 g_datalist_clear(&attribs); | |
1174 last = end + 1; | |
1175 } | |
1176 return ret; | |
1177 } | |
1178 | |
1179 /* Grab custom emote icons | |
1180 static GSList* qq_grab_emoticons(const char *msg, const char*username) | |
1181 { | |
1182 GSList *list; | |
1183 GList *smileys; | |
1184 PurpleSmiley *smiley; | |
1185 const char *smiley_shortcut; | |
1186 char *ptr; | |
1187 int length; | |
1188 PurpleStoredImage *img; | |
1189 | |
1190 smileys = purple_smileys_get_all(); | |
1191 length = strlen(msg); | |
1192 | |
1193 for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { | |
1194 smiley = smileys->data; | |
1195 smiley_shortcut = purple_smiley_get_shortcut(smiley); | |
1196 purple_debug_info("QQ", "Smiley shortcut [%s]\n", smiley_shortcut); | |
1197 | |
1198 ptr = g_strstr_len(msg, length, smiley_shortcut); | |
1199 | |
1200 if (!ptr) | |
1201 continue; | |
1202 | |
1203 purple_debug_info("QQ", "Found Smiley shortcut [%s]\n", smiley_shortcut); | |
1204 | |
1205 img = purple_smiley_get_stored_image(smiley); | |
1206 | |
1207 emoticon = g_new0(MsnEmoticon, 1); | |
1208 emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley)); | |
1209 emoticon->obj = msn_object_new_from_image(img, | |
1210 purple_imgstore_get_filename(img), | |
1211 username, MSN_OBJECT_EMOTICON); | |
1212 | |
1213 purple_imgstore_unref(img); | |
1214 list = g_slist_prepend(list, emoticon); | |
1215 } | |
1216 return list; | |
1217 } | |
1218 */ | |
1219 | |
1220 gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *what, PurpleMessageFlags flags) | |
1221 { | |
1222 qq_data *qd; | |
1223 guint32 uid_to; | |
1224 gint type; | |
1225 qq_im_format *fmt; | |
1226 gchar *msg_stripped, *tmp; | |
1227 GSList *segments, *it; | |
1228 gint msg_len; | |
1229 const gchar *start_invalid; | |
1230 gboolean is_smiley_none; | |
1231 guint8 frag_count, frag_index; | |
1232 | |
1233 g_return_val_if_fail(NULL != gc && NULL != gc->proto_data, -1); | |
1234 g_return_val_if_fail(who != NULL && what != NULL, -1); | |
1235 | |
1236 qd = (qq_data *) gc->proto_data; | |
1237 purple_debug_info("QQ", "Send IM to %s, len %d:\n%s\n", who, strlen(what), what); | |
1238 | |
1239 uid_to = purple_name_to_uid(who); | |
1240 if (uid_to == qd->uid) { | |
1241 /* if msg is to myself, bypass the network */ | |
1242 serv_got_im(gc, who, what, flags, time(NULL)); | |
1243 return 1; | |
1244 } | |
1245 | |
1246 type = (flags == PURPLE_MESSAGE_AUTO_RESP ? QQ_IM_AUTO_REPLY : QQ_IM_TEXT); | |
1247 /* qq_show_packet("IM UTF8", (guint8 *)what, strlen(what)); */ | |
1248 | |
1249 msg_stripped = purple_markup_strip_html(what); | |
1250 g_return_val_if_fail(msg_stripped != NULL, -1); | |
1251 /* qq_show_packet("IM Stripped", (guint8 *)what, strlen(what)); */ | |
1252 | |
1253 /* Check and valid utf8 string */ | |
1254 msg_len = strlen(msg_stripped); | |
1255 g_return_val_if_fail(msg_len > 0, -1); | |
1256 if (!g_utf8_validate(msg_stripped, msg_len, &start_invalid)) { | |
1257 if (start_invalid > msg_stripped) { | |
1258 tmp = g_strndup(msg_stripped, start_invalid - msg_stripped); | |
1259 g_free(msg_stripped); | |
1260 msg_stripped = g_strconcat(tmp, _("(Invalid UTF-8 string)"), NULL); | |
1261 g_free(tmp); | |
1262 } else { | |
1263 g_free(msg_stripped); | |
1264 msg_stripped = g_strdup(_("(Invalid UTF-8 string)")); | |
1265 } | |
1266 } | |
1267 | |
1268 is_smiley_none = qq_im_smiley_none(what); | |
1269 segments = qq_im_get_segments(msg_stripped, is_smiley_none); | |
1270 g_free(msg_stripped); | |
1271 | |
1272 if (segments == NULL) { | |
1273 return -1; | |
1274 } | |
1275 | |
1276 qd->send_im_seq++; | |
1277 fmt = qq_im_fmt_new_by_purple(what); | |
1278 frag_count = g_slist_length(segments); | |
1279 frag_index = 0; | |
1280 for (it = segments; it; it = it->next) { | |
1281 request_send_im(gc, uid_to, type, fmt, (gchar *)it->data, | |
1282 qd->send_im_seq, frag_count, frag_index); | |
1283 g_free(it->data); | |
1284 frag_index++; | |
1285 } | |
1286 g_slist_free(segments); | |
1287 qq_im_fmt_free(fmt); | |
1288 return 1; | |
1289 } |