comparison libpurple/protocols/qq/buddy_list.c @ 23683:1c50f12b1c52

2008.08.02 - csyfek <csyfek(at)gmail.com> * Commit to Pidgin * Tickets: Fixes #1861 Fixes #1902 References #5112 2008.08.02 - ccpaging <ecc_hy(at)hotmail.com> * Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH] * Use random value in inikey * TEA header padding in crypt.c * Rewrite login part of qq_process 2008.07.31 - ccpaging <ecc_hy(at)hotmail.com> * Fixed: send reply when get duplicate server command. The server may not get our reply before. * Tag custom picture as text "(Broken)" 2008.07.30 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> * Change some debug message * Modify buddy status flag according to eva for QQ2006 * Modify buddy status parse and correspond to eva2 * Add getIP/putIP functions to packet_parse.c, and replace some gen_ip_str * Replace guint32 *ip with struct in_addr, and reduce g_new/g_free operation * Source file changed: Merge buddy_status into buddy_list Change login_logout to qq_base Merge keep_alive into qq_base New qq_process extract from qq_network * Fixed: Byte alignment bug in crypt.c, tested in ARM PDA * Fixed: group chat message may get in before getting group info, and so group info is empty * Add qq_send_cmd_group_get_group_info when joined a group chat in group_im.c * Add some new group command identify according eva but further program * Add some new QQ client version identify * Fixed: Identify buddy's client version by IM packet, and not by status * Add some new info in buddy's tooltip text * Add video falg to buddy's emblem. But those flag in buddy status may not prasing correctly * Use new timeout function to handle send keep_alive, resend packet, update buddy status * Add new advanced options: The end user may change interval of keep_alive, resend packet, update buddy status to feed their need. For example, saving network flow when use mobile phone. Keep alive packet must be sent in 60-120 seconds whatever client rcved data of not. The intervals of keep alive and update status should be multiple of resend's interval, Since we use counter not time() in a single timeout function for efficiency. * Rewrite qq_trans.c, and use one g_list to manage: Store server packet before login, and prase all of them when get login Store client send packet for resend scanning, confirm server reply, filter duplicate server reply Store server packet for filter out duplicate * Add QQ_MSG_SYS_NOTICE = 0x06 in sys_msg.c * Rewrite qq_proc_cmd_reply and qq_proc_cmd_server: In QQ protocol, one packet reply may need a new packet send later. We may call it packet trigger. The triggers always is hided in every qq_process_reply. Now we try to extract those triggers and put into a single function, and then every trigger should be obviously and easy to manage.
author SHiNE CsyFeK <csyfek@gmail.com>
date Sat, 02 Aug 2008 15:00:46 +0000
parents bdb38a8bf721
children 5f454b975a99
comparison
equal deleted inserted replaced
23682:21e591b55339 23683:1c50f12b1c52
30 #include "notify.h" 30 #include "notify.h"
31 #include "utils.h" 31 #include "utils.h"
32 #include "packet_parse.h" 32 #include "packet_parse.h"
33 #include "buddy_info.h" 33 #include "buddy_info.h"
34 #include "buddy_list.h" 34 #include "buddy_list.h"
35 #include "buddy_status.h"
36 #include "buddy_opt.h" 35 #include "buddy_opt.h"
37 #include "char_conv.h" 36 #include "char_conv.h"
38 #include "crypt.h" 37 #include "crypt.h"
39 #include "header_info.h" 38 #include "header_info.h"
40 #include "keep_alive.h" 39 #include "qq_base.h"
41 #include "group.h" 40 #include "group.h"
42 #include "group_find.h" 41 #include "group_find.h"
43 #include "group_internal.h" 42 #include "group_internal.h"
44 #include "group_info.h" 43 #include "group_info.h"
45 44
46 #include "qq_network.h" 45 #include "qq_network.h"
47 46
48 #define QQ_GET_ONLINE_BUDDY_02 0x02 47 #define QQ_GET_ONLINE_BUDDY_02 0x02
49 #define QQ_GET_ONLINE_BUDDY_03 0x03 /* unknown function */ 48 #define QQ_GET_ONLINE_BUDDY_03 0x03 /* unknown function */
50 49
51 #define QQ_ONLINE_BUDDY_ENTRY_LEN 38 50 typedef struct _qq_buddy_online {
52 51 qq_buddy_status bs;
53 typedef struct _qq_friends_online_entry {
54 qq_buddy_status *s;
55 guint16 unknown1; 52 guint16 unknown1;
56 guint8 flag1; 53 guint8 ext_flag;
57 guint8 comm_flag; 54 guint8 comm_flag;
58 guint16 unknown2; 55 guint16 unknown2;
59 guint8 ending; /* 0x00 */ 56 guint8 ending; /* 0x00 */
60 } qq_friends_online_entry; 57 } qq_buddy_online;
61 58
62 /* get a list of online_buddies */ 59 /* get a list of online_buddies */
63 void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position) 60 void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position)
64 { 61 {
65 qq_data *qd; 62 qq_data *qd;
79 /* 002-002 */ 76 /* 002-002 */
80 bytes += qq_put8(raw_data + bytes, 0x00); 77 bytes += qq_put8(raw_data + bytes, 0x00);
81 /* 003-004 */ 78 /* 003-004 */
82 bytes += qq_put16(raw_data + bytes, 0x0000); 79 bytes += qq_put16(raw_data + bytes, 0x0000);
83 80
84 qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_ONLINE, raw_data, 5); 81 qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5);
85 qd->last_get_online = time(NULL); 82 qd->last_get_online = time(NULL);
86 } 83 }
87 84
88 /* position starts with 0x0000, 85 /* position starts with 0x0000,
89 * server may return a position tag if list is too long for one packet */ 86 * server may return a position tag if list is too long for one packet */
100 * even can sending packets 00 and get no response. 97 * even can sending packets 00 and get no response.
101 * Now I tested that 00,00,00,00,00,01 work perfectly 98 * Now I tested that 00,00,00,00,00,01 work perfectly
102 * March 22, found the 00,00,00 starts to work as well */ 99 * March 22, found the 00,00,00 starts to work as well */
103 bytes += qq_put8(raw_data + bytes, 0x00); 100 bytes += qq_put8(raw_data + bytes, 0x00);
104 101
105 qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_LIST, raw_data, bytes); 102 qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes);
106 } 103 }
107 104
108 /* get all list, buddies & Quns with groupsid support */ 105 /* get all list, buddies & Quns with groupsid support */
109 void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position) 106 void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position)
110 { 107 {
121 bytes += qq_put32(raw_data + bytes, position); 118 bytes += qq_put32(raw_data + bytes, position);
122 119
123 qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes); 120 qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes);
124 } 121 }
125 122
126 static void _qq_buddies_online_reply_dump_unclear(qq_friends_online_entry *fe) 123 /* parse the data into qq_buddy_status */
127 { 124 static gint get_buddy_status(qq_buddy_status *bs, guint8 *data)
128 GString *dump; 125 {
129 126 gint bytes = 0;
130 g_return_if_fail(fe != NULL); 127
131 128 g_return_val_if_fail(data != NULL && bs != NULL, -1);
132 qq_buddy_status_dump_unclear(fe->s); 129
133 130 /* 000-003: uid */
134 dump = g_string_new(""); 131 bytes += qq_get32(&bs->uid, data + bytes);
135 g_string_append_printf(dump, "unclear fields for [%d]:\n", fe->s->uid); 132 /* 004-004: 0x01 */
136 g_string_append_printf(dump, "031-032: %04x (unknown)\n", fe->unknown1); 133 bytes += qq_get8(&bs->unknown1, data + bytes);
137 g_string_append_printf(dump, "033: %02x (flag1)\n", fe->flag1); 134 /* this is no longer the IP, it seems QQ (as of 2006) no longer sends
138 g_string_append_printf(dump, "034: %02x (comm_flag)\n", fe->comm_flag); 135 * the buddy's IP in this packet. all 0s */
139 g_string_append_printf(dump, "035-036: %04x (unknown)\n", fe->unknown2); 136 /* 005-008: ip */
140 137 bytes += qq_getIP(&bs->ip, data + bytes);
141 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Online buddy entry, %s", dump->str); 138 /* port info is no longer here either */
142 g_string_free(dump, TRUE); 139 /* 009-010: port */
143 } 140 bytes += qq_get16(&bs->port, data + bytes);
141 /* 011-011: 0x00 */
142 bytes += qq_get8(&bs->unknown2, data + bytes);
143 /* 012-012: status */
144 bytes += qq_get8(&bs->status, data + bytes);
145 /* 013-014: client_version */
146 bytes += qq_get16(&bs->unknown3, data + bytes);
147 /* 015-030: unknown key */
148 bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes);
149
150 purple_debug(PURPLE_DEBUG_INFO, "QQ_STATUS",
151 "uid: %d, U1: %d, ip: %s:%d, U2:%d, status:%d, U3:%04X\n",
152 bs->uid, bs->unknown1, inet_ntoa(bs->ip), bs->port,
153 bs->unknown2, bs->status, bs->unknown3);
154
155 return bytes;
156 }
157
158 #define QQ_ONLINE_BUDDY_ENTRY_LEN 38
144 159
145 /* process the reply packet for get_buddies_online packet */ 160 /* process the reply packet for get_buddies_online packet */
146 void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) 161 guint8 qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
147 { 162 {
148 qq_data *qd; 163 qq_data *qd;
149 gint len, bytes, bytes_buddy; 164 gint len, bytes, bytes_buddy;
150 gint count; 165 gint count;
151 guint8 *data, position; 166 guint8 *data, position;
152 PurpleBuddy *b; 167 PurpleBuddy *b;
153 qq_buddy *q_bud; 168 qq_buddy *q_bud;
154 qq_friends_online_entry *fe; 169 qq_buddy_online bo;
155 170
156 g_return_if_fail(buf != NULL && buf_len != 0); 171 g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
157 172
158 qd = (qq_data *) gc->proto_data; 173 qd = (qq_data *) gc->proto_data;
159 len = buf_len; 174 len = buf_len;
160 data = g_newa(guint8, len); 175 data = g_newa(guint8, len);
161 176
162 purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n"); 177 purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n");
163 178
164 if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { 179 if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
165 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online"); 180 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
166 return; 181 return -1;
167 } 182 }
168 183
169 qq_show_packet("Get buddies online reply packet", data, len); 184 /* qq_show_packet("Get buddies online reply packet", data, len); */
170 185
171 bytes = 0; 186 bytes = 0;
172 bytes += qq_get8(&position, data + bytes); 187 bytes += qq_get8(&position, data + bytes);
173 188
174 fe = g_newa(qq_friends_online_entry, 1);
175 fe->s = g_newa(qq_buddy_status, 1);
176
177 count = 0; 189 count = 0;
178 while (bytes < len) { 190 while (bytes < len) {
191 if (len - bytes < QQ_ONLINE_BUDDY_ENTRY_LEN) {
192 purple_debug(PURPLE_DEBUG_ERROR, "QQ",
193 "[buddies online] only %d, need %d",
194 (len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN);
195 break;
196 }
197 memset(&bo, 0 ,sizeof(bo));
198
179 /* set flag */ 199 /* set flag */
180 bytes_buddy = bytes; 200 bytes_buddy = bytes;
181 /* based on one online buddy entry */ 201 /* based on one online buddy entry */
182 /* ATTTENTION! NEWED in the sub function, but FREED here */
183 /* 000-030 qq_buddy_status */ 202 /* 000-030 qq_buddy_status */
184 bytes += qq_buddy_status_read(fe->s, data + bytes); 203 bytes += get_buddy_status(&(bo.bs), data + bytes);
185 /* 031-032: unknown4 */ 204 /* 031-032: */
186 bytes += qq_get16(&fe->unknown1, data + bytes); 205 bytes += qq_get16(&bo.unknown1, data + bytes);
187 /* 033-033: flag1 */ 206 /* 033-033: ext_flag */
188 bytes += qq_get8(&fe->flag1, data + bytes); 207 bytes += qq_get8(&bo.ext_flag, data + bytes);
189 /* 034-034: comm_flag */ 208 /* 034-034: comm_flag */
190 bytes += qq_get8(&fe->comm_flag, data + bytes); 209 bytes += qq_get8(&bo.comm_flag, data + bytes);
191 /* 035-036: */ 210 /* 035-036: */
192 bytes += qq_get16(&fe->unknown2, data + bytes); 211 bytes += qq_get16(&bo.unknown2, data + bytes);
193 /* 037-037: */ 212 /* 037-037: */
194 bytes += qq_get8(&fe->ending, data + bytes); /* 0x00 */ 213 bytes += qq_get8(&bo.ending, data + bytes); /* 0x00 */
195 214
196 if (fe->s->uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) { 215 if (bo.bs.uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
197 purple_debug(PURPLE_DEBUG_ERROR, "QQ", 216 purple_debug(PURPLE_DEBUG_ERROR, "QQ",
198 "uid=0 or entry complete len(%d) != %d", 217 "uid=0 or entry complete len(%d) != %d",
199 (bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN); 218 (bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN);
200 g_free(fe->s->ip);
201 g_free(fe->s->unknown_key);
202 continue; 219 continue;
203 } /* check if it is a valid entry */ 220 } /* check if it is a valid entry */
204 221
205 if (QQ_DEBUG) { 222 /* update buddy information */
206 _qq_buddies_online_reply_dump_unclear(fe); 223 b = purple_find_buddy(purple_connection_get_account(gc),
224 uid_to_purple_name(bo.bs.uid) );
225 q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
226 if (q_bud == NULL) {
227 purple_debug(PURPLE_DEBUG_ERROR, "QQ",
228 "Got an online buddy %d, but not in my buddy list\n", bo.bs.uid);
229 continue;
207 } 230 }
208 231 /* we find one and update qq_buddy */
209 /* update buddy information */ 232 /*
210 b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid)); 233 if(0 != fe->s->client_version)
211 q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; 234 q_bud->client_version = fe->s->client_version;
212 235 */
213 if (q_bud != NULL) { /* we find one and update qq_buddy */ 236 q_bud->ip.s_addr = bo.bs.ip.s_addr;
214 if(0 != fe->s->client_version) 237 q_bud->port = bo.bs.port;
215 q_bud->client_version = fe->s->client_version; 238 q_bud->status = bo.bs.status;
216 g_memmove(q_bud->ip, fe->s->ip, 4); 239 q_bud->ext_flag = bo.ext_flag;
217 q_bud->port = fe->s->port; 240 q_bud->comm_flag = bo.comm_flag;
218 q_bud->status = fe->s->status; 241 qq_update_buddy_contact(gc, q_bud);
219 q_bud->flag1 = fe->flag1; 242 count++;
220 q_bud->comm_flag = fe->comm_flag;
221 qq_update_buddy_contact(gc, q_bud);
222 count++;
223 } else {
224 purple_debug(PURPLE_DEBUG_ERROR, "QQ",
225 "Got an online buddy %d, but not in my buddy list\n", fe->s->uid);
226 }
227
228 g_free(fe->s->ip);
229 g_free(fe->s->unknown_key);
230 } 243 }
231 244
232 if(bytes > len) { 245 if(bytes > len) {
233 purple_debug(PURPLE_DEBUG_ERROR, "QQ", 246 purple_debug(PURPLE_DEBUG_ERROR, "QQ",
234 "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n"); 247 "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
235 } 248 }
236 249
237 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d online buddies, nextposition=%u\n", 250 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d online buddies, nextposition=%u\n",
238 count, (guint) position); 251 count, (guint) position);
239 if (position != QQ_FRIENDS_ONLINE_POSITION_END 252 return position;
240 && position != QQ_FRIENDS_ONLINE_POSITION_START) {
241 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more online buddies\n");
242 qq_send_packet_get_buddies_online(gc, position);
243 } else {
244 purple_debug(PURPLE_DEBUG_INFO, "QQ", "All online buddies received\n");
245 qq_send_packet_get_buddies_levels(gc);
246 qq_refresh_all_buddy_status(gc);
247 }
248 } 253 }
249 254
250 255
251 /* process reply for get_buddies_list */ 256 /* process reply for get_buddies_list */
252 void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) 257 guint16 qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
253 { 258 {
254 qq_data *qd; 259 qq_data *qd;
255 qq_buddy *q_bud; 260 qq_buddy *q_bud;
256 gint len, bytes_expected, count; 261 gint len, bytes_expected, count;
257 gint bytes, buddy_bytes; 262 gint bytes, buddy_bytes;
258 guint16 position, unknown; 263 guint16 position, unknown;
259 guint8 *data, pascal_len; 264 guint8 *data, pascal_len;
260 gchar *name; 265 gchar *name;
261 PurpleBuddy *b; 266 PurpleBuddy *b;
262 267
263 g_return_if_fail(buf != NULL && buf_len != 0); 268 g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
264 269
265 qd = (qq_data *) gc->proto_data; 270 qd = (qq_data *) gc->proto_data;
266 len = buf_len; 271 len = buf_len;
267 data = g_newa(guint8, len); 272 data = g_newa(guint8, len);
268 273
269 if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { 274 if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
270 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list"); 275 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
271 return; 276 return -1;
272 } 277 }
273 bytes = 0; 278 bytes = 0;
274 bytes += qq_get16(&position, data + bytes); 279 bytes += qq_get16(&position, data + bytes);
275 /* the following data is buddy list in this packet */ 280 /* the following data is buddy list in this packet */
276 count = 0; 281 count = 0;
289 294
290 pascal_len = convert_as_pascal_string(data + bytes, &q_bud->nickname, QQ_CHARSET_DEFAULT); 295 pascal_len = convert_as_pascal_string(data + bytes, &q_bud->nickname, QQ_CHARSET_DEFAULT);
291 bytes += pascal_len; 296 bytes += pascal_len;
292 297
293 bytes += qq_get16(&unknown, data + bytes); 298 bytes += qq_get16(&unknown, data + bytes);
294 /* flag1: (0-7) 299 bytes += qq_get8(&q_bud->ext_flag, data + bytes);
295 * bit1 => qq show
296 * comm_flag: (0-7)
297 * bit1 => member
298 * bit4 => TCP mode
299 * bit5 => open mobile QQ
300 * bit6 => bind to mobile
301 * bit7 => whether having a video
302 */
303 bytes += qq_get8(&q_bud->flag1, data + bytes);
304 bytes += qq_get8(&q_bud->comm_flag, data + bytes); 300 bytes += qq_get8(&q_bud->comm_flag, data + bytes);
305 301
306 bytes_expected = 12 + pascal_len; 302 bytes_expected = 12 + pascal_len;
307 303
308 if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) { 304 if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
315 count++; 311 count++;
316 } 312 }
317 313
318 if (QQ_DEBUG) { 314 if (QQ_DEBUG) {
319 purple_debug(PURPLE_DEBUG_INFO, "QQ", 315 purple_debug(PURPLE_DEBUG_INFO, "QQ",
320 "buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x, nick=%s\n", 316 "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
321 q_bud->uid, q_bud->flag1, q_bud->comm_flag, q_bud->nickname); 317 q_bud->uid, q_bud->ext_flag, q_bud->comm_flag, q_bud->nickname);
322 } 318 }
323 319
324 name = uid_to_purple_name(q_bud->uid); 320 name = uid_to_purple_name(q_bud->uid);
325 b = purple_find_buddy(gc->account, name); 321 b = purple_find_buddy(gc->account, name);
326 g_free(name); 322 g_free(name);
339 "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!"); 335 "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
340 } 336 }
341 337
342 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies, nextposition=%u\n", 338 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies, nextposition=%u\n",
343 count, (guint) position); 339 count, (guint) position);
344 if (position != QQ_FRIENDS_LIST_POSITION_START 340 return position;
345 && position != QQ_FRIENDS_LIST_POSITION_END) { 341 }
346 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies\n"); 342
347 qq_send_packet_get_buddies_list(gc, position); 343 guint32 qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
348 } else {
349 purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies received. Requesting for online buddies list\n");
350 qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_LIST_POSITION_START);
351 }
352 }
353
354 void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
355 { 344 {
356 qq_data *qd; 345 qq_data *qd;
357 gint len, i, j; 346 gint len, i, j;
358 gint bytes = 0; 347 gint bytes = 0;
359 guint8 *data; 348 guint8 *data;
361 guint32 unknown, position; 350 guint32 unknown, position;
362 guint32 uid; 351 guint32 uid;
363 guint8 type, groupid; 352 guint8 type, groupid;
364 qq_group *group; 353 qq_group *group;
365 354
366 g_return_if_fail(buf != NULL && buf_len != 0); 355 g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
367 356
368 qd = (qq_data *) gc->proto_data; 357 qd = (qq_data *) gc->proto_data;
369 len = buf_len; 358 len = buf_len;
370 data = g_newa(guint8, len); 359 data = g_newa(guint8, len);
371 360
372 if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { 361 if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
373 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group"); 362 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
374 return; 363 return -1;
375 } 364 }
376 365
377 bytes += qq_get8(&sub_cmd, data + bytes); 366 bytes += qq_get8(&sub_cmd, data + bytes);
378 g_return_if_fail(sub_cmd == 0x01); 367 g_return_val_if_fail(sub_cmd == 0x01, -1);
379 368
380 bytes += qq_get8(&reply_code, data + bytes); 369 bytes += qq_get8(&reply_code, data + bytes);
381 if(0 != reply_code) { 370 if(0 != reply_code) {
382 purple_debug(PURPLE_DEBUG_WARNING, "QQ", 371 purple_debug(PURPLE_DEBUG_WARNING, "QQ",
383 "Get all list with group reply, reply_code(%d) is not zero", reply_code); 372 "Get all list with group reply, reply_code(%d) is not zero", reply_code);
427 if(bytes > len) { 416 if(bytes > len) {
428 purple_debug(PURPLE_DEBUG_ERROR, "QQ", 417 purple_debug(PURPLE_DEBUG_ERROR, "QQ",
429 "qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!"); 418 "qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
430 } 419 }
431 420
432 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j);
433 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies and %d groups, nextposition=%u\n", i, j, (guint) position); 421 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies and %d groups, nextposition=%u\n", i, j, (guint) position);
434 422 return position;
435 if (position != QQ_FRIENDS_ALL_LIST_POSITION_START 423 }
436 && position != QQ_FRIENDS_ALL_LIST_POSITION_END) { 424
437 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies and groups\n"); 425 #define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001
438 qq_send_packet_get_all_list_with_group(gc, position); 426 #define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */
427
428 /* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses,
429 * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy,
430 * using different accounts to get info. */
431
432 /* check if status means online or offline */
433 gboolean is_online(guint8 status)
434 {
435 switch(status) {
436 case QQ_BUDDY_ONLINE_NORMAL:
437 case QQ_BUDDY_ONLINE_AWAY:
438 case QQ_BUDDY_ONLINE_INVISIBLE:
439 return TRUE;
440 case QQ_BUDDY_ONLINE_OFFLINE:
441 return FALSE;
442 }
443 return FALSE;
444 }
445
446 /* Help calculate the correct icon index to tell the server. */
447 gint get_icon_offset(PurpleConnection *gc)
448 {
449 PurpleAccount *account;
450 PurplePresence *presence;
451
452 account = purple_connection_get_account(gc);
453 presence = purple_account_get_presence(account);
454
455 if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
456 return 2;
457 } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
458 || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
459 || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
460 return 1;
439 } else { 461 } else {
440 purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies and groups received\n"); 462 return 0;
441 } 463 }
442 } 464 }
465
466 /* send a packet to change my online status */
467 void qq_send_packet_change_status(PurpleConnection *gc)
468 {
469 qq_data *qd;
470 guint8 raw_data[16] = {0};
471 gint bytes = 0;
472 guint8 away_cmd;
473 guint32 misc_status;
474 gboolean fake_video;
475 PurpleAccount *account;
476 PurplePresence *presence;
477
478 account = purple_connection_get_account(gc);
479 presence = purple_account_get_presence(account);
480
481 qd = (qq_data *) gc->proto_data;
482 if (!qd->logged_in)
483 return;
484
485 if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
486 away_cmd = QQ_BUDDY_ONLINE_INVISIBLE;
487 } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
488 || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
489 || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
490 away_cmd = QQ_BUDDY_ONLINE_AWAY;
491 } else {
492 away_cmd = QQ_BUDDY_ONLINE_NORMAL;
493 }
494
495 misc_status = 0x00000000;
496 fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video");
497 if (fake_video)
498 misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO;
499
500 bytes = 0;
501 bytes += qq_put8(raw_data + bytes, away_cmd);
502 bytes += qq_put32(raw_data + bytes, misc_status);
503
504 qq_send_cmd(qd, QQ_CMD_CHANGE_ONLINE_STATUS, raw_data, bytes);
505 }
506
507 /* parse the reply packet for change_status */
508 void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
509 {
510 qq_data *qd;
511 gint len, bytes;
512 guint8 *data, reply;
513 PurpleBuddy *b;
514 qq_buddy *q_bud;
515 gchar *name;
516
517 g_return_if_fail(buf != NULL && buf_len != 0);
518
519 qd = (qq_data *) gc->proto_data;
520 len = buf_len;
521 data = g_newa(guint8, len);
522
523 if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
524 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
525 return;
526 }
527
528 bytes = 0;
529 bytes = qq_get8(&reply, data + bytes);
530 if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
531 purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail 0x%02X\n", reply);
532 return;
533 }
534
535 /* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n"); */
536 name = uid_to_purple_name(qd->uid);
537 b = purple_find_buddy(gc->account, name);
538 g_free(name);
539 q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
540 if (q_bud != NULL) {
541 qq_update_buddy_contact(gc, q_bud);
542 }
543 }
544
545 /* it is a server message indicating that one of my buddies has changed its status */
546 void qq_process_buddy_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc)
547 {
548 qq_data *qd;
549 gint bytes;
550 guint32 my_uid;
551 guint8 *data;
552 gint data_len;
553 PurpleBuddy *b;
554 qq_buddy *q_bud;
555 qq_buddy_status bs;
556 gchar *name;
557
558 g_return_if_fail(buf != NULL && buf_len != 0);
559
560 qd = (qq_data *) gc->proto_data;
561 data_len = buf_len;
562 data = g_newa(guint8, data_len);
563
564 if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len) ) {
565 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] Failed decrypt\n");
566 return;
567 }
568
569 if (data_len < 35) {
570 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] only %d, need 35 bytes\n", data_len);
571 return;
572 }
573
574 memset(&bs, 0, sizeof(bs));
575 bytes = 0;
576 /* 000-030: qq_buddy_status */
577 bytes += get_buddy_status(&bs, data + bytes);
578 /* 031-034: Unknow, maybe my uid */
579 /* This has a value of 0 when we've changed our status to
580 * QQ_BUDDY_ONLINE_INVISIBLE */
581 bytes += qq_get32(&my_uid, data + bytes);
582
583 name = uid_to_purple_name(bs.uid);
584 b = purple_find_buddy(gc->account, name);
585 g_free(name);
586 q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
587 if (q_bud == NULL) {
588 purple_debug(PURPLE_DEBUG_ERROR, "QQ",
589 "got information of unknown buddy %d\n", bs.uid);
590 return;
591 }
592
593 purple_debug(PURPLE_DEBUG_INFO, "QQ", "status:.uid = %d, q_bud->uid = %d\n", bs.uid , q_bud->uid);
594 if(bs.ip.s_addr != 0) {
595 q_bud->ip.s_addr = bs.ip.s_addr;
596 q_bud->port = bs.port;
597 }
598 q_bud->status =bs.status;
599
600 if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) {
601 qq_send_packet_get_level(gc, q_bud->uid);
602 }
603 qq_update_buddy_contact(gc, q_bud);
604 }
605
606 /*TODO: maybe this should be qq_update_buddy_status() ?*/
607 void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud)
608 {
609 gchar *name;
610 PurpleBuddy *bud;
611 gchar *status_id;
612
613 g_return_if_fail(q_bud != NULL);
614
615 name = uid_to_purple_name(q_bud->uid);
616 bud = purple_find_buddy(gc->account, name);
617
618 if (bud == NULL) {
619 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown buddy: %d\n", q_bud->uid);
620 g_free(name);
621 return;
622 }
623
624 purple_blist_server_alias_buddy(bud, q_bud->nickname); /* server */
625 q_bud->last_refresh = time(NULL);
626
627 /* purple supports signon and idle time
628 * but it is not much use for QQ, I do not use them */
629 /* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
630 status_id = "available";
631 switch(q_bud->status) {
632 case QQ_BUDDY_OFFLINE:
633 status_id = "offline";
634 break;
635 case QQ_BUDDY_ONLINE_NORMAL:
636 status_id = "available";
637 break;
638 case QQ_BUDDY_ONLINE_OFFLINE:
639 status_id = "offline";
640 break;
641 case QQ_BUDDY_ONLINE_AWAY:
642 status_id = "away";
643 break;
644 case QQ_BUDDY_ONLINE_INVISIBLE:
645 status_id = "invisible";
646 break;
647 default:
648 status_id = "invisible";
649 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown status: %x\n", q_bud->status);
650 break;
651 }
652 purple_debug(PURPLE_DEBUG_INFO, "QQ", "buddy %d %s\n", q_bud->uid, status_id);
653 purple_prpl_got_user_status(gc->account, name, status_id, NULL);
654
655 if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE && q_bud->status != QQ_BUDDY_OFFLINE)
656 purple_prpl_got_user_status(gc->account, name, "mobile", NULL);
657 else
658 purple_prpl_got_user_status_deactive(gc->account, name, "mobile");
659
660 if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO && q_bud->status != QQ_BUDDY_OFFLINE)
661 purple_prpl_got_user_status(gc->account, name, "video", NULL);
662 else
663 purple_prpl_got_user_status_deactive(gc->account, name, "video");
664
665 g_free(name);
666 }
667
668 /* refresh all buddies online/offline,
669 * after receiving reply for get_buddies_online packet */
670 void qq_refresh_all_buddy_status(PurpleConnection *gc)
671 {
672 time_t now;
673 GList *list;
674 qq_data *qd;
675 qq_buddy *q_bud;
676
677 qd = (qq_data *) (gc->proto_data);
678 now = time(NULL);
679 list = qd->buddies;
680
681 while (list != NULL) {
682 q_bud = (qq_buddy *) list->data;
683 if (q_bud != NULL && now > q_bud->last_refresh + QQ_UPDATE_ONLINE_INTERVAL
684 && q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) {
685 q_bud->status = QQ_BUDDY_ONLINE_OFFLINE;
686 qq_update_buddy_contact(gc, q_bud);
687 }
688 list = list->next;
689 }
690 }