Пример #1
0
/* Process the reply to group_auth subcmd */
void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc)
{
	gint bytes;
	guint32 id;
	qq_room_data *rmd;
	gchar *msg;

	g_return_if_fail(data != NULL && len > 0);

	if (len < 4) {
		purple_debug_error("QQ",
			"Invalid join room reply, expect %d bytes, read %d bytes\n", 4, len);
		return;
	}
	bytes = 0;
	bytes += qq_get32(&id, data + bytes);
	g_return_if_fail(id > 0);

	rmd = qq_room_data_find(gc, id);
	if (rmd != NULL) {
		msg = g_strdup_printf(_("Successfully joined Qun %s (%u)"), rmd->title_utf8, rmd->ext_id);
		qq_got_message(gc, msg);
		g_free(msg);
	} else {
		qq_got_message(gc, _("Successfully joined Qun"));
	}
}
Пример #2
0
void qq_room_remove(PurpleConnection *gc, guint32 id)
{
	qq_data *qd;
	PurpleChat *chat;
	qq_room_data *rmd;
	gchar *num_str;
	guint32 qun_id;

	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
	qd = (qq_data *) gc->proto_data;

	purple_debug_info("QQ", "Find and remove room data, id %u\n", id);
	rmd = qq_room_data_find(gc, id);
	g_return_if_fail (rmd != NULL);

	qun_id = rmd->qun_id;
	qd->rooms = g_slist_remove(qd->rooms, rmd);
	room_data_free(rmd);

	purple_debug_info("QQ", "Find and remove chat, qun_id %u\n", qun_id);
	num_str = g_strdup_printf("%u", qun_id);
	chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str);
	g_free(num_str);

	g_return_if_fail (chat != NULL);

	purple_blist_remove_chat(chat);
}
Пример #3
0
void qq_process_room_cmd_get_qun_list( guint8 *data, gint data_len, PurpleConnection *gc )
{
	qq_data *qd;
	qq_room_data *rmd;
	guint32 id, qun_id;
	gint bytes;
	guint8 num;
	guint8 i;

	g_return_if_fail(data != NULL && data_len > 0);
	qd = (qq_data *) gc->proto_data;

	/* qq_show_packet("Room List", data, data_len); */

	bytes = 0;
	bytes += qq_get8(&num, data);
	for (i=0; i<num; ++i)
	{	
		bytes += qq_get32(&id, data+bytes);
		bytes += qq_get32(&qun_id, data+bytes);
		bytes ++;

		rmd = qq_room_data_find(gc, id);
		g_return_if_fail(rmd != NULL);
		rmd->qun_id = qun_id;
		rmd->my_role = QQ_ROOM_ROLE_YES;
		purple_debug_info("QQ", "Qun added id: %u qun_id: %u\n",
			rmd->id, rmd->qun_id);
	}
}
Пример #4
0
PurpleChat *qq_room_find_or_new(PurpleConnection *gc, guint32 id, guint32 qun_id)
{
	qq_data *qd;
	qq_room_data *rmd;
	PurpleChat *chat;
	gchar *num_str;

	g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, NULL);
	qd = (qq_data *) gc->proto_data;

	g_return_val_if_fail(id != 0 && qun_id != 0, NULL);

	purple_debug_info("QQ", "Find or add new room: id %u, qun id %u\n", id, qun_id);

	rmd = qq_room_data_find(gc, id);
	if (rmd == NULL) {
		rmd = room_data_new(id, qun_id, NULL);
		g_return_val_if_fail(rmd != NULL, NULL);
		rmd->my_role = QQ_ROOM_ROLE_YES;
		qd->rooms = g_slist_append(qd->rooms, rmd);
	}

	num_str = g_strdup_printf("%u", qun_id);
	chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str);
	g_free(num_str);
	if (chat) {
		return chat;
	}

	return chat_new(gc, rmd);
}
Пример #5
0
/* process group cmd reply "join group" */
void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc)
{
	gint bytes;
	guint32 id;
	qq_room_data *rmd;
	gchar *msg;

	g_return_if_fail(data != NULL && len > 0);

	if (len < 4) {
		purple_debug_error("QQ",
			   "Invalid join room reply, expect %d bytes, read %d bytes\n", 5, len);
		return;
	}

	bytes = 0;
	bytes += qq_get32(&id, data + bytes);

	/* join group OK */
	rmd = qq_room_data_find(gc, id);
	/* need to check if group is NULL or not. */
	g_return_if_fail(rmd != NULL);

	purple_debug_info("QQ", "Succeeded in joining group \"%s\"\n", rmd->name);
	rmd->my_role = QQ_ROOM_ROLE_YES;
	qq_room_conv_open(gc, rmd);

	/*
	switch (reply) {
	case QQ_ROOM_JOIN_OK:
		purple_debug_info("QQ", "Succeeded in joining group \"%s\"\n", rmd->name);
		rmd->my_role = QQ_ROOM_ROLE_YES;
		qq_room_conv_open(gc, rmd);
		break;
	case QQ_ROOM_JOIN_NEED_AUTH:
		purple_debug_info("QQ",
			   "Failed to join room qun id %u %s, needs authentication\n",
			   rmd->qun_id, rmd->name);
		rmd->my_role = QQ_ROOM_ROLE_NO;
		do_room_join_request(gc, rmd);
		break;
	case QQ_ROOM_JOIN_DENIED:
		msg = g_strdup_printf(_("Qun %u denied from joining"), rmd->qun_id);
		purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), msg);
		g_free(msg);
		break;
	default:
		purple_debug_info("QQ",
			   "Failed to join room qun id %u %s, unknown reply: 0x%02x\n",
			   rmd->qun_id, rmd->name, reply);

		purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), _("Join Qun, Unknown Reply"));
	}	*/
}
Пример #6
0
/* send packet to get info for each group member */
gint qq_request_room_get_members_info( PurpleConnection *gc, guint32 room_id, guint32 update_class, guint32 index )
{
	guint8 *raw_data;
	gint bytes, num;
	GList *list;
	qq_room_data *rmd;
	qq_buddy_data *bd;
	guint32 i = 0;

	g_return_val_if_fail(room_id > 0, 0);

	rmd  = qq_room_data_find(gc, room_id);
	g_return_val_if_fail(rmd != NULL, 0);

	for (num = 0, list = rmd->members; list != NULL; list = list->next) {
		bd = (qq_buddy_data *) list->data;
		if (check_update_interval(bd))
			num++;
	}

	if (num <= 0) {
		purple_debug_info("QQ", "No group member info needs to be updated now.\n");
		return 0;
	}

	raw_data = g_newa(guint8, 4 * num);

	bytes = 0;

	list = rmd->members;
	/* index shipped from last request 
		send 30 uids one time	*/
	while (list != NULL) {
		if (i>=index)
		{
			bd = (qq_buddy_data *) list->data;
			if (check_update_interval(bd))
				bytes += qq_put32(raw_data + bytes, bd->uid);
		}
		i++;
		if (i==index+30) break;
		list = list->next;
	}
	/* if reach the end */
	if (list == NULL)	i=0;

	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_MEMBERS_INFO, rmd->id, raw_data, bytes,
			update_class, i);
	return num;
}
Пример #7
0
void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc)
{
	guint32 room_id, member_uid;
	guint8 unknown;
	gint bytes, num;
	qq_room_data *rmd;
	qq_buddy_data *bd;

	g_return_if_fail(data != NULL && len > 0);

	if (len <= 3) {
		purple_debug_error("QQ", "Invalid group online member reply, discard it!\n");
		return;
	}

	bytes = 0;
	bytes += qq_get32(&room_id, data + bytes);
	bytes += qq_get8(&unknown, data + bytes);	/* 0x3c ?? */
	g_return_if_fail(room_id > 0);

	rmd = qq_room_data_find(gc, room_id);
	if (rmd == NULL) {
		purple_debug_error("QQ", "Can not info of room id [%u]\n", room_id);
		return;
	}

	/* set all offline first, then update those online */
	set_all_offline(rmd);
	num = 0;
	while (bytes < len) {
		bytes += qq_get32(&member_uid, data + bytes);
		num++;
		bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
		if (bd != NULL)
			bd->status = QQ_BUDDY_ONLINE_NORMAL;
	}
	if(bytes > len) {
		purple_debug_error("QQ",
			"group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
	}

	purple_debug_info("QQ", "Group \"%s\" has %d online members\n", rmd->name, num);
	qq_room_conv_set_onlines(gc, rmd);
}
Пример #8
0
static void group_join_cb(qq_room_req *add_req, const gchar *reason_utf8)
{
	qq_room_data *rmd;

	g_return_if_fail(add_req != NULL);
	if (add_req->gc == NULL || add_req->id == 0) {
		g_free(add_req);
		return;
	}

	rmd = qq_room_data_find(add_req->gc, add_req->id);
	if (rmd == NULL) {
		purple_debug_error("QQ", "Can not find room data of %u\n", add_req->id);
		g_free(add_req);
		return;
	}

	qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8);
	g_free(add_req);
}
Пример #9
0
void qq_room_got_chat_in(PurpleConnection *gc,
		guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time)
{
	PurpleConversation *conv;
	qq_data *qd;
	qq_buddy_data *bd;
	qq_room_data *rmd;
	gchar *from;

	g_return_if_fail(gc != NULL && room_id != 0);
	g_return_if_fail(msg != NULL);

	qd = (qq_data *)gc->proto_data;
	conv = purple_find_chat(gc, room_id);
	rmd = qq_room_data_find(gc, room_id);
	g_return_if_fail(rmd != NULL);

	purple_debug_info("QQ", "is_show_chat:%d\n", qd->is_show_chat);
	if (NULL == conv && qd->is_show_chat) {
		conv = qq_room_conv_open(gc, rmd);
	}

	if (NULL == conv) {
		purple_debug_info("QQ", "Conversion of %u is not open, missing from %d:/n%s/v\n",
				room_id, uid_from, msg);
		return;
	}

	if (uid_from != 0) {

		bd = qq_room_buddy_find(rmd, uid_from);
		if (bd == NULL || bd->nickname == NULL)
			from = g_strdup_printf("%u", uid_from);
		else
			from = g_strdup(bd->nickname);
	} else {
		from = g_strdup("");
	}
	serv_got_chat_in(gc, room_id, from, 0, msg, in_time);
	g_free(from);
}
Пример #10
0
static void group_quit_cb(qq_room_req *add_req)
{
	PurpleConnection *gc;
	guint32 id;
	qq_room_data *rmd;

	if (add_req->gc == NULL || add_req->id == 0) {
		g_free(add_req);
		return;
	}

	gc = add_req->gc;
	id = add_req->id;

	rmd = qq_room_data_find(gc, id);
	if (rmd == NULL) {
		g_free(add_req);
		return;
	}

	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_QUIT, rmd->id);
	g_free(add_req);
}
Пример #11
0
static void action_show_chat(PurpleBlistNode * node, gpointer flag)
{
	PurpleChat *chat = (PurpleChat *)node;
	PurpleAccount *account = purple_chat_get_account(chat);
	PurpleConnection *gc = purple_account_get_connection(account);
	GHashTable *components = purple_chat_get_components(chat);
	gchar *num_str;
	guint32 room_id;
	qq_room_data *rmd;

	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));

	g_return_if_fail(components != NULL);

	num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID);
	if (!num_str)
	{
		purple_debug_error("QQ", "Cannot find Room! Wait and Retry");
		return;
	}
	room_id = strtoul(num_str, NULL, 10);
	g_return_if_fail(room_id != 0);

	rmd = qq_room_data_find(gc, room_id);
	g_return_if_fail(rmd != NULL);
	
	if (flag)
	{
		rmd->is_show_chat = TRUE;
		g_hash_table_replace(components, g_strdup(QQ_ROOM_KEY_ISSHOW), g_strdup_printf("%u", TRUE));
		purple_notify_info(gc, _("QQ Chat Room"), _("Receive and Show QQ Chat Room Message"), num_str);
	} else {
		rmd->is_show_chat = FALSE;
		g_hash_table_replace(components, g_strdup(QQ_ROOM_KEY_ISSHOW), g_strdup_printf("%u", FALSE));
		purple_notify_info(gc, _("QQ Chat Room"), _("QQ Chat Room Message Blocked"), num_str);
	}
}
Пример #12
0
/* Attempt to join a group without auth */
void qq_group_join(PurpleConnection *gc, GHashTable *data)
{
	gchar *ext_id_str;
	gchar *id_str;
	guint32 ext_id;
	guint32 id;
	qq_room_data *rmd;

	g_return_if_fail(data != NULL);

	ext_id_str = g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
	id_str = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
	purple_debug_info("QQ", "Join room %s, extend id %s\n", id_str, ext_id_str);

	if (id_str != NULL) {
		id = strtoul(id_str, NULL, 10);
		if (id != 0) {
			rmd = qq_room_data_find(gc, id);
			if (rmd) {
				qq_request_room_join(gc, rmd);
				return;
			}
		}
	}

	purple_debug_info("QQ", "Search and join extend id %s\n", ext_id_str);
	if (ext_id_str == NULL) {
		return;
	}
	ext_id = strtoul(ext_id_str, NULL, 10);
	if (ext_id == 0) {
		return;
	}

	qq_request_room_search(gc, ext_id, QQ_ROOM_SEARCH_FOR_JOIN);
}
Пример #13
0
void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq,
		guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
		guint32 update_class, guintptr ship_value)
{
	qq_data *qd;
	guint8 *data;
	gint data_len;
	qq_room_data *rmd;
	gint bytes;
	guint8 reply_cmd, reply;

	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
	qd = (qq_data *) gc->proto_data;

	data = g_newa(guint8, rcved_len);
	data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key);
	if (data_len < 0) {
		purple_debug_warning("QQ",
			"Can not decrypt room cmd by session key, [%05d], 0x%02X %s for %d, len %d\n",
			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
		qq_show_packet("Can not decrypted", rcved, rcved_len);
		return;
	}

	if (room_id <= 0) {
		purple_debug_warning("QQ",
			"room id is 0, [%05d], 0x%02X %s for %d, len %d\n",
			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
		/* Some room cmd has no room id, like QQ_ROOM_CMD_SEARCH */
	}

	if (data_len <= 2) {
		purple_debug_warning("QQ",
			"Invaild len of room cmd decrypted, [%05d], 0x%02X %s for %d, len %d\n",
			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
		return;
	}

	bytes = 0;
	bytes += qq_get8(&reply_cmd, data + bytes);
	bytes += qq_get8(&reply, data + bytes);

	if (reply_cmd != room_cmd) {
		purple_debug_warning("QQ",
			"Missing room cmd in reply 0x%02X %s, [%05d], 0x%02X %s for %d, len %d\n",
			reply_cmd, qq_get_room_cmd_desc(reply_cmd),
			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
	}

	/* now process the packet */
	if (reply != QQ_ROOM_CMD_REPLY_OK) {
		switch (reply) {	/* this should be all errors */
		case QQ_ROOM_CMD_REPLY_NOT_MEMBER:
			rmd = qq_room_data_find(gc, room_id);
			if (rmd == NULL) {
				purple_debug_warning("QQ",
						"Missing room id in [%05d], 0x%02X %s for %d, len %d\n",
						seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
			} else {
				purple_debug_warning("QQ",
					   "Not a member of room \"%s\"\n", rmd->name);
				rmd->my_role = QQ_ROOM_ROLE_NO;
			}
			break;
		case QQ_ROOM_CMD_REPLY_SEARCH_ERROR:
			if (qd->roomlist != NULL) {
				if (purple_roomlist_get_in_progress(qd->roomlist))
					purple_roomlist_set_in_progress(qd->roomlist, FALSE);
			}
		default:
			process_room_cmd_notify(gc, reply_cmd, room_id, reply, data + bytes, data_len - bytes);
		}
		return;
	}

	/* seems ok so far, so we process the reply according to sub_cmd */
	switch (reply_cmd) {
	case QQ_ROOM_CMD_GET_QUN_LIST:
		qq_process_room_cmd_get_qun_list(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_GET_INFO:
		qq_process_room_cmd_get_info(data + bytes, data_len - bytes, ship_value, gc);
		break;
	case QQ_ROOM_CMD_CREATE:
		qq_group_process_create_group_reply(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_CHANGE_INFO:
		qq_group_process_modify_info_reply(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_MEMBER_OPT:
		qq_group_process_modify_members_reply(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_ACTIVATE:
		qq_group_process_activate_group_reply(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_SEARCH:
		qq_process_room_search(gc, data + bytes, data_len - bytes, ship_value);
		break;
	case QQ_ROOM_CMD_JOIN:
		qq_process_group_cmd_join_group(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_AUTH:
		qq_process_group_cmd_join_group_auth(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_QUIT:
		qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_SEND_IM:
		qq_process_room_send_im(gc, data + bytes, data_len - bytes);
		break;
	case QQ_ROOM_CMD_GET_ONLINES:
		qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc);
		break;
	case QQ_ROOM_CMD_GET_MEMBERS_INFO:
		qq_process_room_cmd_get_members_info(data + bytes, data_len - bytes, ship_value, gc);
		break;
	default:
		purple_debug_warning("QQ", "Unknown room cmd 0x%02X %s\n",
			   reply_cmd, qq_get_room_cmd_desc(reply_cmd));
	}

	if (update_class == QQ_CMD_CLASS_NONE)
		return;

	if (update_class == QQ_CMD_CLASS_UPDATE_ALL) {
		qq_update_all_rooms(gc, room_cmd, room_id);
		return;
	}
	if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) {
		update_all_rooms_online(gc, room_cmd, room_id);
		return;
	}
	if (update_class == QQ_CMD_CLASS_UPDATE_ROOM) {
		qq_update_room(gc, room_cmd, room_id);
	}
}
Пример #14
0
/* process the reply to get_members_info packet */
void qq_process_room_cmd_get_members_info( guint8 *data, gint len, guint32 index, PurpleConnection *gc )
{
	gint bytes;
	gint num;
	guint32 id, member_uid;
	guint16 unknown;
	qq_room_data *rmd;
	qq_buddy_data *bd;
	gchar *nick;

	g_return_if_fail(data != NULL && len > 0);

	/* qq_show_packet("qq_process_room_cmd_get_members_info", data, len); */

	bytes = 0;
	bytes += qq_get32(&id, data + bytes);
	g_return_if_fail(id > 0);

	rmd = qq_room_data_find(gc, id);
	g_return_if_fail(rmd != NULL);

	num = 0;

	while (bytes < len) {
		bytes += qq_get32(&member_uid, data + bytes);
		g_return_if_fail(member_uid > 0);
		bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
		g_return_if_fail(bd != NULL);

		num++;
		bytes += qq_get16(&(bd->face), data + bytes);
		bytes += qq_get8(&(bd->age), data + bytes);
		bytes += qq_get8(&(bd->gender), data + bytes);
		/* only here use old charset GB18030 */
		bytes += qq_get_vstr(&nick, QQ_CHARSET_DEFAULT, sizeof(guint8), data + bytes);
		bytes += qq_get16(&unknown, data + bytes);
		bytes += qq_get8(&(bd->ext_flag), data + bytes);
		bytes += qq_get8(&(bd->comm_flag), data + bytes);

		qq_filter_str(nick);
		bd->nickname = g_strdup(nick);
		g_free(nick);

#if 0
		purple_debug_info("QQ",
				"member [%d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
				member_uid, bd->ext_flag, bd->comm_flag, bd->nickname);
#endif

		bd->last_update = time(NULL);
	}
	if (bytes > len) {
		purple_debug_error("QQ",
				"group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
	}
	purple_debug_info("QQ", "Group \"%s\" got %d member info\n", rmd->name, num);

	if (index)
	{
		qq_request_room_get_members_info(gc, id, 0, index);
		return;
	}
	rmd->has_got_members_info = TRUE;
	qq_room_conv_set_onlines(gc, rmd);
}
Пример #15
0
void qq_process_room_cmd_get_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc)
{
	qq_data *qd;
	qq_room_data *rmd;
	qq_buddy_data *bd;
	PurpleChat *chat;
	PurpleConversation *conv;
	guint8 organization, role;
	guint16 max_members;
	guint32 resend_flag, member_uid, id, qun_id, last_uid;
	gint bytes; 
	guint num=0;
	guint8 has_more=0;
	gchar *topic;

	g_return_if_fail(data != NULL && data_len > 0);
	qd = (qq_data *) gc->proto_data;

	/* qq_show_packet("Room Info", data, data_len); */

	bytes = 0;
	bytes += qq_get32(&id, data + bytes);
	g_return_if_fail(id > 0);

	bytes += qq_get32(&qun_id, data + bytes);
	g_return_if_fail(qun_id > 0);

	chat = qq_room_find_or_new(gc, id, qun_id);
	g_return_if_fail(chat != NULL);
	rmd = qq_room_data_find(gc, id);
	g_return_if_fail(rmd != NULL);

	bytes += qq_get32(&resend_flag, data + bytes);		//first 00 00 00 03, then 00 00 00 02

	if (resend_flag == 0x00000003)
	{
		bytes += qq_get8(&(rmd->type8), data + bytes);
		bytes += 4;	//maybe vip sign
		bytes += qq_get32(&(rmd->creator_uid), data + bytes);
		if (rmd->creator_uid == qd->uid)
			rmd->my_role = QQ_ROOM_ROLE_ADMIN;
		bytes += qq_get8(&(rmd->auth_type), data + bytes);
		bytes += 4	;	/* oldCategory */
		bytes += 2;	// 00 00
		bytes += qq_get32(&(rmd->category), data + bytes);
		bytes += qq_get16(&max_members, data + bytes);
		bytes += 1; 
		bytes += 8;
		purple_debug_info("QQ", "type: %u creator: %u category: %u max_members: %u\n",
			rmd->type8, rmd->creator_uid, rmd->category, max_members);

		bytes += qq_get_vstr(&(rmd->name), NULL, sizeof(guint8), data + bytes);
		bytes += 2;	/* 0x0000 */
		bytes += qq_get_vstr(&(rmd->bulletin), NULL, sizeof(guint8), data + bytes);
		bytes += qq_get_vstr(&(rmd->intro), NULL, sizeof(guint8), data + bytes);
		bytes += qq_get_vstr(&(rmd->token), NULL, sizeof(guint16), data + bytes);
		purple_debug_info("QQ", "room [%s] bulletin [%s] intro [%s] \n",
			rmd->name, rmd->bulletin, rmd->intro);
		bytes += 2;		//Unknown
		bytes += qq_get32(&last_uid, data + bytes);	/* last_uid of this recv, request more with it */
		bytes += qq_get8(&has_more, data + bytes);	/* if there are more, request again */
		/* now comes the member list separated by 0x00 */
	} else {
		/* resend_flag 00 00 00 02 is special, start with random one only 5 bytes */
		bytes += qq_get32(&member_uid, data + bytes);
		num++;
		bytes += qq_get8(&organization, data + bytes);
		bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
	}


	while (bytes < data_len) {
		bytes += qq_get32(&member_uid, data + bytes);
		num++;
		bytes += qq_get8(&organization, data + bytes);
		bytes += qq_get8(&role, data + bytes);

#if 0
		if(organization != 0 || role != 0) {
			purple_debug_info("QQ", "%u, organization=%d, role=%d\n", member_uid, organization, role);
		}
#endif
		
		bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
		if (bd != NULL)
			bd->role = role;
	}

	purple_debug_info("QQ", "Qun \"%s\" has received %d members\n", rmd->name, num);

	if (has_more)
	{
		qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, id, NULL, 0,
			0, last_uid);
	} else {
		qq_room_update_chat_info(chat, rmd);
		if (action == QQ_ROOM_INFO_DISPLAY) {
			room_info_display(gc, rmd);
		}

		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
			rmd->name, purple_connection_get_account(gc));
		if(NULL == conv) {
			purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", rmd->name);
			return;
		}

		topic = g_strdup_printf("%u %s", rmd->qun_id, rmd->bulletin);
		purple_debug_info("QQ", "Set chat topic to %s\n", topic);
		purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic);
		g_free(topic);
	}
}