comparison libpurple/protocols/qq/buddy_list.c @ 24079:1bdf7b602684

2008.10.07 - ccpaging <ccpaging(at)gmail.com> * Update qun conversation * Drop group_conv.c and group_conv.h
author SHiNE CsyFeK <csyfek@gmail.com>
date Wed, 22 Oct 2008 14:58:32 +0000
parents ce94189f15ad
children 818ab62006f5
comparison
equal deleted inserted replaced
24078:0a92aa673a33 24079:1bdf7b602684
80 qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5, update_class, 0); 80 qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5, update_class, 0);
81 } 81 }
82 82
83 /* position starts with 0x0000, 83 /* position starts with 0x0000,
84 * server may return a position tag if list is too long for one packet */ 84 * server may return a position tag if list is too long for one packet */
85 void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint update_class) 85 void qq_request_get_buddies(PurpleConnection *gc, guint16 position, gint update_class)
86 { 86 {
87 qq_data *qd; 87 qq_data *qd;
88 guint8 raw_data[16] = {0}; 88 guint8 raw_data[16] = {0};
89 gint bytes = 0; 89 gint bytes = 0;
90 90
156 156
157 return bytes; 157 return bytes;
158 } 158 }
159 159
160 /* process the reply packet for get_buddies_online packet */ 160 /* process the reply packet for get_buddies_online packet */
161 guint8 qq_process_get_buddies_online_reply(guint8 *data, gint data_len, PurpleConnection *gc) 161 guint8 qq_process_get_buddies_online(guint8 *data, gint data_len, PurpleConnection *gc)
162 { 162 {
163 qq_data *qd; 163 qq_data *qd;
164 gint bytes, bytes_start; 164 gint bytes, bytes_start;
165 gint count; 165 gint count;
166 guint8 position; 166 guint8 position;
167 qq_buddy *buddy; 167 qq_buddy *buddy;
168 qq_buddy_online bo;
169 int entry_len = 38; 168 int entry_len = 38;
169
170 qq_buddy_status bs;
171 struct {
172 guint16 unknown1;
173 guint8 ext_flag;
174 guint8 comm_flag;
175 guint16 unknown2;
176 guint8 ending; /* 0x00 */
177 } packet;
170 178
171 g_return_val_if_fail(data != NULL && data_len != 0, -1); 179 g_return_val_if_fail(data != NULL && data_len != 0, -1);
172 180
173 qd = (qq_data *) gc->proto_data; 181 qd = (qq_data *) gc->proto_data;
174 182
183 if (data_len - bytes < entry_len) { 191 if (data_len - bytes < entry_len) {
184 purple_debug_error("QQ", "[buddies online] only %d, need %d\n", 192 purple_debug_error("QQ", "[buddies online] only %d, need %d\n",
185 (data_len - bytes), entry_len); 193 (data_len - bytes), entry_len);
186 break; 194 break;
187 } 195 }
188 memset(&bo, 0 ,sizeof(bo)); 196 memset(&bs, 0 ,sizeof(bs));
197 memset(&packet, 0 ,sizeof(packet));
189 198
190 /* set flag */ 199 /* set flag */
191 bytes_start = bytes; 200 bytes_start = bytes;
192 /* based on one online buddy entry */ 201 /* based on one online buddy entry */
193 /* 000-030 qq_buddy_status */ 202 /* 000-030 qq_buddy_status */
194 bytes += get_buddy_status(&(bo.bs), data + bytes); 203 bytes += get_buddy_status(&bs, data + bytes);
195 /* 031-032: */ 204 /* 031-032: */
196 bytes += qq_get16(&bo.unknown1, data + bytes); 205 bytes += qq_get16(&packet.unknown1, data + bytes);
197 /* 033-033: ext_flag */ 206 /* 033-033: ext_flag */
198 bytes += qq_get8(&bo.ext_flag, data + bytes); 207 bytes += qq_get8(&packet.ext_flag, data + bytes);
199 /* 034-034: comm_flag */ 208 /* 034-034: comm_flag */
200 bytes += qq_get8(&bo.comm_flag, data + bytes); 209 bytes += qq_get8(&packet.comm_flag, data + bytes);
201 /* 035-036: */ 210 /* 035-036: */
202 bytes += qq_get16(&bo.unknown2, data + bytes); 211 bytes += qq_get16(&packet.unknown2, data + bytes);
203 /* 037-037: */ 212 /* 037-037: */
204 bytes += qq_get8(&bo.ending, data + bytes); /* 0x00 */ 213 bytes += qq_get8(&packet.ending, data + bytes); /* 0x00 */
205 /* skip 4 bytes in qq2007 */ 214 /* skip 4 bytes in qq2007 */
206 if (qd->client_version >= 2007) bytes += 4; 215 if (qd->client_version >= 2007) bytes += 4;
207 216
208 if (bo.bs.uid == 0 || (bytes - bytes_start) != entry_len) { 217 if (bs.uid == 0 || (bytes - bytes_start) != entry_len) {
209 purple_debug_error("QQ", "uid=0 or entry complete len(%d) != %d", 218 purple_debug_error("QQ", "uid=0 or entry complete len(%d) != %d",
210 (bytes - bytes_start), entry_len); 219 (bytes - bytes_start), entry_len);
211 continue; 220 continue;
212 } /* check if it is a valid entry */ 221 } /* check if it is a valid entry */
213 222
214 if (bo.bs.uid == qd->uid) { 223 if (bs.uid == qd->uid) {
215 purple_debug_warning("QQ", "I am in online list %d\n", bo.bs.uid); 224 purple_debug_warning("QQ", "I am in online list %d\n", bs.uid);
216 } 225 }
217 226
218 /* update buddy information */ 227 /* update buddy information */
219 buddy = qq_get_buddy(gc, bo.bs.uid); 228 buddy = qq_buddy_find(gc, bs.uid);
220 if (buddy == NULL) { 229 if (buddy == NULL) {
221 purple_debug_error("QQ", 230 purple_debug_error("QQ",
222 "Got an online buddy %d, but not in my buddy list\n", bo.bs.uid); 231 "Got an online buddy %d, but not in my buddy list\n", bs.uid);
223 continue; 232 continue;
224 } 233 }
225 /* we find one and update qq_buddy */ 234 /* we find one and update qq_buddy */
226 /* 235 /*
227 if(0 != fe->s->client_tag) 236 if(0 != fe->s->client_tag)
228 q_bud->client_tag = fe->s->client_tag; 237 q_bud->client_tag = fe->s->client_tag;
229 */ 238 */
230 buddy->ip.s_addr = bo.bs.ip.s_addr; 239 buddy->ip.s_addr = bs.ip.s_addr;
231 buddy->port = bo.bs.port; 240 buddy->port = bs.port;
232 buddy->status = bo.bs.status; 241 buddy->status = bs.status;
233 buddy->ext_flag = bo.ext_flag; 242 buddy->ext_flag = packet.ext_flag;
234 buddy->comm_flag = bo.comm_flag; 243 buddy->comm_flag = packet.comm_flag;
235 qq_update_buddy_contact(gc, buddy); 244 qq_update_buddy_status(gc, bs.uid, bs.status, packet.comm_flag);
236 count++; 245 count++;
237 } 246 }
238 247
239 if(bytes > data_len) { 248 if(bytes > data_len) {
240 purple_debug_error("QQ", 249 purple_debug_error("QQ",
241 "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n"); 250 "qq_process_get_buddies_online: Dangerous error! maybe protocol changed, notify developers!\n");
242 } 251 }
243 252
244 purple_debug_info("QQ", "Received %d online buddies, nextposition=%u\n", 253 purple_debug_info("QQ", "Received %d online buddies, nextposition=%u\n",
245 count, (guint) position); 254 count, (guint) position);
246 return position; 255 return position;
247 } 256 }
248 257
249 258
250 /* process reply for get_buddies_list */ 259 /* process reply for get_buddies_list */
251 guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleConnection *gc) 260 guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc)
252 { 261 {
253 qq_data *qd; 262 qq_data *qd;
254 qq_buddy *buddy; 263 qq_buddy bd;
255 gint bytes_expected, count; 264 gint bytes_expected, count;
256 gint bytes, buddy_bytes; 265 gint bytes, buddy_bytes;
257 gint nickname_len; 266 gint nickname_len;
258 guint16 position, unknown; 267 guint16 position, unknown;
259 gchar *purple_name; 268 PurpleBuddy *buddy;
260 PurpleBuddy *purple_buddy;
261 269
262 g_return_val_if_fail(data != NULL && data_len != 0, -1); 270 g_return_val_if_fail(data != NULL && data_len != 0, -1);
263 271
264 qd = (qq_data *) gc->proto_data; 272 qd = (qq_data *) gc->proto_data;
265 273
271 bytes = 0; 279 bytes = 0;
272 bytes += qq_get16(&position, data + bytes); 280 bytes += qq_get16(&position, data + bytes);
273 /* the following data is buddy list in this packet */ 281 /* the following data is buddy list in this packet */
274 count = 0; 282 count = 0;
275 while (bytes < data_len) { 283 while (bytes < data_len) {
276 buddy = g_new0(qq_buddy, 1); 284 memset(&bd, 0, sizeof(bd));
277 /* set flag */ 285 /* set flag */
278 buddy_bytes = bytes; 286 buddy_bytes = bytes;
279 /* 000-003: uid */ 287 /* 000-003: uid */
280 bytes += qq_get32(&buddy->uid, data + bytes); 288 bytes += qq_get32(&bd.uid, data + bytes);
281 /* 004-005: icon index (1-255) */ 289 /* 004-005: icon index (1-255) */
282 bytes += qq_get16(&buddy->face, data + bytes); 290 bytes += qq_get16(&bd.face, data + bytes);
283 /* 006-006: age */ 291 /* 006-006: age */
284 bytes += qq_get8(&buddy->age, data + bytes); 292 bytes += qq_get8(&bd.age, data + bytes);
285 /* 007-007: gender */ 293 /* 007-007: gender */
286 bytes += qq_get8(&buddy->gender, data + bytes); 294 bytes += qq_get8(&bd.gender, data + bytes);
287 295
288 nickname_len = qq_get_vstr(&buddy->nickname, QQ_CHARSET_DEFAULT, data + bytes); 296 nickname_len = qq_get_vstr(&bd.nickname, QQ_CHARSET_DEFAULT, data + bytes);
289 bytes += nickname_len; 297 bytes += nickname_len;
290 qq_filter_str(buddy->nickname); 298 qq_filter_str(bd.nickname);
291 299
292 /* Fixme: merge following as 32bit flag */ 300 /* Fixme: merge following as 32bit flag */
293 bytes += qq_get16(&unknown, data + bytes); 301 bytes += qq_get16(&unknown, data + bytes);
294 bytes += qq_get8(&buddy->ext_flag, data + bytes); 302 bytes += qq_get8(&bd.ext_flag, data + bytes);
295 bytes += qq_get8(&buddy->comm_flag, data + bytes); 303 bytes += qq_get8(&bd.comm_flag, data + bytes);
296 304
297 if (qd->client_version >= 2007) { 305 if (qd->client_version >= 2007) {
298 bytes += 4; /* skip 4 bytes */ 306 bytes += 4; /* skip 4 bytes */
299 bytes_expected = 16 + nickname_len; 307 bytes_expected = 16 + nickname_len;
300 } else { 308 } else {
301 bytes_expected = 12 + nickname_len; 309 bytes_expected = 12 + nickname_len;
302 } 310 }
303 311
304 if (buddy->uid == 0 || (bytes - buddy_bytes) != bytes_expected) { 312 if (bd.uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
305 purple_debug_info("QQ", 313 purple_debug_info("QQ",
306 "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes); 314 "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes);
307 g_free(buddy->nickname); 315 g_free(bd.nickname);
308 g_free(buddy);
309 continue; 316 continue;
310 } else { 317 } else {
311 count++; 318 count++;
312 } 319 }
313 320
314 #if 1 321 #if 1
315 purple_debug_info("QQ", "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n", 322 purple_debug_info("QQ", "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
316 buddy->uid, buddy->ext_flag, buddy->comm_flag, buddy->nickname); 323 bd.uid, bd.ext_flag, bd.comm_flag, bd.nickname);
317 #endif 324 #endif
318 325
319 purple_name = uid_to_purple_name(buddy->uid); 326 buddy = qq_buddy_find_or_new(gc, bd.uid);
320 purple_buddy = purple_find_buddy(gc->account, purple_name); 327 if (buddy == NULL || buddy->proto_data == NULL) {
321 g_free(purple_name); 328 g_free(bd.nickname);
322 329 continue;
323 if (purple_buddy == NULL) { 330 }
324 purple_buddy = qq_create_buddy(gc, buddy->uid, TRUE, FALSE); 331 bd.last_update = time(NULL);
325 } 332 purple_blist_server_alias_buddy(buddy, bd.nickname);
326 333 qq_update_buddy_status(gc, bd.uid, bd.status, bd.comm_flag);
327 purple_buddy->proto_data = buddy; 334
328 qd->buddies = g_list_append(qd->buddies, buddy); 335 g_memmove(buddy->proto_data, &bd, sizeof(qq_buddy));
329 qq_update_buddy_contact(gc, buddy); 336 /* nickname has been copy to buddy_data do not free
337 g_free(bd.nickname);
338 */
330 } 339 }
331 340
332 if(bytes > data_len) { 341 if(bytes > data_len) {
333 purple_debug_error("QQ", 342 purple_debug_error("QQ",
334 "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!"); 343 "qq_process_get_buddies: Dangerous error! maybe protocol changed, notify developers!");
335 } 344 }
336 345
337 purple_debug_info("QQ", "Received %d buddies, nextposition=%u\n", 346 purple_debug_info("QQ", "Received %d buddies, nextposition=%u\n",
338 count, (guint) position); 347 count, (guint) position);
339 return position; 348 return position;
383 purple_debug_info("QQ", "Buddy entry, uid=%d, type=%d", uid, type); 392 purple_debug_info("QQ", "Buddy entry, uid=%d, type=%d", uid, type);
384 continue; 393 continue;
385 } 394 }
386 if(0x1 == type) { /* a buddy */ 395 if(0x1 == type) { /* a buddy */
387 /* don't do anything but count - buddies are handled by 396 /* don't do anything but count - buddies are handled by
388 * qq_request_get_buddies_list */ 397 * qq_request_get_buddies */
389 ++i; 398 ++i;
390 } else { /* a group */ 399 } else { /* a group */
391 group = qq_room_search_id(gc, uid); 400 group = qq_room_search_id(gc, uid);
392 if(group == NULL) { 401 if(group == NULL) {
393 purple_debug_info("QQ", 402 purple_debug_info("QQ",
419 428
420 /* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses, 429 /* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses,
421 * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy, 430 * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy,
422 * using different accounts to get info. */ 431 * using different accounts to get info. */
423 432
424 /* check if status means online or offline */
425 gboolean is_online(guint8 status)
426 {
427 switch(status) {
428 case QQ_BUDDY_ONLINE_NORMAL:
429 case QQ_BUDDY_ONLINE_AWAY:
430 case QQ_BUDDY_ONLINE_INVISIBLE:
431 case QQ_BUDDY_ONLINE_BUSY:
432 return TRUE;
433 case QQ_BUDDY_CHANGE_TO_OFFLINE:
434 return FALSE;
435 }
436 return FALSE;
437 }
438
439 /* Help calculate the correct icon index to tell the server. */ 433 /* Help calculate the correct icon index to tell the server. */
440 gint get_icon_offset(PurpleConnection *gc) 434 gint get_icon_offset(PurpleConnection *gc)
441 { 435 {
442 PurpleAccount *account; 436 PurpleAccount *account;
443 PurplePresence *presence; 437 PurplePresence *presence;
513 } 507 }
514 qq_send_cmd_mess(gc, QQ_CMD_CHANGE_STATUS, raw_data, bytes, update_class, 0); 508 qq_send_cmd_mess(gc, QQ_CMD_CHANGE_STATUS, raw_data, bytes, update_class, 0);
515 } 509 }
516 510
517 /* parse the reply packet for change_status */ 511 /* parse the reply packet for change_status */
518 void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnection *gc) 512 void qq_process_change_status(guint8 *data, gint data_len, PurpleConnection *gc)
519 { 513 {
520 qq_data *qd; 514 qq_data *qd;
521 gint bytes; 515 gint bytes;
522 guint8 reply; 516 guint8 reply;
523 qq_buddy *buddy; 517 qq_buddy *buddy;
532 purple_debug_warning("QQ", "Change status fail 0x%02X\n", reply); 526 purple_debug_warning("QQ", "Change status fail 0x%02X\n", reply);
533 return; 527 return;
534 } 528 }
535 529
536 /* purple_debug_info("QQ", "Change status OK\n"); */ 530 /* purple_debug_info("QQ", "Change status OK\n"); */
537 buddy = qq_get_buddy(gc, qd->uid); 531 buddy = qq_buddy_find(gc, qd->uid);
538 if (buddy != NULL) { 532 if (buddy != NULL) {
539 qq_update_buddy_contact(gc, buddy); 533 qq_update_buddy_contact(gc, buddy);
540 } 534 }
541 } 535 }
542 536
565 /* 031-034: Unknow, maybe my uid */ 559 /* 031-034: Unknow, maybe my uid */
566 /* This has a value of 0 when we've changed our status to 560 /* This has a value of 0 when we've changed our status to
567 * QQ_BUDDY_ONLINE_INVISIBLE */ 561 * QQ_BUDDY_ONLINE_INVISIBLE */
568 bytes += qq_get32(&my_uid, data + bytes); 562 bytes += qq_get32(&my_uid, data + bytes);
569 563
570 buddy = qq_get_buddy(gc, bs.uid); 564 buddy = qq_buddy_find(gc, bs.uid);
571 if (buddy == NULL) { 565 if (buddy == NULL) {
572 purple_debug_warning("QQ", "Get status of unknown buddy %d\n", bs.uid); 566 purple_debug_warning("QQ", "Get status of unknown buddy %d\n", bs.uid);
573 return; 567 return;
574 } 568 }
575 569
585 } else { 579 } else {
586 qq_request_get_level(gc, buddy->uid); 580 qq_request_get_level(gc, buddy->uid);
587 } 581 }
588 } 582 }
589 qq_update_buddy_contact(gc, buddy); 583 qq_update_buddy_contact(gc, buddy);
584 }
585
586 /*TODO: maybe this should be qq_update_buddy_status() ?*/
587 void qq_update_buddy_status(PurpleConnection *gc, guint32 uid, guint8 status, guint8 flag)
588 {
589 gchar *who;
590 gchar *status_id;
591
592 g_return_if_fail(uid != 0);
593
594 who = uid_to_purple_name(uid);
595
596 /* purple supports signon and idle time
597 * but it is not much use for QQ, I do not use them */
598 /* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
599 status_id = "available";
600 switch(status) {
601 case QQ_BUDDY_OFFLINE:
602 status_id = "offline";
603 break;
604 case QQ_BUDDY_ONLINE_NORMAL:
605 status_id = "available";
606 break;
607 case QQ_BUDDY_CHANGE_TO_OFFLINE:
608 status_id = "offline";
609 break;
610 case QQ_BUDDY_ONLINE_AWAY:
611 status_id = "away";
612 break;
613 case QQ_BUDDY_ONLINE_INVISIBLE:
614 status_id = "invisible";
615 break;
616 case QQ_BUDDY_ONLINE_BUSY:
617 status_id = "busy";
618 break;
619 default:
620 status_id = "invisible";
621 purple_debug_error("QQ", "unknown status: %x\n", status);
622 break;
623 }
624 purple_debug_info("QQ", "Update buddy %s status as %s\n", who, status_id);
625 purple_prpl_got_user_status(gc->account, who, status_id, NULL);
626
627 if (flag & QQ_COMM_FLAG_MOBILE && status != QQ_BUDDY_OFFLINE)
628 purple_prpl_got_user_status(gc->account, who, "mobile", NULL);
629 else
630 purple_prpl_got_user_status_deactive(gc->account, who, "mobile");
631
632 g_free(who);
590 } 633 }
591 634
592 /*TODO: maybe this should be qq_update_buddy_status() ?*/ 635 /*TODO: maybe this should be qq_update_buddy_status() ?*/
593 void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *buddy) 636 void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *buddy)
594 { 637 {
653 g_free(purple_name); 696 g_free(purple_name);
654 } 697 }
655 698
656 /* refresh all buddies online/offline, 699 /* refresh all buddies online/offline,
657 * after receiving reply for get_buddies_online packet */ 700 * after receiving reply for get_buddies_online packet */
658 void qq_refresh_all_buddy_status(PurpleConnection *gc) 701 void qq_update_buddyies_status(PurpleConnection *gc)
659 { 702 {
660 time_t now; 703 qq_data *qd;
661 GList *list; 704 PurpleBuddy *buddy;
662 qq_data *qd; 705 qq_buddy *bd;
663 qq_buddy *q_bud; 706 GSList *buddies, *it;
707 time_t tm_limit = time(NULL);
664 708
665 qd = (qq_data *) (gc->proto_data); 709 qd = (qq_data *) (gc->proto_data);
666 now = time(NULL); 710
667 list = qd->buddies; 711 tm_limit -= QQ_UPDATE_ONLINE_INTERVAL;
668 712
669 while (list != NULL) { 713 buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
670 q_bud = (qq_buddy *) list->data; 714 for (it = buddies; it; it = it->next) {
671 if (q_bud != NULL && now > q_bud->last_update + QQ_UPDATE_ONLINE_INTERVAL 715 buddy = it->data;
672 && q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) { 716 if (buddy == NULL) continue;
673 q_bud->status = QQ_BUDDY_CHANGE_TO_OFFLINE; 717 if (buddy->proto_data == NULL) continue;
674 qq_update_buddy_contact(gc, q_bud); 718
675 } 719 bd = (qq_buddy *)buddy->proto_data;
676 list = list->next; 720 if (bd->uid == 0) continue;
677 } 721 if (bd->uid == qd->uid) continue; /* my status is always online in my buddy list */
678 } 722 if (tm_limit < bd->last_update) continue;
723 if (bd->status == QQ_BUDDY_ONLINE_INVISIBLE) continue;
724
725 bd->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
726 qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
727 }
728 }
729
730 void qq_buddy_data_free_all(PurpleConnection *gc)
731 {
732 qq_data *qd;
733 PurpleBuddy *buddy;
734 GSList *buddies, *it;
735 gint count = 0;
736
737 qd = (qq_data *) (gc->proto_data);
738
739 buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
740 for (it = buddies; it; it = it->next) {
741 buddy = it->data;
742 if (buddy == NULL) continue;
743 if (buddy->proto_data == NULL) continue;
744
745 qq_buddy_data_free(buddy->proto_data);
746 buddy->proto_data = NULL;
747
748 count++;
749 }
750
751 if (count > 0) {
752 purple_debug_info("QQ", "%d buddies' data are freed\n", count);
753 }
754 }
755