changeset 24164:2a19984c0005

2008.10.27 - ccpaging <ccpaging(at)gmail.com> * Update 'buddy_adding' protocol
author SHiNE CsyFeK <csyfek@gmail.com>
date Tue, 28 Oct 2008 16:47:06 +0000
parents 7f5433ffbf8d
children cae676ac3c70
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/buddy_opt.c libpurple/protocols/qq/buddy_opt.h libpurple/protocols/qq/char_conv.c libpurple/protocols/qq/char_conv.h libpurple/protocols/qq/group_join.c libpurple/protocols/qq/group_opt.c libpurple/protocols/qq/qq_define.c libpurple/protocols/qq/qq_define.h libpurple/protocols/qq/qq_process.c
diffstat 10 files changed, 240 insertions(+), 215 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Tue Oct 28 16:47:06 2008 +0000
@@ -1,3 +1,6 @@
+2008.10.27 - ccpaging <ccpaging(at)gmail.com>
+	* Update 'buddy_adding' protocol
+
 2008.10.22 - ccpaging <ccpaging(at)gmail.com>
 	* 20081022
 
--- a/libpurple/protocols/qq/buddy_opt.c	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Tue Oct 28 16:47:06 2008 +0000
@@ -66,6 +66,12 @@
 	g_free(add_req);
 }
 
+static void buddy_req_cancel_cb(qq_buddy_req *add_req, const gchar *msg)
+{
+	g_return_if_fail(add_req != NULL);
+	buddy_req_free(add_req);
+}
+
 PurpleGroup *qq_group_find_or_new(const gchar *group_name)
 {
 	PurpleGroup *g;
@@ -234,7 +240,7 @@
 	qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, raw_data, bytes, 0, uid);
 }
 
-void qq_request_auth_info(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid)
+void qq_request_auth_code(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid)
 {
 	guint8 raw_data[16];
 	gint bytes;
@@ -245,180 +251,214 @@
 	bytes += qq_put16(raw_data + bytes, sub_cmd);
 	bytes += qq_put32(raw_data + bytes, uid);
 
-	qq_send_cmd_mess(gc, QQ_CMD_AUTH_INFO, raw_data, bytes, 0, uid);
+	qq_send_cmd_mess(gc, QQ_CMD_AUTH_CODE, raw_data, bytes, 0, uid);
 }
 
-void qq_process_auth_info(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
+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 *auth = NULL;
-	guint8 auth_len = 0;
+	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_info", data, data_len);
+	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);
-	if (bytes + 2 <= data_len) {
-		bytes += 1;	/* skip 1 byte, 0x00 */
-		bytes += qq_get8(&auth_len, data + bytes);
-		if (auth_len > 0) {
-			g_return_if_fail(bytes + auth_len <= data_len);
-			auth = g_newa(guint8, auth_len);
-			bytes += qq_getdata(auth, auth_len, data + bytes);
-		}
-	} else {
-		qq_show_packet("No auth info", data, data_len);
-	}
+	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) {
-		g_return_if_fail(auth != NULL && auth_len > 0);
-		request_remove_buddy_ex(gc, uid, auth, auth_len);
+		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, auth, auth_len);
+		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);
 }
 
-#define AUHT_TYPE_QUESTION_GET        0x01
-#define AUTH_TYPE_QUESTION_SET        0x02
-#define AUTH_TYPE_QUESTION_REQUEST    0x03
-#define AUTH_TYPE_QUESTION_ANSWER     0x04
-/*
-int EvaAddFriendAuthQuestionPacket::putBody( unsigned char * buf )
+static void add_buddy_question_cb(qq_buddy_req *add_req, const gchar *text)
 {
-	int pos = 0;
-	buf[pos++] = m_AuthStatus; // 0x01, 0x02, 0x03 or 0x04
-
-	if(m_AuthStatus == AUHT_TYPE_QUESTION_GET) {
-		buf[pos++] = 0x00;
-		return pos;
+	g_return_if_fail(add_req != NULL);
+	if (add_req->gc == NULL || add_req->uid == 0) {
+		buddy_req_free(add_req);
+		return;
 	}
 
-	if(m_AuthStatus == AUTH_TYPE_QUESTION_SET){
-		printf("question(%d):%s\n", m_Question.length(), m_Question.c_str());
-		buf[pos++] = (unsigned char)(m_Question.length() & 0xff);
-		memcpy(buf + pos, m_Question.c_str(), m_Question.length());
-		pos += m_Question.length();
+	qq_request_question(add_req->gc, QQ_QUESTION_ANSWER, add_req->uid, NULL, text);
+	buddy_req_free(add_req);
+}
 
-		buf[pos++] = (unsigned char)(m_Answer.length() & 0xff);
-		printf("answer(%d):%s\n", m_Answer.length(), m_Answer.c_str());
-		memcpy(buf + pos, m_Answer.c_str(), m_Answer.length());
-		pos += m_Answer.length();
+static void add_buddy_question_input(PurpleConnection *gc, guint32 uid, gchar *question)
+{
+	gchar *who, *msg;
+	qq_buddy_req *add_req;
+	g_return_if_fail(uid != 0);
+
+	add_req = g_new0(qq_buddy_req, 1);
+	add_req->gc = gc;
+	add_req->uid = uid;
+	add_req->auth = NULL;
+	add_req->auth_len = 0;
 
-		buf[pos++] = 0x00;
-printf("EvaAddFriendAuthQuestionPacket\n");
-for(int i=0;i<pos;i++){
-	if(!(i%8)) printf("\n");
-	printf("%2x ", buf[i]);
+	who = uid_to_purple_name(uid);
+	msg = g_strdup_printf(_("%d needs Q&A"), uid);
+	purple_request_input(gc, _("Add buddy Q&A"), msg,
+			_("Input answer here"),
+			NULL,
+			TRUE, FALSE, NULL,
+			_("Send"), G_CALLBACK(add_buddy_question_cb),
+			_("Cancel"), G_CALLBACK(buddy_req_cancel_cb),
+			purple_connection_get_account(gc), who, NULL,
+			add_req);
+
+	g_free(msg);
+	g_free(who);
 }
-printf("\n\n");
-		return pos;
-	}
-	// unknown 2 bytes, always 0x0001
-	buf[pos++] = 0x00;
-	buf[pos++] = 0x01;
 
-	*((unsigned int *)(buf + pos)) = htonl(m_AddQQNum);
-	pos+= 4;
-
-	if(m_AuthStatus == AUTH_TYPE_QUESTION_REQUEST) return pos;
-
-	buf[pos++] = (unsigned char)(m_Answer.length() & 0xff);
-	memcpy(buf + pos, m_Answer.c_str(), m_Answer.length());
-	pos += m_Answer.length();
+void qq_request_question(PurpleConnection *gc,
+		guint8 cmd, guint32 uid, const gchar *question_utf8, const gchar *answer_utf8)
+{
+	guint8 raw_data[MAX_PACKET_SIZE - 16];
+	gint bytes;
 
-	return pos;
+	g_return_if_fail(uid > 0);
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, cmd);
+	if (cmd == QQ_QUESTION_GET) {
+		bytes += qq_put8(raw_data + bytes, 0);
+		qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid);
+		return;
+	}
+	if (cmd == QQ_QUESTION_SET) {
+		bytes += qq_put_vstr(raw_data + bytes, question_utf8, QQ_CHARSET_DEFAULT);
+		bytes += qq_put_vstr(raw_data + bytes, answer_utf8, QQ_CHARSET_DEFAULT);
+		bytes += qq_put8(raw_data + bytes, 0);
+		qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid);
+		return;
+	}
+	/* Unknow 2 bytes, 0x(00 01) */
+	bytes += qq_put8(raw_data + bytes, 0x00);
+	bytes += qq_put8(raw_data + bytes, 0x01);
+	g_return_if_fail(uid != 0);
+	bytes += qq_put32(raw_data + bytes, uid);
+	if (cmd == QQ_QUESTION_REQUEST) {
+		qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid);
+		return;
+	}
+	bytes += qq_put_vstr(raw_data + bytes, answer_utf8, QQ_CHARSET_DEFAULT);
+	bytes += qq_put8(raw_data + bytes, 0);
+	qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid);
+	return;
 }
-*
-void EvaAddFriendAuthQuestionReplyPacket::parseBody( )
+
+static void request_add_buddy_by_question(PurpleConnection *gc, guint32 uid,
+	guint8 *code, guint16 code_len)
 {
-printf("EvaAddFriendAuthQuestionReplyPacket\n");
-for(int i=0;i<bodyLength;i++){
-	if(!(i%8)) printf("\n");
-	printf("%2x ", decryptedBuf[i]);
-}
-printf("\n\n");
-	int offset = 0;
+	guint8 raw_data[MAX_PACKET_SIZE - 16];
+	gint bytes = 0;
 
-	m_AuthStatus = decryptedBuf[offset++];
+	g_return_if_fail(uid != 0 && code_len > 0);
 
-	if(m_AuthStatus == AUHT_TYPE_QUESTION_GET){
-		m_CodeLen = decryptedBuf[offset++];
-		if(m_CodeLen){
-			if(m_Code) delete [] m_Code;
-			m_Code = new unsigned char[m_CodeLen+1];
-			memcpy(m_Code, decryptedBuf + offset, m_CodeLen);
-			m_Code[m_CodeLen] = 0x00;
-			offset += m_CodeLen;
-		}
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, 0x10);
+	bytes += qq_put32(raw_data + bytes, uid);
+	bytes += qq_put16(raw_data + bytes, 0);
+
+	bytes += qq_put8(raw_data + bytes, 0);
+	bytes += qq_put8(raw_data + bytes, 0);	/* no auth code */
+
+	bytes += qq_put16(raw_data + bytes, code_len);
+	bytes += qq_putdata(raw_data + bytes, code, code_len);
+
+	bytes += qq_put8(raw_data + bytes, 1);	/* ALLOW ADD ME FLAG */
+	bytes += qq_put8(raw_data + bytes, 0);	/* group number? */
+	qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH_EX, raw_data, bytes);
+}
 
-		unsigned int len = decryptedBuf[offset++];
-		if(len){
-			char *tmp = new char [len+1];
-			memcpy(tmp, decryptedBuf + offset, len);
-			tmp[len] = 0x00;
-			m_Answer = tmp;
-			delete []tmp;
-			offset += len;
+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;
 	}
 
-	if(m_AuthStatus == AUTH_TYPE_QUESTION_SET){
-		m_ReplyCode = decryptedBuf[offset++]; // 0x00: success, (? -- 0x01: error)
-		return;
-	}
-
-	offset+=2; // unknown 2 bytes, always be 0x0001
-
-	m_ReplyCode = decryptedBuf[offset++];
-
-	if(m_ReplyCode == 0x01){
-		// should be error happened
+	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;
 	}
 
-	switch(m_AuthStatus){
-	case AUTH_TYPE_QUESTION_REQUEST:
-		m_CodeLen = decryptedBuf[offset++];
-		break;
-	case AUTH_TYPE_QUESTION_ANSWER:{
-		m_CodeLen = ntohs( *( (unsigned short *)(decryptedBuf + offset)) );
-		offset+=2;
+	if (cmd == QQ_QUESTION_ANSWER) {
+		bytes += qq_get8(&reply, data + bytes);
+		if (reply == 0x01) {
+			purple_notify_error(gc, _("Add Buddy"), _("Invalid answer."), NULL);
+			return;
 		}
-		break;
-	default:
-		fprintf(stderr, "Unknown auth status code: 0x%2x\n", m_AuthStatus);
+		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;
 	}
-	if(m_CodeLen){
-		if(m_Code) delete [] m_Code;
-		m_Code = new unsigned char[m_CodeLen+1];
-		memcpy(m_Code, decryptedBuf + offset, m_CodeLen);
-		offset += m_CodeLen;
-	}
 
-	// just in case the url, give it a terminate char
-	if(m_AuthStatus == AUTH_TYPE_QUESTION_REQUEST ||
-		m_AuthStatus == AUHT_TYPE_QUESTION_GET){
-		m_Code[m_CodeLen] = 0x00;
-		m_CodeLen++;
-	}
+	g_return_if_reached();
 }
- */
 
 /* try to remove myself from someone's buddy list */
 static void request_buddy_remove_me(PurpleConnection *gc, guint32 uid)
@@ -488,13 +528,11 @@
 static void request_add_buddy_auth_ex(PurpleConnection *gc, guint32 uid,
 	const gchar *text, guint8 *auth, guint8 auth_len)
 {
-	guint8 *raw_data;
+	guint8 raw_data[MAX_PACKET_SIZE - 16];
 	gint bytes = 0;
-	gchar *msg;
 
 	g_return_if_fail(uid != 0);
 
-	raw_data = g_newa(guint8, QQ_MSG_IM_MAX);
 	bytes = 0;
 	bytes += qq_put8(raw_data + bytes, 0x02);
 	bytes += qq_put32(raw_data + bytes, uid);
@@ -508,18 +546,18 @@
 		bytes += qq_putdata(raw_data + bytes, auth, auth_len);
 	}
 	bytes += qq_put8(raw_data + bytes, 1);	/* ALLOW ADD ME FLAG */
-	bytes += qq_put8(raw_data + bytes, 0);	/* Destination group */
-	if (text == NULL) {
-		bytes += qq_put8(raw_data + bytes, 0);
-	} else {
-		msg = utf8_to_qq(text, QQ_CHARSET_DEFAULT);
-		bytes += qq_put8(raw_data + bytes, strlen(msg));
-		bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg));
-		g_free(msg);
-	}
+	bytes += qq_put8(raw_data + bytes, 0);	/* group number? */
+	bytes += qq_put_vstr(raw_data + bytes, text, QQ_CHARSET_DEFAULT);
 	qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH_EX, raw_data, bytes);
 }
 
+void qq_process_add_buddy_auth_ex(PurpleConnection *gc, guint8 *data, gint data_len, guint32 ship32)
+{
+	g_return_if_fail(data != NULL && data_len != 0);
+
+	qq_show_packet("qq_process_question", data, data_len);
+}
+
 static void add_buddy_auth_cb(qq_buddy_req *add_req, const gchar *text)
 {
 	qq_data *qd;
@@ -586,12 +624,6 @@
 	g_free(who);
 }
 
-static void buddy_req_cancel_cb(qq_buddy_req *add_req, const gchar *msg)
-{
-	g_return_if_fail(add_req != NULL);
-	buddy_req_free(add_req);
-}
-
 static void add_buddy_no_auth_cb(qq_buddy_req *add_req)
 {
 	qq_data *qd;
@@ -855,12 +887,12 @@
 		case 0x00:	/* no authorize */
 			break;
 		case 0x01:	/* authorize */
-			qq_request_auth_info(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_ADD_BUDDY, uid);
+			qq_request_auth_code(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_ADD_BUDDY, uid);
 			break;
 		case 0x02:	/* disable */
 			break;
 		case 0x03:	/* answer question */
-			//qq_request_question(gc, uid);
+			qq_request_question(gc, QQ_QUESTION_REQUEST, uid, NULL, NULL);
 			break;
 		default:
 			g_return_if_reached();
@@ -885,7 +917,7 @@
 	uid = purple_name_to_uid(buddy->name);
 	if (uid > 0 && uid != qd->uid) {
 		if (qd->client_version > 2005) {
-			qq_request_auth_info(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_REMOVE_BUDDY, uid);
+			qq_request_auth_code(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_REMOVE_BUDDY, uid);
 		} else {
 			request_remove_buddy(gc, uid);
 			request_buddy_remove_me(gc, uid);
@@ -1020,8 +1052,8 @@
 		gchar *from, guint8 *data, gint data_len)
 {
 	gint bytes;
+	guint16 code_len;
 	guint8 *code;
-	guint16 code_len;
 
 	g_return_val_if_fail(data != NULL && data_len > 0, 0);
 
--- a/libpurple/protocols/qq/buddy_opt.h	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.h	Tue Oct 28 16:47:06 2008 +0000
@@ -41,6 +41,13 @@
 	QQ_AUTH_INFO_UPDATE_BUDDY_INFO = 0x0007,
 };
 
+enum {
+	QQ_QUESTION_GET = 0x01,
+	QQ_QUESTION_SET = 0x02,
+	QQ_QUESTION_REQUEST = 0x03,		/* get question only*/
+	QQ_QUESTION_ANSWER = 0x04,
+};
+
 void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 void qq_change_buddys_group(PurpleConnection *gc, const char *who,
 		const char *old_group, const char *new_group);
@@ -59,8 +66,13 @@
 
 void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_len);
 
-void qq_request_auth_info(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid);
-void qq_process_auth_info(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
+void qq_request_auth_code(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid);
+void qq_process_auth_code(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
+void qq_request_question(PurpleConnection *gc,
+		guint8 cmd, guint32 uid, const gchar *question_utf8, const gchar *answer_utf8);
+void qq_process_question(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
+
+void qq_process_add_buddy_auth_ex(PurpleConnection *gc, guint8 *data, gint data_len, guint32 ship32);
 
 qq_buddy_data *qq_buddy_data_find(PurpleConnection *gc, guint32 uid);
 void qq_buddy_data_free(qq_buddy_data *bd);
--- a/libpurple/protocols/qq/char_conv.c	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Tue Oct 28 16:47:06 2008 +0000
@@ -144,6 +144,24 @@
 	return len + 1;
 }
 
+gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset)
+{
+	gchar *str;
+	guint8 len;
+
+	if (str_utf8 == NULL || (len = strlen(str_utf8)) == 0) {
+		buf[0] = 0;
+		return 1;
+	}
+	str = do_convert(str_utf8, -1, to_charset, UTF8);
+	len = strlen(str_utf8);
+	buf[0] = len;
+	if (len > 0) {
+		memcpy(buf + 1, str, len);
+	}
+	return 1 + len;
+}
+
 /* convert QQ formatted msg to Purple formatted msg (and UTF-8) */
 gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg, const gint client_version)
 {
--- a/libpurple/protocols/qq/char_conv.h	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.h	Tue Oct 28 16:47:06 2008 +0000
@@ -30,6 +30,7 @@
 #define QQ_CHARSET_DEFAULT      "GB18030"
 
 gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data);
+gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset);
 
 gchar *qq_smiley_to_purple(gchar *text);
 gchar *purple_smiley_to_qq(gchar *text);
--- a/libpurple/protocols/qq/group_join.c	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Tue Oct 28 16:47:06 2008 +0000
@@ -154,31 +154,23 @@
 	g_free(msg);
 }
 
-void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, guint8 opt, guint32 uid, const gchar *reason_utf8)
+void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, 
+		guint8 opt, guint32 uid, const gchar *reason_utf8)
 {
-	guint8 *raw_data;
-	gchar *reason_qq;
+	guint8 raw_data[MAX_PACKET_SIZE - 16];
 	gint bytes;
 
 	g_return_if_fail(rmd != NULL);
 
-	if (reason_utf8 == NULL || strlen(reason_utf8) == 0)
-		reason_qq = g_strdup("");
-	else
-		reason_qq = utf8_to_qq(reason_utf8, QQ_CHARSET_DEFAULT);
-
 	if (opt == QQ_ROOM_AUTH_REQUEST_APPLY) {
 		rmd->my_role = QQ_ROOM_ROLE_REQUESTING;
 		uid = 0;
 	}
 
-	raw_data = g_newa(guint8, 6 + strlen(reason_qq));
-
 	bytes = 0;
 	bytes += qq_put8(raw_data + bytes, opt);
 	bytes += qq_put32(raw_data + bytes, uid);
-	bytes += qq_put8(raw_data + bytes, strlen(reason_qq));
-	bytes += qq_putdata(raw_data + bytes, (guint8 *) reason_qq, strlen(reason_qq));
+	bytes += qq_put_vstr(raw_data + bytes, reason_utf8, QQ_CHARSET_DEFAULT);
 
 	qq_send_room_cmd(gc, QQ_ROOM_CMD_AUTH, rmd->id, raw_data, bytes);
 }
--- a/libpurple/protocols/qq/group_opt.c	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Tue Oct 28 16:47:06 2008 +0000
@@ -209,19 +209,11 @@
 
 void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd)
 {
-	guint8 *data;
-	gint data_len;
+	guint8 data[MAX_PACKET_SIZE - 16];
 	gint bytes;
-	gchar *group_name, *group_desc, *notice;
 
 	g_return_if_fail(rmd != NULL);
 
-	group_name = rmd->title_utf8 == NULL ? "" : utf8_to_qq(rmd->title_utf8, QQ_CHARSET_DEFAULT);
-	group_desc = rmd->desc_utf8 == NULL ? "" : utf8_to_qq(rmd->desc_utf8, QQ_CHARSET_DEFAULT);
-	notice = rmd->notice_utf8 == NULL ? "" : utf8_to_qq(rmd->notice_utf8, QQ_CHARSET_DEFAULT);
-
-	data_len = 64 + strlen(group_name) + strlen(group_desc) + strlen(notice);
-	data = g_newa(guint8, data_len);
 	bytes = 0;
 	/* 005-005 */
 	bytes += qq_put8(data + bytes, 0x01);
@@ -232,23 +224,13 @@
 	/* 009-010 */
 	bytes += qq_put16(data + bytes, rmd->category);
 
-	bytes += qq_put8(data + bytes, strlen(group_name));
-	bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name));
+	bytes += qq_put_vstr(data + bytes, rmd->title_utf8, QQ_CHARSET_DEFAULT);
 
 	bytes += qq_put16(data + bytes, 0x0000);
 
-	bytes += qq_put8(data + bytes, strlen(notice));
-	bytes += qq_putdata(data+ bytes, (guint8 *) notice, strlen(notice));
-
-	bytes += qq_put8(data + bytes, strlen(group_desc));
-	bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc));
+	bytes += qq_put_vstr(data + bytes, rmd->notice_utf8, QQ_CHARSET_DEFAULT);
+	bytes += qq_put_vstr(data + bytes, rmd->desc_utf8, QQ_CHARSET_DEFAULT);
 
-	if (bytes > data_len) {
-		purple_debug_error("QQ",
-			   "Overflow in qq_room_change_info, max %d bytes, now %d bytes\n",
-			   data_len, bytes);
-		return;
-	}
 	qq_send_room_cmd(gc, QQ_ROOM_CMD_CHANGE_INFO, rmd->id, data, bytes);
 }
 
@@ -479,7 +461,7 @@
 {
 	guint32 ext_id, admin_uid;
 	guint8 type8;
-	gchar *reason_utf8, *msg, *reason;
+	gchar *msg, *reason;
 	qq_room_data *rmd;
 	gint bytes;
 
@@ -493,11 +475,10 @@
 
 	g_return_if_fail(ext_id > 0 && admin_uid > 0);
 
-	bytes += qq_get_vstr(&reason_utf8, QQ_CHARSET_DEFAULT, data + bytes);
+	bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes);
 
 	msg = g_strdup_printf
 		(_("Failed to join Qun %d, operated by admin %d"), ext_id, admin_uid);
-	reason = g_strdup_printf(_("Message: %s"), reason_utf8);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
 
@@ -509,7 +490,6 @@
 
 	g_free(msg);
 	g_free(reason);
-	g_free(reason_utf8);
 }
 
 /* the request to join a group is approved */
--- a/libpurple/protocols/qq/qq_define.c	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.c	Tue Oct 28 16:47:06 2008 +0000
@@ -179,8 +179,8 @@
 		return "QQ_CMD_TOKEN_EX";
 	case QQ_CMD_CHECK_PWD:
 		return "QQ_CMD_CHECK_PWD";
-	case QQ_CMD_AUTH_INFO:
-		return "QQ_CMD_AUTH_INFO";
+	case QQ_CMD_AUTH_CODE:
+		return "QQ_CMD_AUTH_CODE";
 	case QQ_CMD_ADD_BUDDY_NO_AUTH_EX:
 		return "QQ_CMD_ADD_BUDDY_NO_AUTH_EX";
 	case QQ_CMD_ADD_BUDDY_AUTH_EX:
--- a/libpurple/protocols/qq/qq_define.h	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Tue Oct 28 16:47:06 2008 +0000
@@ -70,7 +70,7 @@
 	QQ_CMD_GET_SERVER = 0x0091,					/* select login server */
 	QQ_CMD_TOKEN_EX = 0x00BA,						/* get LOGIN token */
 	QQ_CMD_CHECK_PWD = 0x00DD,				/* Password verify */
-	QQ_CMD_AUTH_INFO = 0x00AE,				/* the request verification of information */
+	QQ_CMD_AUTH_CODE = 0x00AE,				/* the request verification of information */
 	QQ_CMD_ADD_BUDDY_NO_AUTH_EX = 0x00A7,			/* add friend without auth */
 	QQ_CMD_ADD_BUDDY_AUTH_EX = 0x00A8, 				/* add buddy with auth */
 	QQ_CMD_BUDDY_CHECK_CODE =  0x00B5,
--- a/libpurple/protocols/qq/qq_process.c	Tue Oct 28 16:46:08 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Tue Oct 28 16:47:06 2008 +0000
@@ -98,49 +98,30 @@
 {
 	qq_data *qd = (qq_data *) gc->proto_data;
 	gint bytes;
-	guint8 *temp;
-	guint8 temp_len;
 	gchar *title, *brief, *url;
-	gchar *title_utf8;
-	gchar *content, *content_utf8;
+	gchar *content;
 
 	g_return_if_fail(data != NULL && data_len != 0);
 
 	/* qq_show_packet("Rcv news", data, data_len); */
 
-	temp = g_newa(guint8, data_len);
-	bytes = 4;	/* ignore unknown 4 bytes */
-
-	bytes += qq_get8(&temp_len, data + bytes);
-	g_return_if_fail(bytes + temp_len <= data_len);
-	bytes += qq_getdata(temp, temp_len, data+bytes);
-	title = g_strndup((gchar *)temp, temp_len);
+	bytes = 4;	/* skip unknown 4 bytes */
 
-	bytes += qq_get8(&temp_len, data + bytes);
-	g_return_if_fail(bytes + temp_len <= data_len);
-	bytes += qq_getdata(temp, temp_len, data+bytes);
-	brief = g_strndup((gchar *)temp, temp_len);
+	bytes += qq_get_vstr(&title, QQ_CHARSET_DEFAULT, data + bytes);
+	bytes += qq_get_vstr(&brief, QQ_CHARSET_DEFAULT, data + bytes);
+	bytes += qq_get_vstr(&url, QQ_CHARSET_DEFAULT, data + bytes);
 
-	bytes += qq_get8(&temp_len, data + bytes);
-	g_return_if_fail(bytes + temp_len <= data_len);
-	bytes += qq_getdata(temp, temp_len, data+bytes);
-	url = g_strndup((gchar *)temp, temp_len);
-
-	title_utf8 = qq_to_utf8(title, QQ_CHARSET_DEFAULT);
 	content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url);
-	content_utf8 = qq_to_utf8(content, QQ_CHARSET_DEFAULT);
 
 	if (qd->is_show_news) {
-		qq_got_attention(gc, content_utf8);
+		qq_got_attention(gc, content);
 	} else {
-		purple_debug_info("QQ", "QQ Server news:\n%s\n%s", title_utf8, content_utf8);
+		purple_debug_info("QQ", "QQ Server news:\n%s\n", content);
 	}
 	g_free(title);
-	g_free(title_utf8);
 	g_free(brief);
 	g_free(url);
 	g_free(content);
-	g_free(content_utf8);
 }
 
 static void do_msg_sys_30(PurpleConnection *gc, guint8 *data, gint data_len)
@@ -1099,12 +1080,18 @@
 			}
 			purple_debug_info("QQ", "All buddies and groups received\n");
 			break;
-		case QQ_CMD_AUTH_INFO:
-			qq_process_auth_info(gc, data, data_len, ship32);
+		case QQ_CMD_AUTH_CODE:
+			qq_process_auth_code(gc, data, data_len, ship32);
+			break;
+		case QQ_CMD_BUDDY_QUESTION:
+			qq_process_question(gc, data, data_len, ship32);
 			break;
 		case QQ_CMD_ADD_BUDDY_NO_AUTH_EX:
 			qq_process_add_buddy_no_auth_ex(gc, data, data_len, ship32);
 			break;
+		case QQ_CMD_ADD_BUDDY_AUTH_EX:
+			qq_process_add_buddy_auth_ex(gc, data, data_len, ship32);
+			break;
 		case QQ_CMD_BUDDY_CHECK_CODE:
 			qq_process_buddy_check_code(gc, data, data_len);
 			break;