示例#1
0
void qq_process_search_uid( PurpleConnection *gc, guint8 *data, gint data_len, qq_buddy_opt_req *opt_req )
{
	gint bytes;
	guint32 uid;
	guint8 status;
	gchar * name;
	guint16 icon;

	g_return_if_fail(data != NULL && data_len != 0);
	g_return_if_fail(opt_req && opt_req->uid != 0);

	//qq_show_packet("qq_process_search_uid", data, data_len);
	bytes = 7;
	bytes += qq_get32(&uid, data + bytes);
	g_return_if_fail(uid == opt_req->uid);

	bytes ++;
	bytes += qq_get8(&status, data + bytes);

	bytes += 4;
	bytes += qq_get_vstr(&name, NULL, sizeof(guint8), data + bytes);
	bytes += qq_get16(&icon, data + bytes);

	bytes += 13;
	bytes += qq_get16(&opt_req->no_auth_len, data + bytes);
	if (opt_req->no_auth)
	{
		opt_req->no_auth = g_new0(guint8, opt_req->no_auth_len);
		bytes += qq_getdata(opt_req->no_auth, opt_req->no_auth_len, data + bytes);
	}

	qq_request_add_buddy_post(gc, opt_req, NULL);
}
示例#2
0
void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_len)
{
	qq_data *qd;
	gint bytes;
	guint8 cmd;
	guint8 reply;
	guint32 uid;
	guint16 flag1, flag2;

	g_return_if_fail(data != NULL && data_len >= 5);

	qd = (qq_data *) gc->proto_data;

	qq_show_packet("buddy_check_code", data, data_len);

	bytes = 0;
	bytes += qq_get8(&cmd, data + bytes);		/* 0x03 */
	bytes += qq_get8(&reply, data + bytes);

	if (reply == 0) {
		purple_debug_info("QQ", "Failed checking code\n");
		return;
	}

	bytes += qq_get32(&uid, data + bytes);
	g_return_if_fail(uid != 0);
	bytes += qq_get16(&flag1, data + bytes);
	bytes += qq_get16(&flag2, data + bytes);
	purple_debug_info("QQ", "Check code reply Ok, uid %u, flag 0x%04X-0x%04X\n",
			uid, flag1, flag2);
	return;
}
示例#3
0
static void process_level(PurpleConnection *gc, guint8 *data, gint data_len)
{
	gint bytes = 0;
	guint32 uid, onlineTime;
	guint16 level, timeRemainder;
	qq_buddy_data *bd;

	while (data_len - bytes >= 12) {
		bytes += qq_get32(&uid, data + bytes);
		bytes += qq_get32(&onlineTime, data + bytes);
		bytes += qq_get16(&level, data + bytes);
		bytes += qq_get16(&timeRemainder, data + bytes);
		purple_debug_info("QQ", "level: %d, uid %u, tmOnline: %d, tmRemainder: %d\n",
				level, uid, onlineTime, timeRemainder);

		bd = qq_buddy_data_find(gc, uid);
		if (bd == NULL) {
			purple_debug_error("QQ", "Got levels of %u not in my buddy list\n", uid);
			continue;
		}

		bd->onlineTime = onlineTime;
		bd->level = level;
		bd->timeRemainder = timeRemainder;
	}

	if (bytes != data_len) {
		purple_debug_error("QQ",
				"Wrong format of Get levels. Truncate %d bytes.\n", data_len - bytes);
	}
}
示例#4
0
void qq_process_auth_token( PurpleConnection *gc, guint8 *data, gint data_len, guint32 dataptr, qq_buddy_opt_req *opt_req )
{
	gint bytes;
	guint8 cmd, reply;
	guint16 sub_cmd;
	guint8 *code = NULL;
	guint16 code_len = 0;

	g_return_if_fail(data != NULL && data_len != 0);
	g_return_if_fail(opt_req && opt_req->uid != 0);

	//qq_show_packet("qq_process_auth_token", data, data_len);
	bytes = 0;
	bytes += qq_get8(&cmd, data + bytes);
	bytes += qq_get16(&sub_cmd, data + bytes);
	bytes += qq_get8(&reply, data + bytes);

	/* if reply == 0x01, we need request captcha */
	if (reply)
	{
		/* if this is end, means you have submitted the wrong captcha  */
		if (bytes>=data_len)
		{
			qq_request_auth_token(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_ADD_BUDDY, 0, opt_req);
			return;
		}

		bytes += qq_get_vstr(&code, NULL, sizeof(guint16), data + bytes);
		purple_util_fetch_url_request(
			(gchar *)code, TRUE, NULL, TRUE,  NULL, TRUE, auth_token_captcha_input_cb, opt_req);
		return;
	}
	
	bytes += qq_get16(&opt_req->auth_len, data + bytes);
	g_return_if_fail(opt_req->auth_len > 0);
	g_return_if_fail(bytes + opt_req->auth_len <= data_len);
	opt_req->auth = g_new0(guint8, opt_req->auth_len);
	bytes += qq_getdata(opt_req->auth, opt_req->auth_len, data + bytes);

	if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_REMOVE_BUDDY) {
		qq_request_remove_buddy(gc, opt_req);
		return;
	}
	if (sub_cmd == QQ_AUTH_INFO_ADD_BUDDY) {
		if (opt_req->auth_type == 0x01)
			add_buddy_authorize_input(gc, opt_req);
		else if (opt_req->auth_type == 0x00)
			qq_request_search_uid(gc, opt_req);
		return;
	}
	if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_UPDATE_BUDDY_INFO) {
		request_change_info(gc, (guint8 *)dataptr, code, code_len);
		return;
	}
	purple_debug_info("QQ", "Got auth info cmd 0x%x, sub 0x%x, reply 0x%x\n",
			cmd, sub_cmd, reply);
}
示例#5
0
/* this header is NOT tcp header, it is QQ package header.
 * combines header_tag, source_tag, cmd and seq */
static gint packet_get_header(guint8 *header_tag,  guint16 *source_tag,
	guint16 *cmd, guint16 *seq, guint8 *buf)
{
	gint bytes = 0;
	bytes += qq_get8(header_tag, buf + bytes);
	bytes += qq_get16(source_tag, buf + bytes);
	bytes += qq_get16(cmd, buf + bytes);
	bytes += qq_get16(seq, buf + bytes);
	return bytes;
}
示例#6
0
文件: im.c 项目: Mons/libpurple-mini
/* read the common parts of the normal_im,
 * returns the bytes read if succeed, or -1 if there is any error */
static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len)
{
	gint bytes;
	g_return_val_if_fail(data != NULL && len > 0, -1);

	bytes = 0;
	bytes += qq_get16(&(im_header->version_from), data + bytes);
	bytes += qq_get32(&(im_header->uid_from), data + bytes);
	bytes += qq_get32(&(im_header->uid_to), data + bytes);
	bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes);
	bytes += qq_get16(&(im_header->im_type), data + bytes);
	return bytes;
}
示例#7
0
static gint server_buddy_check_code(PurpleConnection *gc,
		gchar *from, guint8 *data, gint data_len)
{
	gint bytes;
	guint16 code_len;
	guint8 *code;

	g_return_val_if_fail(data != NULL && data_len > 0, 0);

	bytes = 0;
	bytes += qq_get16(&code_len, data + bytes);
	if (code_len <= 0) {
		purple_debug_info("QQ", "Server msg for buddy has no code\n");
		return bytes;
	}
	if (bytes + code_len < data_len) {
		purple_debug_error("QQ", "Code len error in server msg for buddy\n");
		qq_show_packet("server_buddy_check_code", data, data_len);
		code_len = data_len - bytes;
	}
	code = g_newa(guint8, code_len);
	bytes += qq_getdata(code, code_len, data + bytes);

	request_buddy_check_code(gc, from, code, code_len);
	return bytes;
}
示例#8
0
文件: im.c 项目: Mons/libpurple-mini
/* data includes text msg and font attr*/
gint qq_get_im_tail(qq_im_format *fmt, guint8 *data, gint data_len)
{
	gint bytes, text_len;
	guint8 tail_len;
	guint8 font_len;

	g_return_val_if_fail(fmt != NULL && data != NULL, 0);
	g_return_val_if_fail(data_len > 1, 0);
	tail_len = data[data_len - 1];
	g_return_val_if_fail(tail_len > 2, 0);
	text_len = data_len - tail_len;
	g_return_val_if_fail(text_len >= 0, 0);

	bytes = text_len;
	/* qq_show_packet("IM tail", data + bytes, tail_len); */
	bytes += 1;		/* skip 0x00 */
	bytes += qq_get8(&fmt->attr, data + bytes);
	bytes += qq_getdata(fmt->rgb, sizeof(fmt->rgb), data + bytes);	/* red,green,blue */
 	bytes += 1;	/* skip 0x00 */
	bytes += qq_get16(&fmt->charset, data + bytes);

	font_len = data_len - bytes - 1;
	g_return_val_if_fail(font_len > 0, bytes + 1);

	fmt->font_len = font_len;
	if (fmt->font != NULL)	g_free(fmt->font);
	fmt->font = g_strndup((gchar *)data + bytes, fmt->font_len);
	return tail_len;
}
示例#9
0
static void process_level_2007(PurpleConnection *gc, guint8 *data, gint data_len)
{
	gint bytes;
	guint32 uid, onlineTime;
	guint16 level, timeRemainder;
	qq_buddy_data *bd;
	guint16 str_len;
	gchar *str;
	gchar *str_utf8;

	bytes = 0;
	bytes += qq_get32(&uid, data + bytes);
	bytes += qq_get32(&onlineTime, data + bytes);
	bytes += qq_get16(&level, data + bytes);
	bytes += qq_get16(&timeRemainder, data + bytes);
	purple_debug_info("QQ", "level: %d, uid %u, tmOnline: %d, tmRemainder: %d\n",
			level, uid, onlineTime, timeRemainder);

	bd = qq_buddy_data_find(gc, uid);
	if (bd == NULL) {
		purple_debug_error("QQ", "Got levels of %u not in my buddy list\n", uid);
		return;
	}

	bd->onlineTime = onlineTime;
	bd->level = level;
	bd->timeRemainder = timeRemainder;

	/* extend bytes in qq2007*/
	bytes += 4;	/* skip 8 bytes */
	/* qq_show_packet("Buddies level", data + bytes, data_len - bytes); */

	do {
		bytes += qq_get16(&str_len, data + bytes);
		if (str_len <= 0 || bytes + str_len > data_len) {
			purple_debug_error("QQ",
					"Wrong format of Get levels. Truncate %d bytes.\n", data_len - bytes);
			break;
		}
		str = g_strndup((gchar *)data + bytes, str_len);
		bytes += str_len;
		str_utf8 = qq_to_utf8(str, QQ_CHARSET_DEFAULT);
		purple_debug_info("QQ", "%s\n", str_utf8);
		g_free(str_utf8);
		g_free(str);
	} while (bytes < data_len);
}
示例#10
0
static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf)
{
	gint bytes = 0;
	bytes += qq_get16(&(fh->client_ver), buf + bytes);
	bytes += qq_get8(&fh->file_key, buf + bytes);
	bytes += qq_get32(&(fh->sender_uid), buf + bytes);
	bytes += qq_get32(&(fh->receiver_uid), buf + bytes);

	fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key));
	fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key));
	return bytes;
}
示例#11
0
void qq_process_auth_code(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
{
	qq_data *qd;
	gint bytes;
	guint8 cmd, reply;
	guint16 sub_cmd;
	guint8 *code = NULL;
	guint16 code_len = 0;

	g_return_if_fail(data != NULL && data_len != 0);
	g_return_if_fail(uid != 0);

	qd = (qq_data *) gc->proto_data;

	qq_show_packet("qq_process_auth_code", data, data_len);
	bytes = 0;
	bytes += qq_get8(&cmd, data + bytes);
	bytes += qq_get16(&sub_cmd, data + bytes);
	bytes += qq_get8(&reply, data + bytes);
	g_return_if_fail(bytes + 2 <= data_len);

	bytes += qq_get16(&code_len, data + bytes);
	g_return_if_fail(code_len > 0);
	g_return_if_fail(bytes + code_len <= data_len);
	code = g_newa(guint8, code_len);
	bytes += qq_getdata(code, code_len, data + bytes);

	if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_REMOVE_BUDDY) {
		request_remove_buddy_ex(gc, uid, code, code_len);
		return;
	}
	if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_ADD_BUDDY) {
		add_buddy_authorize_input(gc, uid, code, code_len);
		return;
	}
	purple_debug_info("QQ", "Got auth info cmd 0x%x, sub 0x%x, reply 0x%x\n",
			cmd, sub_cmd, reply);
}
示例#12
0
/* process group cmd reply "search group" */
void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32)
{
	qq_data *qd;
	qq_room_data rmd;
	PurpleChat *chat;
	gint bytes;
	guint8 search_type;
	guint16 unknown;

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

	bytes = 0;
	bytes += qq_get8(&search_type, data + bytes);

	/* now it starts with group_info_entry */
	bytes += qq_get32(&(rmd.id), data + bytes);
	bytes += qq_get32(&(rmd.ext_id), data + bytes);
	bytes += qq_get8(&(rmd.type8), data + bytes);
	bytes += qq_get16(&(unknown), data + bytes);
	bytes += qq_get16(&(unknown), data + bytes);
	bytes += qq_get32(&(rmd.creator_uid), data + bytes);
	bytes += qq_get16(&(unknown), data + bytes);
	bytes += qq_get16(&(unknown), data + bytes);
	bytes += qq_get16(&(unknown), data + bytes);
	bytes += qq_get32(&(rmd.category), data + bytes);
	bytes += qq_get_vstr(&(rmd.title_utf8), QQ_CHARSET_DEFAULT, data + bytes);
	bytes += qq_get16(&(unknown), data + bytes);
	bytes += qq_get8(&(rmd.auth_type), data + bytes);
	bytes += qq_get_vstr(&(rmd.desc_utf8), QQ_CHARSET_DEFAULT, data + bytes);
	/* end of one qq_group */
	if(bytes != len) {
		purple_debug_error("QQ",
			"group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
	}

	if (ship32 == QQ_ROOM_SEARCH_FOR_JOIN) {
		chat = qq_room_find_or_new(gc, rmd.id, rmd.ext_id);
		g_return_if_fail(chat != NULL);

		qq_room_update_chat_info(chat, &rmd);
		qq_request_room_join(gc, &rmd);
	} else {
		add_to_roomlist(qd, &rmd);
	}
}
示例#13
0
void qq_process_question(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
{
	qq_data *qd;
	gint bytes;
	guint8 cmd, reply;
	gchar *question, *answer;
	guint16 code_len;
	guint8 *code;

	g_return_if_fail(data != NULL && data_len != 0);

	qd = (qq_data *) gc->proto_data;

	qq_show_packet("qq_process_question", data, data_len);
	bytes = 0;
	bytes += qq_get8(&cmd, data + bytes);
	if (cmd == QQ_QUESTION_GET) {
		bytes += qq_get_vstr(&question, QQ_CHARSET_DEFAULT, data + bytes);
		bytes += qq_get_vstr(&answer, QQ_CHARSET_DEFAULT, data + bytes);
		purple_debug_info("QQ", "Get buddy adding Q&A:\n%s\n%s\n", question, answer);
		g_free(question);
		g_free(answer);
		return;
	}
	if (cmd == QQ_QUESTION_SET) {
		bytes += qq_get8(&reply, data + bytes);
		if (reply == 0) {
			purple_debug_info("QQ", "Successed setting Q&A\n");
		} else {
			purple_debug_warning("QQ", "Failed setting Q&A, reply %d\n", reply);
		}
		return;
	}

	g_return_if_fail(uid != 0);
	bytes += 2; /* skip 2 bytes, 0x(00 01)*/
	if (cmd == QQ_QUESTION_REQUEST) {
		bytes += qq_get8(&reply, data + bytes);
		if (reply == 0x01) {
			purple_debug_warning("QQ", "Failed getting question, reply %d\n", reply);
			return;
		}
		bytes += qq_get_vstr(&question, QQ_CHARSET_DEFAULT, data + bytes);
		purple_debug_info("QQ", "Get buddy question:\n%s\n", question);
		add_buddy_question_input(gc, uid, question);
		g_free(question);
		return;
	}

	if (cmd == QQ_QUESTION_ANSWER) {
		bytes += qq_get8(&reply, data + bytes);
		if (reply == 0x01) {
			purple_notify_error(gc, _("Add Buddy"), _("Invalid answer."), NULL);
			return;
		}
		bytes += qq_get16(&code_len, data + bytes);
		g_return_if_fail(code_len > 0);
		g_return_if_fail(bytes + code_len <= data_len);

		code = g_newa(guint8, code_len);
		bytes += qq_getdata(code, code_len, data + bytes);
		request_add_buddy_by_question(gc, uid, code, code_len);
		return;
	}

	g_return_if_reached();
}
示例#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
static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint data_len)
{
	gint bytes ;
	gint decryped_bytes;
	qq_file_header fh;
	guint8 *decrypted_data;
	gint decrypted_len;
	qq_data *qd = (qq_data *) gc->proto_data;
	guint16 packet_type;
	guint16 seq;
	guint8 hellobyte;
	ft_info *info = (ft_info *) qd->xfer->data;

	bytes = 0;
	bytes += _qq_get_file_header(&fh, data + bytes);

	decrypted_data = g_newa(guint8, data_len);
	decrypted_len = qq_decrypt(decrypted_data, data, data_len, qd->session_md5);
	if ( decrypted_len <= 0 ) {
		purple_debug_error("QQ", "Error decrypt rcv file ctrl packet\n");
		return;
	}

	/* only for debug info */
	decryped_bytes = 16;	/* skip md5 section */
	decryped_bytes += qq_get16(&packet_type, decrypted_data + decryped_bytes);
	decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes);
	decryped_bytes += 4+1+1+19+1;	/* skip something */

	purple_debug_info("QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
		decrypted_data, decrypted_len,
		"decrypted control packet received:");

	switch (packet_type) {
		case QQ_FILE_CMD_NOTIFY_IP_ACK:
			decryped_bytes = 0;
			qq_get_conn_info(info, decrypted_data + decryped_bytes);
			/* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);
			break;
		case QQ_FILE_CMD_SENDER_SAY_HELLO:
			/* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
			decryped_bytes += 47;
			decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes);
			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh.sender_uid, hellobyte);
			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh.sender_uid, 0);
			break;
		case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
			/* I'm sender, do nothing */
			break;
		case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
			/* I'm sender, ack the hello packet and send the first data */
			decryped_bytes += 47;
			decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes);
			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh.sender_uid, hellobyte);
			_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
			break;
		case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
			/* I'm receiver, do nothing */
			break;
		case QQ_FILE_CMD_PING:
			/* I'm receiver, ack the PING */
			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh.sender_uid, 0);
			break;
		case QQ_FILE_CMD_PONG:
			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);
			break;
		default:
			purple_debug_info("QQ", "unprocess file command %d\n", packet_type);
	}
}
示例#16
0
/* recv an IM from a group chat */
void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type)
{
	qq_data *qd;
	gchar *msg_smiley, *msg_fmt, *msg_utf8;
	gint bytes, tail_len;
	struct {
		guint32 ext_id;
		guint8 type8;
		guint32 member_uid;
		guint16 unknown;
		guint16 msg_seq;
		time_t send_time;
		guint32 version;
		guint16 msg_len;
		gchar *msg;
	} im_text;
	guint32 temp_id;
	guint16 content_type;
	guint8 frag_count, frag_index;
	guint16 msg_id;
	qq_im_format *fmt = NULL;

	/* at least include im_text.msg_len */
	g_return_if_fail(data != NULL && data_len > 23);
	qd = (qq_data *) gc->proto_data;

	/* qq_show_packet("ROOM_IM", data, data_len); */
	memset(&im_text, 0, sizeof(im_text));
	bytes = 0;
	bytes += qq_get32(&(im_text.ext_id), data + bytes);
	bytes += qq_get8(&(im_text.type8), data + bytes);

	if(QQ_MSG_TEMP_QUN_IM == msg_type) {
		bytes += qq_get32(&temp_id, data + bytes);
	}

	bytes += qq_get32(&(im_text.member_uid), bytes + data);
	bytes += qq_get16(&im_text.unknown, data + bytes);	/* 0x0001? */
	bytes += qq_get16(&(im_text.msg_seq), data + bytes);
	bytes += qq_getime(&im_text.send_time, data + bytes);
	bytes += qq_get32(&im_text.version, data + bytes);
	bytes += qq_get16(&(im_text.msg_len), data + bytes);
	purple_debug_info("QQ", "Room IM, ext id %u, seq %u, version 0x%04X, len %u\n",
		im_text.ext_id, im_text.msg_seq, im_text.version, im_text.msg_len);

	if (im_text.msg_len != data_len - bytes) {
		purple_debug_warning("QQ", "Room IM length %d should be %d\n",
			im_text.msg_len, data_len - bytes);
		im_text.msg_len = data_len - bytes;
	}

	g_return_if_fail(im_text.msg_len > 0 && bytes + im_text.msg_len <= data_len);
	if(msg_type != QQ_MSG_QUN_IM_UNKNOWN) {
		g_return_if_fail(im_text.msg_len >= 10);

		bytes += qq_get16(&content_type, data + bytes);
		bytes += qq_get8(&frag_count, data + bytes);
		bytes += qq_get8(&frag_index, data + bytes);
		bytes += qq_get16(&msg_id, data + bytes);
		bytes += 4;	/* skip 0x(00 00 00 00) */
		purple_debug_info("QQ", "Room IM, content %d, frag %d-%d, msg id %u\n",
			content_type, frag_count, frag_index, msg_id);
		im_text.msg_len -= 10;
	}
	g_return_if_fail(im_text.msg_len > 0);

	/* qq_show_packet("Message", data + bytes, data_len - bytes); */
	if (frag_count <= 1 || frag_count == frag_index + 1) {
		fmt = qq_im_fmt_new();
		tail_len = qq_get_im_tail(fmt, data + bytes, data_len - bytes);
		im_text.msg = g_strndup((gchar *)(data + bytes), data_len - tail_len);
	} else {
		im_text.msg = g_strndup((gchar *)(data + bytes), data_len - bytes);
	}

	/* group im_group has no flag to indicate whether it has font_attr or not */
	msg_smiley = qq_emoticon_to_purple(im_text.msg);
	if (fmt != NULL) {
		msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley);
		msg_utf8 =  qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT);
		g_free(msg_fmt);
		qq_im_fmt_free(fmt);
	} else {
		msg_utf8 =  qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT);
	}
	g_free(msg_smiley);

	purple_debug_info("QQ", "Room (%u) IM from %u: %s\n",
			im_text.ext_id, im_text.member_uid, msg_utf8);
 	qq_room_got_chat_in(gc, id, im_text.member_uid, msg_utf8, im_text.send_time);

	g_free(msg_utf8);
	g_free(im_text.msg);
}
示例#17
0
文件: im.c 项目: Mons/libpurple-mini
/* process received extended (2007) text IM */
static void process_extend_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
{
	qq_data *qd;
	guint16 purple_msg_type;
	gchar *who;
	gchar *msg_smiley, *msg_fmt, *msg_utf8;
	PurpleBuddy *buddy;
	qq_buddy_data *bd;
	gint bytes, tail_len;
	qq_im_format *fmt = NULL;

	struct {
		/* now comes the part for text only */
		guint16 msg_seq;
		guint32 send_time;
		guint16 sender_icon;
		guint32 has_font_attr;
		guint8 unknown1[8];
		guint8 fragment_count;
		guint8 fragment_index;
		guint8 msg_id;
		guint8 unknown2;
		guint8 msg_type;
		gchar *msg;		/* no fixed length, ends with 0x00 */
		guint8 fromMobileQQ;
	} im_text;

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

	qd = (qq_data *) gc->proto_data;
	memset(&im_text, 0, sizeof(im_text));

	/* qq_show_packet("Extend IM text", data, len); */
	bytes = 0;
	bytes += qq_get16(&(im_text.msg_seq), data + bytes);
	bytes += qq_get32(&(im_text.send_time), data + bytes);
	bytes += qq_get16(&(im_text.sender_icon), data + bytes);
	bytes += qq_get32(&(im_text.has_font_attr), data + bytes);
	bytes += qq_getdata(im_text.unknown1, sizeof(im_text.unknown1), data + bytes);
	bytes += qq_get8(&(im_text.fragment_count), data + bytes);
	bytes += qq_get8(&(im_text.fragment_index), data + bytes);
	bytes += qq_get8(&(im_text.msg_id), data + bytes);
	bytes += 1; 	/* skip 0x00 */
	bytes += qq_get8(&(im_text.msg_type), data + bytes);
	purple_debug_info("QQ", "IM Seq %u, id %04X, fragment %d-%d, type %d, %s\n",
			im_text.msg_seq, im_text.msg_id,
			im_text.fragment_count, im_text.fragment_index,
			im_text.msg_type,
			im_text.has_font_attr ? "exist font atrr" : "");

	if (im_text.has_font_attr) {
		fmt = qq_im_fmt_new();
		tail_len = qq_get_im_tail(fmt, data + bytes, len - bytes);
		im_text.msg = g_strndup((gchar *)(data + bytes), len - tail_len);
	} else	{
		im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes);
	}
	/* qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg)); */

	if(im_text.fragment_count == 0) 	im_text.fragment_count = 1;

	who = uid_to_purple_name(im_header->uid_from);
	buddy = purple_find_buddy(gc->account, who);
	if (buddy == NULL) {
		/* create no-auth buddy */
		buddy = qq_buddy_new(gc, im_header->uid_from);
	}
	bd = (buddy == NULL) ? NULL : purple_buddy_get_protocol_data(buddy);
	if (bd != NULL) {
		bd->client_tag = im_header->version_from;
		bd->face = im_text.sender_icon;
		qq_update_buddy_icon(gc->account, who, bd->face);
	}

	purple_msg_type = 0;

	msg_smiley = qq_emoticon_to_purple(im_text.msg);
	if (fmt != NULL) {
		msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley);
		msg_utf8 =  qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT);
		g_free(msg_fmt);
		qq_im_fmt_free(fmt);
	} else {
		msg_utf8 =  qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT);
	}
	g_free(msg_smiley);

	/* send encoded to purple, note that we use im_text.send_time,
	 * not the time we receive the message
	 * as it may have been delayed when I am not online. */
	serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time);

	g_free(msg_utf8);
	g_free(who);
	g_free(im_text.msg);
}
示例#18
0
static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint len)
{
	gint bytes ;
	qq_file_header fh;
	guint16 packet_type;
	guint16 packet_seq;
	guint8 sub_type;
	guint32 fragment_index;
	guint16 fragment_len;
	guint32 fragment_offset;
	qq_data *qd = (qq_data *) gc->proto_data;
	ft_info *info = (ft_info *) qd->xfer->data;

	bytes = 0;
	bytes += _qq_get_file_header(&fh, data + bytes);

	bytes += 1; /* skip an unknown byte */
	bytes += qq_get16(&packet_type, data + bytes);
	switch(packet_type)
	{
		case QQ_FILE_CMD_FILE_OP:
			bytes += qq_get16(&packet_seq, data + bytes);
			bytes += qq_get8(&sub_type, data + bytes);
			switch (sub_type)
			{
				case QQ_FILE_BASIC_INFO:
					bytes += 4;	/* file length, we have already known it from xfer */
					bytes += qq_get32(&info->fragment_num, data + bytes);
					bytes += qq_get32(&info->fragment_len, data + bytes);

					/* FIXME: We must check the md5 here,
					 * if md5 doesn't match we will ignore
					 * the packet or send sth as error number */

					info->max_fragment_index = 0;
					info->window = 0;
					purple_debug_info("QQ",
							"start receiving data, %d fragments with %d length each\n",
							info->fragment_num, info->fragment_len);
					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
							0, 0, NULL, 0);
					break;
				case QQ_FILE_DATA_INFO:
					bytes += qq_get32(&fragment_index, data + bytes);
					bytes += qq_get32(&fragment_offset, data + bytes);
					bytes += qq_get16(&fragment_len, data + bytes);
					purple_debug_info("QQ",
							"received %dth fragment with length %d, offset %d\n",
							fragment_index, fragment_len, fragment_offset);

					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
							fragment_index, packet_seq, NULL, 0);
					_qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset);
					break;
				case QQ_FILE_EOF:
					purple_debug_info("QQ", "end of receiving\n");
					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
							0, 0, NULL, 0);
					break;
			}
			break;
		case QQ_FILE_CMD_FILE_OP_ACK:
			bytes += qq_get16(&packet_seq, data + bytes);
			bytes += qq_get8(&sub_type, data + bytes);
			switch (sub_type)
			{
				case QQ_FILE_BASIC_INFO:
					info->max_fragment_index = 0;
					info->window = 0;
					/* It is ready to send file data */
					_qq_send_file_progess(gc);
					break;
				case QQ_FILE_DATA_INFO:
					bytes += qq_get32(&fragment_index, data + bytes);
					_qq_update_send_progess(gc, fragment_index);
					if (purple_xfer_is_completed(qd->xfer))
						_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0);
					/*	else
						_qq_send_file_progess(gc); */
					break;
				case QQ_FILE_EOF:
					/* FIXME: OK, we can end the connection successfully */

					_qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
					purple_xfer_set_completed(qd->xfer, TRUE);
					break;
			}
			break;
		case QQ_FILE_EOF:
			_qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
			purple_xfer_set_completed(qd->xfer, TRUE);
			purple_xfer_end(qd->xfer);
			break;
		case QQ_FILE_BASIC_INFO:
			purple_debug_info("QQ", "here\n");
			_qq_send_file_data_packet(gc, QQ_FILE_DATA_INFO, 0, 0, 0, NULL, 0);
			break;
		default:
			purple_debug_info("QQ", "_qq_process_recv_file_data: unknown packet type [%d]\n",
					packet_type);
			break;
	}
}
示例#19
0
static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
{
	PurpleConnection *gc = (PurpleConnection *) data;
	qq_data *qd;
	qq_connection *conn;
	guint8 buf[1024];		/* set to 16 when test  tcp_rxqueue */
	gint buf_len;
	gint bytes;

	guint8 *pkt;
	guint16 pkt_len;

	gchar *error_msg;
	guint8 *jump;
	gint jump_len;

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

	if(cond != PURPLE_INPUT_READ) {
		purple_connection_error_reason(gc,
				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
				_("Socket error"));
		return;
	}

	conn = connection_find(qd, source);
	g_return_if_fail(conn != NULL);

	/* test code, not using tcp_rxqueue
	memset(pkt,0, sizeof(pkt));
	buf_len = read(qd->fd, pkt, sizeof(pkt));
	if (buf_len > 2) {
		packet_process(gc, pkt + 2, buf_len - 2);
	}
	return;
	*/

	buf_len = read(source, buf, sizeof(buf));
	if (buf_len < 0) {
		if (errno == EAGAIN)
			/* No worries */
			return;

		error_msg = g_strdup_printf(_("Lost connection with server: %s"), g_strerror(errno));
		purple_connection_error_reason(gc,
				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
				error_msg);
		g_free(error_msg);
		return;
	} else if (buf_len == 0) {
		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
				_("Server closed the connection"));
		return;
	}

	/* keep alive will be sent in 30 seconds since last_receive
	 *  QQ need a keep alive packet in every 60 seconds
	 gc->last_received = time(NULL);
	*/
#if 1
	purple_debug_info("TCP_PENDING", "Read %d bytes, tcp_rxlen is %d\n", buf_len, conn->tcp_rxlen);
#endif
	conn->tcp_rxqueue = g_realloc(conn->tcp_rxqueue, buf_len + conn->tcp_rxlen);
	memcpy(conn->tcp_rxqueue + conn->tcp_rxlen, buf, buf_len);
	conn->tcp_rxlen += buf_len;

	pkt = g_newa(guint8, MAX_PACKET_SIZE);
	while (PURPLE_CONNECTION_IS_VALID(gc)) {
		if (qd->openconns == NULL) {
			break;
		}
		if (conn->tcp_rxqueue == NULL) {
			conn->tcp_rxlen = 0;
			break;
		}
		if (conn->tcp_rxlen < QQ_TCP_HEADER_LENGTH) {
			break;
		}

		bytes = 0;
		bytes += qq_get16(&pkt_len, conn->tcp_rxqueue + bytes);
		if (conn->tcp_rxlen < pkt_len) {
			break;
		}

#if 1
		qq_show_packet("tcp_pending", conn->tcp_rxqueue, pkt_len);
#endif
		/* purple_debug_info("TCP_PENDING", "Packet len=%d, rxlen=%d\n", pkt_len, conn->tcp_rxlen); */
		if ( pkt_len < QQ_TCP_HEADER_LENGTH
		    || *(conn->tcp_rxqueue + bytes) != QQ_PACKET_TAG
			|| *(conn->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) {
			/* HEY! This isn't even a QQ. What are you trying to pull? */
			purple_debug_warning("TCP_PENDING", "Packet error, no header or tail tag\n");

			jump = memchr(conn->tcp_rxqueue + 1, QQ_PACKET_TAIL, conn->tcp_rxlen - 1);
			if ( !jump ) {
				purple_debug_warning("TCP_PENDING", "Failed to find next tail, clear receive buffer\n");
				g_free(conn->tcp_rxqueue);
				conn->tcp_rxqueue = NULL;
				conn->tcp_rxlen = 0;
				return;
			}

			/* jump and over QQ_PACKET_TAIL */
			jump_len = (jump - conn->tcp_rxqueue) + 1;
			purple_debug_warning("TCP_PENDING", "Find next tail at %d, jump %d\n", jump_len, jump_len + 1);
			g_memmove(conn->tcp_rxqueue, jump, conn->tcp_rxlen - jump_len);
			conn->tcp_rxlen -= jump_len;
			continue;
		}

		/* get packet */
		memset(pkt, 0, MAX_PACKET_SIZE);
		g_memmove(pkt, conn->tcp_rxqueue + bytes, pkt_len - bytes);

		/* jump to next packet */
		conn->tcp_rxlen -= pkt_len;
		if (conn->tcp_rxlen) {
			/* purple_debug_info("TCP_PENDING", "shrink tcp_rxqueue to %d\n", conn->tcp_rxlen);	*/
			jump = g_memdup(conn->tcp_rxqueue + pkt_len, conn->tcp_rxlen);
			g_free(conn->tcp_rxqueue);
			conn->tcp_rxqueue = jump;
		} else {
			/* purple_debug_info("TCP_PENDING", "free tcp_rxqueue\n"); */
			g_free(conn->tcp_rxqueue);
			conn->tcp_rxqueue = NULL;
		}

		/* packet_process may call disconnect and destory data like conn
		 * do not call packet_process before jump,
		 * break if packet_process return FALSE */
		if (packet_process(gc, pkt, pkt_len - bytes) == FALSE) {
			purple_debug_info("TCP_PENDING", "Connection has been destory\n");
			break;
		}
	}
}
示例#20
0
/* I receive a message, mainly it is text msg,
 * but we need to process other types (group etc) */
static void process_private_msg(guint8 *data, gint data_len, guint16 cmd, guint16 seq, PurpleConnection *gc)
{
	qq_data *qd;
	gint bytes;
	guint16 len;
	guint8 tmp8;
	struct {
		guint32 uid_from;
		guint32 uid_to;
		guint32 seq;
		struct in_addr ip_from;
		guint16 port_from;
		guint16 msg_type;
	} header;

	g_return_if_fail(data != NULL && data_len != 0);

	qd = (qq_data *) gc->proto_data;

	if (data_len < 16) {	/* we need to ack with the first 16 bytes */
		purple_debug_error("QQ", "MSG is too short\n");
		return;
	} else {
		/* when we receive a message,
		 * we send an ACK which is the first 16 bytes of incoming packet */
		qq_send_server_reply(gc, cmd, seq, data, 16);
	}

	/* check len first */
	if (data_len < 20) {	/* length of im_header */
		purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len);
		return;
	}

	bytes = 0;
	bytes += qq_get32(&(header.uid_from), data + bytes);
	bytes += qq_get32(&(header.uid_to), data + bytes);
	bytes += qq_get32(&(header.seq), data + bytes);
	/* if the message is delivered via server, it is server IP/port */
	bytes += qq_getIP(&(header.ip_from), data + bytes);
	bytes += qq_get16(&(header.port_from), data + bytes);
	bytes += qq_get16(&(header.msg_type), data + bytes);
	/* im_header prepared */

	if (header.uid_to != qd->uid) {	/* should not happen */
		purple_debug_error("QQ", "MSG to %u, NOT me\n", header.uid_to);
		return;
	}

	/* check bytes */
	if (bytes >= data_len - 1) {
		purple_debug_warning("QQ", "Empty MSG\n");
		return;
	}

	switch (header.msg_type) {
	case QQ_MSG_BUDDY_84:
	case QQ_MSG_BUDDY_85:
		purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from);
		qq_process_im(gc, data + bytes, data_len - bytes, header.msg_type);
		break;
	case QQ_MSG_TO_UNKNOWN:
	case QQ_MSG_BUDDY_09:		
	case QQ_MSG_BUDDY_A6:
	case QQ_MSG_BUDDY_A7:
	case QQ_MSG_BUDDY_78:
		purple_debug_info("QQ	", "MSG from buddy [%d]\n", header.uid_from);
		bytes += 1;
		qq_get8(&tmp8, data+bytes);
		while (tmp8 == 0)
		{
			bytes += 1;
			bytes += qq_get16(&len, data+bytes);
			bytes += len;
			qq_get8(&tmp8, data+bytes);
		}
		qq_process_im(gc, data + bytes, data_len - bytes, header.msg_type);
		break;
	case QQ_MSG_TYPING:
		qq_process_typing(gc, data+bytes, data_len-bytes, header.uid_from);
		break;
	case QQ_MSG_NEWS:
		do_server_news(gc, data + bytes, data_len - bytes);
		break;
	case QQ_MSG_SMS:
		do_got_sms(gc, data + bytes, data_len - bytes);
		break;
	case QQ_MSG_ROOM_IM_UNKNOWN:
	case QQ_MSG_TEMP_ROOM_IM:
	case QQ_MSG_ROOM_IM:
	case QQ_MSG_ROOM_IM_52:
		purple_debug_info("QQ", "MSG from room [%d]\n", header.uid_from);
		qq_process_room_im(data + bytes, data_len - bytes, header.uid_from, gc, header.msg_type);
		break;
	case QQ_MSG_ADD_TO_ROOM:
		purple_debug_info("QQ", "Notice from [%d], Added\n", header.uid_from);
		/* uid_from is group id
		* we need this to create a dummy group and add to blist */
		qq_process_room_buddy_joined(data + bytes, data_len - bytes, header.uid_from, gc);
		break;
	case QQ_MSG_DEL_FROM_ROOM:
		purple_debug_info("QQ", "Notice from room [%d], Removed\n", header.uid_from);
		/* uid_from is group id */
		qq_process_room_buddy_removed(data + bytes, data_len - bytes, header.uid_from, gc);
		break;
	case QQ_MSG_APPLY_ADD_TO_ROOM:
		purple_debug_info("QQ", "Notice from room [%d], Joined\n", header.uid_from);
		/* uid_from is group id */
		qq_process_room_buddy_request_join(data + bytes, data_len - bytes, header.uid_from, gc);
		break;
	case QQ_MSG_APPROVE_APPLY_ADD_TO_ROOM:
		purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n",
			header.uid_from);
		/* uid_from is group id */
		qq_process_room_buddy_approved(data + bytes, data_len - bytes, header.uid_from, gc);
		break;
	case QQ_MSG_REJCT_APPLY_ADD_TO_ROOM:
		purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n",
			header.uid_from);
		/* uid_from is group id */
		qq_process_room_buddy_rejected(data + bytes, data_len - bytes, header.uid_from, gc);
		break;
	case QQ_MSG_SYS:
		do_msg_sys(gc, data + bytes, data_len - bytes);
		break;
	default:
		purple_debug_warning("QQ", "MSG from %u, unknown type %s [0x%04X]\n",
			header.uid_from, get_im_type_desc(header.msg_type), header.msg_type);
		qq_show_packet("MSG header", data, bytes);
		if (data_len - bytes > 0) {
			qq_show_packet("MSG data", data + bytes, data_len - bytes);
		}
		break;
	}
}
示例#21
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);
	}
}