/* send IM to a group */ void qq_send_packet_group_im(GaimConnection *gc, qq_group *group, const gchar *msg) { gint data_len, bytes; guint8 *raw_data, *cursor, *send_im_tail; guint16 msg_len; gchar *msg_filtered; g_return_if_fail(group != NULL && msg != NULL); msg_filtered = gaim_markup_strip_html(msg); msg_len = strlen(msg_filtered); data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN; raw_data = g_newa(guint8, data_len); cursor = raw_data; bytes = 0; bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEND_MSG); bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); bytes += create_packet_w(raw_data, &cursor, msg_len + QQ_SEND_IM_AFTER_MSG_LEN); bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len); send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL, FALSE, FALSE, FALSE, QQ_SEND_IM_AFTER_MSG_LEN); bytes += create_packet_data(raw_data, &cursor, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN); g_free(send_im_tail); g_free(msg_filtered); if (bytes == data_len) /* create OK */ qq_send_group_cmd(gc, group, raw_data, data_len); else gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes); }
/* get all list, buddies & Quns with groupsid support */ void qq_send_packet_get_all_list_with_group(GaimConnection *gc, guint32 position) { guint8 *raw_data, *cursor; gint data_len; data_len = 10; raw_data = g_newa(guint8, data_len); cursor = raw_data; /* 0x01 download, 0x02, upload */ create_packet_b(raw_data, &cursor, 0x01); /* unknown 0x02 */ create_packet_b(raw_data, &cursor, 0x02); /* unknown 00 00 00 00 */ create_packet_dw(raw_data, &cursor, 0x00000000); create_packet_dw(raw_data, &cursor, position); qq_send_cmd(gc, QQ_CMD_GET_ALL_LIST_WITH_GROUP, TRUE, 0, TRUE, raw_data, data_len); }
/* send login packet to QQ server */ static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guint8 *token) { qq_data *qd; guint8 *buf, *cursor, *raw_data, *encrypted_data; guint16 seq_ret; gint encrypted_len, bytes; gint pos; qd = (qq_data *) gc->proto_data; buf = g_newa(guint8, MAX_PACKET_SIZE); raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH); encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16); /* 16 bytes more */ qd->inikey = _gen_login_key(); /* now generate the encrypted data * 000-015 use pwkey as key to encrypt empty string */ qq_crypt(ENCRYPT, (guint8 *) "", 0, qd->pwkey, raw_data, &encrypted_len); /* 016-016 */ raw_data[16] = 0x00; /* 017-020, used to be IP, now zero */ *((guint32 *) (raw_data + 17)) = 0x00000000; /* 021-022, used to be port, now zero */ *((guint16 *) (raw_data + 21)) = 0x0000; /* 023-051, fixed value, unknown */ g_memmove(raw_data + 23, login_23_51, 29); /* 052-052, login mode */ raw_data[52] = qd->login_mode; /* 053-068, fixed value, maybe related to per machine */ g_memmove(raw_data + 53, login_53_68, 16); /* 069, login token length */ raw_data[69] = token_length; pos = 70; /* 070-093, login token, normally 24 bytes */ g_memmove(raw_data + pos, token, token_length); pos += token_length; /* 100 bytes unknown */ g_memmove(raw_data + pos, login_100_bytes, 100); pos += 100; /* all zero left */ memset(raw_data+pos, 0, QQ_LOGIN_DATA_LENGTH - pos); qq_crypt(ENCRYPT, raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len); cursor = buf; bytes = 0; bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_LOGIN, TRUE, &seq_ret); bytes += create_packet_dw(buf, &cursor, qd->uid); bytes += create_packet_data(buf, &cursor, qd->inikey, QQ_KEY_LENGTH); bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len); bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL); if (bytes == (cursor - buf)) /* packet creation OK */ _qq_send_packet(gc, buf, bytes, QQ_CMD_LOGIN); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create login packet\n"); }
/* request before login */ void qq_send_packet_request_login_token(PurpleConnection *gc) { qq_data *qd; guint8 *buf, *cursor; guint16 seq_ret; gint bytes; qd = (qq_data *) gc->proto_data; buf = g_newa(guint8, MAX_PACKET_SIZE); cursor = buf; bytes = 0; bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_REQUEST_LOGIN_TOKEN, TRUE, &seq_ret); bytes += create_packet_dw(buf, &cursor, qd->uid); bytes += create_packet_b(buf, &cursor, 0); bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL); if (bytes == (cursor - buf)) /* packet creation OK */ _qq_send_packet(gc, buf, bytes, QQ_CMD_REQUEST_LOGIN_TOKEN); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create request login token packet\n"); }
/* get a list of online_buddies */ void qq_send_packet_get_buddies_online(GaimConnection *gc, guint8 position) { qq_data *qd; guint8 *raw_data, *cursor; qd = (qq_data *) gc->proto_data; raw_data = g_newa(guint8, 5); cursor = raw_data; /* 000-000 get online friends cmd * only 0x02 and 0x03 returns info from server, other valuse all return 0xff * I can also only send the first byte (0x02, or 0x03) * and the result is the same */ create_packet_b(raw_data, &cursor, QQ_GET_ONLINE_BUDDY_02); /* 001-001 seems it supports 255 online buddies at most */ create_packet_b(raw_data, &cursor, position); /* 002-002 */ create_packet_b(raw_data, &cursor, 0x00); /* 003-004 */ create_packet_w(raw_data, &cursor, 0x0000); qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_ONLINE, TRUE, 0, TRUE, raw_data, 5); qd->last_get_online = time(NULL); }
/* position starts with 0x0000, * server may return a position tag if list is too long for one packet */ void qq_send_packet_get_buddies_list(GaimConnection *gc, guint16 position) { guint8 *raw_data, *cursor; gint data_len; data_len = 3; raw_data = g_newa(guint8, data_len); cursor = raw_data; /* 000-001 starting position, can manually specify */ create_packet_w(raw_data, &cursor, position); /* before Mar 18, 2004, any value can work, and we sent 00 * I do not know what data QQ server is expecting, as QQ2003iii 0304 itself * even can sending packets 00 and get no response. * Now I tested that 00,00,00,00,00,01 work perfectly * March 22, found the 00,00,00 starts to work as well */ create_packet_b(raw_data, &cursor, 0x00); qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_LIST, TRUE, 0, TRUE, raw_data, data_len); }