pidgin.openq: 8ff543e6: 2008.09.26 - ccpaging <ccpaging(at)gmail...

csyfek at gmail.com csyfek at gmail.com
Wed Oct 22 12:12:05 EDT 2008


-----------------------------------------------------------------
Revision: 8ff543e6a27ca96a8ca7233b36314fb7e97b1e23
Ancestor: bb5558d316fe6765728168f9e19639095ba6478d
Author: csyfek at gmail.com
Date: 2008-10-22T14:35:05
Branch: im.pidgin.pidgin.openq
URL: http://d.pidgin.im/viewmtn/revision/info/8ff543e6a27ca96a8ca7233b36314fb7e97b1e23

Modified files:
        libpurple/protocols/qq/buddy_info.c
        libpurple/protocols/qq/buddy_list.c
        libpurple/protocols/qq/buddy_opt.c
        libpurple/protocols/qq/file_trans.c
        libpurple/protocols/qq/group_conv.c
        libpurple/protocols/qq/group_im.c
        libpurple/protocols/qq/group_im.h
        libpurple/protocols/qq/group_info.c
        libpurple/protocols/qq/group_info.h
        libpurple/protocols/qq/group_internal.c
        libpurple/protocols/qq/group_join.c
        libpurple/protocols/qq/group_join.h
        libpurple/protocols/qq/group_opt.c
        libpurple/protocols/qq/im.c libpurple/protocols/qq/im.h
        libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq.h
        libpurple/protocols/qq/qq_base.c
        libpurple/protocols/qq/qq_base.h
        libpurple/protocols/qq/qq_define.c
        libpurple/protocols/qq/qq_define.h
        libpurple/protocols/qq/qq_network.c
        libpurple/protocols/qq/qq_process.c
        libpurple/protocols/qq/send_file.c
        libpurple/protocols/qq/utils.c

ChangeLog: 

2008.09.26 - ccpaging <ccpaging(at)gmail.com>
	* Added 'Request/Add/Remove Buddy' functions

-------------- next part --------------
============================================================
--- libpurple/protocols/qq/buddy_info.c	b82b64007711ec63c899a89220ba30ed3241b2c8
+++ libpurple/protocols/qq/buddy_info.c	49d7853ce0d123563875612b7944b764c3498ec8
@@ -32,6 +32,7 @@
 #include "buddy_list.h"
 #include "buddy_info.h"
 #include "char_conv.h"
+#include "im.h"
 #include "qq_define.h"
 #include "qq_base.h"
 #include "qq_network.h"
@@ -452,7 +453,7 @@ void qq_process_modify_info_reply(guint8
 	data[data_len] = '\0';
 	if (qd->uid == atoi((gchar *) data)) {	/* return should be my uid */
 		purple_debug_info("QQ", "Update info ACK OK\n");
-		purple_notify_info(gc, _("QQ Buddy"), _("Successed:"), _("Change buddy information."));
+		qq_got_attention(gc, _("Successed changing buddy information."));
 	}
 }
 
@@ -735,7 +736,6 @@ void qq_process_get_level_reply(guint8 *
 		if (uid == qd->uid) {
 			qd->my_level = level;
 			purple_debug_warning("QQ", "Got my levels as %d\n", qd->my_level);
-			continue;
 		}
 
 		purple_name = uid_to_purple_name(uid);
============================================================
--- libpurple/protocols/qq/buddy_list.c	a2fd962185dde0cffff2556d476f1659d00d65d5
+++ libpurple/protocols/qq/buddy_list.c	d1094ce99ac6dc4f9e8d4b8ef21bda69c9b833c7
@@ -138,7 +138,7 @@ static gint get_buddy_status(qq_buddy_st
 	bytes += qq_get8(&bs->unknown2, data + bytes);
 	/* 012-012: status */
 	bytes += qq_get8(&bs->status, data + bytes);
-	/* 013-014: client_version */
+	/* 013-014: client tag */
 	bytes += qq_get16(&bs->unknown3, data + bytes);
 	/* 015-030: unknown key */
 	bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes);
@@ -207,7 +207,6 @@ guint8 qq_process_get_buddies_online_rep
 
 		if (bo.bs.uid == qd->uid) {
 			purple_debug_warning("QQ", "I am in online list %d\n", bo.bs.uid);
-			continue;
 		}
 
 		/* update buddy information */
@@ -228,8 +227,8 @@ guint8 qq_process_get_buddies_online_rep
 		}
 		/* we find one and update qq_buddy */
 		/*
-		if(0 != fe->s->client_version)
-			q_bud->client_version = fe->s->client_version;
+		if(0 != fe->s->client_tag)
+			q_bud->client_tag = fe->s->client_tag;
 		*/
 		q_bud->ip.s_addr = bo.bs.ip.s_addr;
 		q_bud->port = bo.bs.port;
============================================================
--- libpurple/protocols/qq/buddy_opt.c	494a448c64ee5e7ea82091e45f136183beb2a954
+++ libpurple/protocols/qq/buddy_opt.c	9ac9005db536eb3c2d5c5e624f6980e8059c7947
@@ -40,8 +40,7 @@
 #include "utils.h"
 
 #define PURPLE_GROUP_QQ_FORMAT          "QQ (%s)"
-#define PURPLE_GROUP_QQ_UNKNOWN         "QQ Unknown"
-#define PURPLE_GROUP_QQ_BLOCKED         "QQ Blocked"
+#define PURPLE_GROUP_QQ_UNKNOWN      "QQ Unknown"
 
 #define QQ_REMOVE_BUDDY_REPLY_OK      0x00
 #define QQ_REMOVE_SELF_REPLY_OK       0x00
@@ -237,7 +236,7 @@ void qq_add_buddy(PurpleConnection *gc, 
 	if (b != NULL) {
 		purple_blist_remove_buddy(b);
 	}
-	purple_notify_error(gc, NULL, _("QQ Number Error"), _("Invalid QQ Number"));
+	purple_notify_error(gc, _("QQ Buddy"), _("QQ Number Error"), _("Invalid QQ Number"));
 }
 
 void qq_change_buddys_group(PurpleConnection *gc, const char *who,
@@ -347,6 +346,10 @@ void qq_remove_buddy_and_me(PurpleBlistN
 
 	uid = purple_name_to_uid(who);
 	g_return_if_fail(uid > 0);
+	
+	if (uid == qd->uid) {
+		return;
+	}
 
 	add_req = g_new0(qq_add_request, 1);
 	add_req->gc = gc;
@@ -373,15 +376,15 @@ void qq_process_buddy_add_auth(guint8 *d
 	qd = (qq_data *) gc->proto_data;
 
 	if (data[0] != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
-		purple_debug_warning("QQ", "Add buddy with auth request failed\n");
+		purple_debug_warning("QQ", "Failed authorizing of adding requestion\n");
 		if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) {
 			return;
 		}
 		msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
-		purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8);
+		purple_notify_error(gc, _("QQ Buddy"), _("Failed authorizing of adding requestion"), msg_utf8);
 		g_free(msg_utf8);
 	} else {
-		purple_debug_info("QQ", "Add buddy with auth request OK\n");
+		qq_got_attention(gc, _("Successed authorizing of adding requestion"));
 	}
 }
 
@@ -399,9 +402,7 @@ void qq_process_buddy_remove(guint8 *dat
 		purple_debug_warning("QQ", "Remove buddy fails\n");
 		purple_notify_info(gc, _("QQ Buddy"), _("Failed:"),  _("Remove buddy"));
 	} else {		/* if reply */
-		purple_debug_info("QQ", "Remove buddy OK\n");
-		/* TODO: We don't really need to notify the user about this, do we? */
-		purple_notify_info(gc, _("QQ Buddy"), _("Successed:"),  _("Remove buddy"));
+		qq_got_attention(gc, _("Successed removing budy."));
 	}
 }
 
@@ -419,10 +420,7 @@ void qq_process_buddy_remove_me(guint8 *
 		purple_debug_warning("QQ", "Remove self fails\n");
 		purple_notify_info(gc, _("QQ Buddy"), _("Failed:"), _("Remove me from other's buddy list"));
 	} else {		/* if reply */
-		purple_debug_info("QQ", "Remove from a buddy OK\n");
-#if 0
-		purple_notify_info(gc, _("QQ Buddy"), _("Successed:"), _("Remove from other's buddy list"));
-#endif
+		qq_got_attention(gc, _("Successed removing me from other's budy list."));
 	}
 }
 
@@ -478,8 +476,8 @@ void qq_process_buddy_add_no_auth(guint8
 		g_free(nombre);
 	} else {	/* add OK */
 		qq_create_buddy(gc, uid, TRUE, TRUE);
-		msg = g_strdup_printf(_("Add into %d's buddy list"), uid);
-		purple_notify_info(gc, _("QQ Buddy"), _("Successed:"), msg);
+		msg = g_strdup_printf(_("Successed adding into %d's buddy list"), uid);
+		qq_got_attention(gc, msg);
 		g_free(msg);
 	}
 	g_strfreev(segments);
@@ -514,7 +512,7 @@ PurpleBuddy *qq_create_buddy(PurpleConne
 	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	if (is_known) {
+	if (is_known || uid == qd->uid) {
 		group_name = g_strdup_printf(PURPLE_GROUP_QQ_FORMAT,
 				purple_account_get_username(gc->account));
 	 } else {
@@ -531,7 +529,7 @@ PurpleBuddy *qq_create_buddy(PurpleConne
 		purple_blist_remove_buddy(buddy);
 
 	buddy = purple_buddy_new(gc->account, buddy_name, NULL);
-	if ( !is_known ) {
+	if ( !is_known && uid != qd->uid) {
 		if (purple_privacy_check(gc->account, buddy_name)) {
 			purple_privacy_deny(gc->account, buddy_name, TRUE, FALSE);
 		} else {
@@ -701,8 +699,8 @@ static void server_buddy_added(PurpleCon
 					_("Add"), G_CALLBACK(buddy_add_no_auth_cb),
 				    _("Search"), G_CALLBACK(buddy_add_check_info_cb));
 	} else {
-		message = g_strdup_printf(_("%s added you [%s] to buddy list"), from, to);
-		purple_notify_info(gc, _("QQ Budy"), _("Successed:"), message);
+		message = g_strdup_printf(_("Successed adding into %s's buddy list"), from);
+		qq_got_attention(gc, message);
 	}
 
 	g_free(name);
@@ -712,7 +710,7 @@ static void server_buddy_added_me(Purple
 /* the buddy approves your request of adding him/her as your friend */
 static void server_buddy_added_me(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
 {
-	gchar *message;
+	PurpleAccount *account = purple_connection_get_account(gc);
 	qq_data *qd;
 
 	g_return_if_fail(from != NULL && to != NULL);
@@ -720,10 +718,7 @@ static void server_buddy_added_me(Purple
 	qd = (qq_data *) gc->proto_data;
 	qq_create_buddy(gc, strtol(from, NULL, 10), TRUE, TRUE);
 
-	message = g_strdup_printf(_("Requestion approved by %s"), from);
-	purple_notify_info(gc, _("QQ Buddy"), _("Notice:"), message);
-
-	g_free(message);
+	purple_account_notify_added(account, from, to, NULL, msg_utf8);
 }
 
 /* you are rejected by the person */
============================================================
--- libpurple/protocols/qq/file_trans.c	2c8f48f87dabe9f111faa4bb4fba3e6134d342d9
+++ libpurple/protocols/qq/file_trans.c	fca62851a2a3c22e64c44f18723b44372680f226
@@ -248,7 +248,7 @@ static gint _qq_send_file(PurpleConnecti
 	file_key = _gen_file_key();
 
 	bytes += qq_put8(raw_data + bytes, packet_type);
-	bytes += qq_put16(raw_data + bytes, qd->client_version);
+	bytes += qq_put16(raw_data + bytes, qd->client_tag);
 	bytes += qq_put8(raw_data + bytes, file_key & 0xff);
 	bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(qd->uid, file_key));
 	bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(to_uid, file_key));
@@ -266,7 +266,7 @@ void qq_send_file_ctl_packet(PurpleConne
 {
 	qq_data *qd;
 	gint bytes, bytes_expected, encrypted_len;
-	guint8 *raw_data, *encrypted_data;
+	guint8 *raw_data, *encrypted;
 	time_t now;
 	ft_info *info;
 
@@ -334,19 +334,19 @@ void qq_send_file_ctl_packet(PurpleConne
 		raw_data, bytes,
 		"sending packet[%s]:", qq_get_file_cmd_desc(packet_type));
 
-	encrypted_data = g_newa(guint8, bytes + 16);
-	encrypted_len = qq_encrypt(encrypted_data, raw_data, bytes, info->file_session_key);
+	encrypted = g_newa(guint8, bytes + 16);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, info->file_session_key);
 	/*debug: try to decrypt it */
 
 #if 0
 	guint8 *buf;
 	int buflen;
-	hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
+	hex_dump = hex_dump_to_str(encrypted, encrypted_len);
 	purple_debug_info("QQ", "encrypted packet: \n%s", hex_dump);
 	g_free(hex_dump);
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	buflen = encrypted_len;
-	if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
+	if (qq_crypt(DECRYPT, encrypted, encrypted_len, info->file_session_key, buf, &buflen)) {
 		purple_debug_info("QQ", "decrypt success\n");
 	   if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
 			purple_debug_info("QQ", "checksum ok\n");
@@ -360,7 +360,7 @@ void qq_send_file_ctl_packet(PurpleConne
 #endif
 
 	purple_debug_info("QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
-	_qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
+	_qq_send_file(gc, encrypted, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
 }
 
 /* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */
============================================================
--- libpurple/protocols/qq/group_conv.c	5b30fdabc44113fce4d6e14886de4b9581276962
+++ libpurple/protocols/qq/group_conv.c	7615797f0108d685b7433b6aa67efaa649b74043
@@ -37,6 +37,7 @@ PurpleConversation *qq_room_conv_create(
 {
 	PurpleConversation *conv;
 	qq_data *qd;
+	gchar *topic_utf8;
 
 	g_return_val_if_fail(group != NULL, NULL);
 	qd = (qq_data *) gc->proto_data;
@@ -51,7 +52,11 @@ PurpleConversation *qq_room_conv_create(
 	serv_got_joined_chat(gc, qd->channel++, group->title_utf8);
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_utf8, purple_connection_get_account(gc));
 	if (conv != NULL) {
-		purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, group->notice_utf8);
+		topic_utf8 = g_strdup_printf("%d %s", group->ext_id, group->notice_utf8);
+		purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8);
+		purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8);
+		g_free(topic_utf8);
+
 		if (group->is_got_info)
 			qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, group->id);
 		else
@@ -83,9 +88,9 @@ void qq_group_conv_refresh_online_member
 			/* we need unique identifiers for everyone in the chat or else we'll
 			 * run into problems with functions like get_cb_real_name from qq.c */
 			member_name =   (member->nickname != NULL && *(member->nickname) != '\0') ?
-					g_strdup_printf("%s (qq-%u)", member->nickname, member->uid) :
-					g_strdup_printf("(qq-%u)", member->uid);
-			member_uid = g_strdup_printf("(qq-%u)", member->uid);
+					g_strdup_printf("%s (%u)", member->nickname, member->uid) :
+					g_strdup_printf("(%u)", member->uid);
+			member_uid = g_strdup_printf("(%u)", member->uid);
 
 			flag = 0;
 			/* TYPING to put online above OP and FOUNDER */
============================================================
--- libpurple/protocols/qq/group_im.c	2c084034186d20995d280b3c9b29688ae3efda63
+++ libpurple/protocols/qq/group_im.c	61e16cd31f54b9f0b254330ccc744d9c94e6a8f9
@@ -45,18 +45,6 @@
 #include "qq_process.h"
 #include "utils.h"
 
-typedef struct _qq_recv_group_im {
-	guint32 ext_id;
-	guint8 type8;
-	guint32 member_uid;
-	guint16 msg_seq;
-	time_t send_time;
-	guint16 msg_len;
-	gchar *msg;
-	guint8 *font_attr;
-	gint font_attr_len;
-} qq_recv_group_im;
-
 /* send IM to a group */
 void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg)
 {
@@ -150,6 +138,41 @@ void qq_process_room_msg_apply_join(guin
 	g_free(reason_utf8);
 }
 
+void qq_room_got_chat_in(PurpleConnection *gc,
+		qq_group *group, guint32 uid_from, const gchar *msg, time_t in_time)
+{
+	PurpleAccount *account = purple_connection_get_account(gc);
+	PurpleConversation *conv;
+	qq_buddy *buddy;
+	gchar *from;
+
+	g_return_if_fail(group != NULL);
+
+	conv = purple_find_conversation_with_account(
+			PURPLE_CONV_TYPE_CHAT, group->title_utf8, account);
+	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/show_room_when_newin")) {
+		conv = qq_room_conv_create(gc, group);
+	}
+
+	if (conv == NULL) {
+		return;
+	}
+
+	if (uid_from != 0) {
+		buddy = qq_group_find_member_by_uid(group, uid_from);
+		if (buddy == NULL || buddy->nickname == NULL)
+			from = uid_to_purple_name(uid_from);
+		else
+			from = g_strdup(buddy->nickname);
+	} else {
+		from = g_strdup("");
+	}
+	serv_got_chat_in(gc,
+			purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
+				from, 0, msg, in_time);
+	g_free(from);
+}
+
 /* the request to join a group is rejected */
 void qq_process_room_msg_been_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
@@ -157,12 +180,12 @@ void qq_process_room_msg_been_rejected(g
 	guint8 type8;
 	gchar *reason_utf8, *msg, *reason;
 	qq_group *group;
-	gint bytes = 0;
+	gint bytes;
 
 	g_return_if_fail(data != NULL && len > 0);
 
 	/* FIXME: check length here */
-
+	bytes = 0;
 	bytes += qq_get32(&ext_id, data + bytes);
 	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&admin_uid, data + bytes);
@@ -183,8 +206,8 @@ void qq_process_room_msg_been_rejected(g
 		qq_group_refresh(gc, group);
 	}
 
-	g_free(reason);
 	g_free(msg);
+	g_free(reason);
 	g_free(reason_utf8);
 }
 
@@ -193,35 +216,36 @@ void qq_process_room_msg_been_approved(g
 {
 	guint32 ext_id, admin_uid;
 	guint8 type8;
-	gchar *reason_utf8, *msg;
+	gchar *msg, *reason;
 	qq_group *group;
-	gint bytes = 0;
+	gint bytes;
+	time_t now;
 
 	g_return_if_fail(data != NULL && len > 0);
 
 	/* FIXME: check length here */
-
+	bytes = 0;
 	bytes += qq_get32(&ext_id, data + bytes);
 	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&admin_uid, data + bytes);
 
 	g_return_if_fail(ext_id > 0 && admin_uid > 0);
 	/* it is also a "?" here, so do not display */
-	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &reason, QQ_CHARSET_DEFAULT);
 
-	msg = g_strdup_printf
-		(_("Successed to join Qun %d, operated by admin %d"), ext_id, admin_uid);
-
-	purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL);
-
 	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
 		group->my_role = QQ_ROOM_ROLE_YES;
 		qq_group_refresh(gc, group);
 	}
 
+	msg = g_strdup_printf(_("<b>Joinning Qun %d is approved by Admin %d for %s</b>"),
+			ext_id, admin_uid, reason);
+	now = time(NULL);
+	qq_room_got_chat_in(gc, group, 0, msg, now);
+
 	g_free(msg);
-	g_free(reason_utf8);
+	g_free(reason);
 }
 
 /* process the packet when removed from a group */
@@ -232,26 +256,26 @@ void qq_process_room_msg_been_removed(gu
 	gchar *msg;
 	qq_group *group;
 	gint bytes = 0;
+	time_t now = time(NULL);
 
 	g_return_if_fail(data != NULL && len > 0);
 
 	/* FIXME: check length here */
-
+	bytes = 0;
 	bytes += qq_get32(&ext_id, data + bytes);
 	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&uid, data + bytes);
 
 	g_return_if_fail(ext_id > 0 && uid > 0);
 
-	msg = g_strdup_printf(_("[%d] removed from Qun \"%d\""), uid, ext_id);
-	purple_notify_info(gc, _("QQ Qun Operation"), _("Notice:"), msg);
-
 	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
 		group->my_role = QQ_ROOM_ROLE_NO;
 		qq_group_refresh(gc, group);
 	}
 
+	msg = g_strdup_printf(_("<b>Removed buddy %d.</b>"), uid);
+	qq_room_got_chat_in(gc, group, 0, msg, now);
 	g_free(msg);
 }
 
@@ -262,21 +286,19 @@ void qq_process_room_msg_been_added(guin
 	guint8 type8;
 	qq_group *group;
 	gchar *msg;
-	gint bytes = 0;
+	gint bytes;
+	time_t now = time(NULL);
 
 	g_return_if_fail(data != NULL && len > 0);
 
 	/* FIXME: check length here */
-
+	bytes = 0;
 	bytes += qq_get32(&ext_id, data + bytes);
 	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&uid, data + bytes);
 
 	g_return_if_fail(ext_id > 0 && uid > 0);
 
-	msg = g_strdup_printf(_("[%d] added to Qun \"%d\""), uid, ext_id);
-	purple_notify_info(gc, _("QQ Qun Operation"), _("Notice:"), msg);
-
 	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
 		group->my_role = QQ_ROOM_ROLE_YES;
@@ -289,23 +311,34 @@ void qq_process_room_msg_been_added(guin
 		/* the return of this cmd will automatically update the group in blist */
 	}
 
+	msg = g_strdup_printf(_("<b>Added new buddy %d.</b>"), uid);
+	qq_room_got_chat_in(gc, group, 0, msg, now);
 	g_free(msg);
 }
 
 /* recv an IM from a group chat */
 void qq_process_room_msg_normal(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 im_type)
 {
-	gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name;
-	guint16 unknown;
-	guint32 unknown4;
-	PurpleConversation *conv;
+	gchar *msg_with_purple_smiley, *msg_utf8_encoded;
 	qq_data *qd;
-	qq_buddy *member;
 	qq_group *group;
-	qq_recv_group_im *im_group;
 	gint skip_len;
-	gint bytes = 0;
+	gint bytes ;
+	struct {
+		guint32 ext_id;
+		guint8 type8;
+		guint32 member_uid;
+		guint16 unknown;
+		guint16 msg_seq;
+		time_t send_time;
+		guint32 unknown4;
+		guint16 msg_len;
+		gchar *msg;
+		guint8 *font_attr;
+		gint font_attr_len;
+	} packet;
 
+
 	g_return_if_fail(data != NULL && data_len > 0);
 
 	/* FIXME: check length here */
@@ -315,21 +348,20 @@ void qq_process_room_msg_normal(guint8 *
 #if 0
 	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", data, data_len, "group im hex dump");
 #endif
+	memset(&packet, 0, sizeof(packet));
+	bytes = 0;
+	bytes += qq_get32(&(packet.ext_id), data + bytes);
+	bytes += qq_get8(&(packet.type8), data + bytes);
 
-	im_group = g_newa(qq_recv_group_im, 1);
-
-	bytes += qq_get32(&(im_group->ext_id), data + bytes);
-	bytes += qq_get8(&(im_group->type8), data + bytes);
-
 	if(QQ_RECV_IM_TEMP_QUN_IM == im_type) {
 		bytes += qq_get32(&(id), data + bytes);
 	}
 
-	bytes += qq_get32(&(im_group->member_uid), bytes + data);
-	bytes += qq_get16(&unknown, data + bytes);	/* 0x0001? */
-	bytes += qq_get16(&(im_group->msg_seq), data + bytes);
-	bytes += qq_getime(&im_group->send_time, data + bytes);
-	bytes += qq_get32(&unknown4, data + bytes);	/* versionID */
+	bytes += qq_get32(&(packet.member_uid), bytes + data);
+	bytes += qq_get16(&packet.unknown, data + bytes);	/* 0x0001? */
+	bytes += qq_get16(&(packet.msg_seq), data + bytes);
+	bytes += qq_getime(&packet.send_time, data + bytes);
+	bytes += qq_get32(&packet.unknown4, data + bytes);	/* versionID */
 	/*
 	 * length includes font_attr
 	 * this msg_len includes msg and font_attr
@@ -340,8 +372,8 @@ void qq_process_room_msg_normal(guint8 *
 	 * 3. font_attr
 	 */
 
-	bytes += qq_get16(&(im_group->msg_len), data + bytes);
-	g_return_if_fail(im_group->msg_len > 0);
+	bytes += qq_get16(&(packet.msg_len), data + bytes);
+	g_return_if_fail(packet.msg_len > 0);
 
 	/*
 	 * 10 bytes from lumaqq
@@ -358,44 +390,28 @@ void qq_process_room_msg_normal(guint8 *
 		skip_len = 0;
 	bytes += skip_len;
 
-	im_group->msg = g_strdup((gchar *) data + bytes);
-	bytes += strlen(im_group->msg) + 1;
+	packet.msg = g_strdup((gchar *) data + bytes);
+	bytes += strlen(packet.msg) + 1;
 	/* there might not be any font_attr, check it */
-	im_group->font_attr_len = im_group->msg_len - strlen(im_group->msg) - 1 - skip_len;
-	if (im_group->font_attr_len > 0)
-		im_group->font_attr = g_memdup(data + bytes, im_group->font_attr_len);
+	packet.font_attr_len = packet.msg_len - strlen(packet.msg) - 1 - skip_len;
+	if (packet.font_attr_len > 0)
+		packet.font_attr = g_memdup(data + bytes, packet.font_attr_len);
 	else
-		im_group->font_attr = NULL;
+		packet.font_attr = NULL;
 
 	/* group im_group has no flag to indicate whether it has font_attr or not */
-	msg_with_purple_smiley = qq_smiley_to_purple(im_group->msg);
-	if (im_group->font_attr_len > 0)
-		msg_utf8_encoded = qq_encode_to_purple(im_group->font_attr,
-				im_group->font_attr_len, msg_with_purple_smiley);
+	msg_with_purple_smiley = qq_smiley_to_purple(packet.msg);
+	if (packet.font_attr_len > 0)
+		msg_utf8_encoded = qq_encode_to_purple(packet.font_attr,
+				packet.font_attr_len, msg_with_purple_smiley);
 	else
 		msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
 
 	group = qq_room_search_id(gc, id);
-	g_return_if_fail(group != NULL);
+ 	qq_room_got_chat_in(gc, group, packet.member_uid, msg_utf8_encoded, packet.send_time);
 
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_utf8, purple_connection_get_account(gc));
-	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/show_room_when_newin")) {
-		conv = qq_room_conv_create(gc, group);
-	}
-
-	if (conv != NULL) {
-		member = qq_group_find_member_by_uid(group, im_group->member_uid);
-		if (member == NULL || member->nickname == NULL)
-			im_src_name = uid_to_purple_name(im_group->member_uid);
-		else
-			im_src_name = g_strdup(member->nickname);
-		serv_got_chat_in(gc,
-				purple_conv_chat_get_id(PURPLE_CONV_CHAT
-					(conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time);
-		g_free(im_src_name);
-	}
 	g_free(msg_with_purple_smiley);
 	g_free(msg_utf8_encoded);
-	g_free(im_group->msg);
-	g_free(im_group->font_attr);
+	g_free(packet.msg);
+	g_free(packet.font_attr);
 }
============================================================
--- libpurple/protocols/qq/group_im.h	80dbc5dfa185c472df09adc543fa04eea527b8b8
+++ libpurple/protocols/qq/group_im.h	f082bf0a6d5c24f2fa63cf2d18a18461f1283fbb
@@ -29,6 +29,9 @@
 #include "connection.h"
 #include "group.h"
 
+void qq_room_got_chat_in(PurpleConnection *gc,
+		qq_group *group, guint32 uid_from, const gchar *msg, time_t in_time);
+
 void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg);
 
 void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc);
============================================================
--- libpurple/protocols/qq/group_info.c	0089d00bee0b490ac0b3f79637718984fa83f7e1
+++ libpurple/protocols/qq/group_info.c	34d0c999c22b0d2562bac53c5faac7ad69f2a228
@@ -101,12 +101,48 @@ gint qq_request_room_get_buddies(PurpleC
 	return num;
 }
 
-void qq_process_room_cmd_get_info(guint8 *data, gint data_len, PurpleConnection *gc)
+static void room_info_display(PurpleConnection *gc, qq_group *group)
 {
+	PurpleNotifyUserInfo *room_info;
+	gchar *utf8_value;
+
+	g_return_if_fail(group != NULL && group->id > 0);
+
+	room_info = purple_notify_user_info_new();
+
+	purple_notify_user_info_add_pair(room_info, _("Title"), group->title_utf8);
+	purple_notify_user_info_add_pair(room_info, _("Notice"), group->notice_utf8);
+	purple_notify_user_info_add_pair(room_info, _("Detail"), group->desc_utf8);
+
+	purple_notify_user_info_add_section_break(room_info);
+
+	utf8_value = g_strdup_printf(("%d"), group->creator_uid);
+	purple_notify_user_info_add_pair(room_info, _("Creator"), utf8_value);
+	g_free(utf8_value);
+
+	purple_notify_user_info_add_pair(room_info, _("About me"), group->my_role_desc);
+
+	utf8_value = g_strdup_printf(("%d"), group->category);
+	purple_notify_user_info_add_pair(room_info, _("Category"), utf8_value);
+	g_free(utf8_value);
+
+	utf8_value = g_strdup_printf(("%d"), group->auth_type);
+	purple_notify_user_info_add_pair(room_info, _("Authorize"), utf8_value);
+	g_free(utf8_value);
+
+	utf8_value = g_strdup_printf(("%d"), group->ext_id);
+	purple_notify_userinfo(gc, utf8_value, room_info, NULL, NULL);
+	g_free(utf8_value);
+
+	purple_notify_user_info_destroy(room_info);
+}
+
+void qq_process_room_cmd_get_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc)
+{
 	qq_group *group;
 	qq_buddy *member;
 	qq_data *qd;
-	PurpleConversation *purple_conv;
+	PurpleConversation *conv;
 	guint8 organization, role;
 	guint16 unknown, max_members;
 	guint32 member_uid, id, ext_id;
@@ -115,6 +151,7 @@ void qq_process_room_cmd_get_info(guint8
 	guint8 unknown1;
 	gint bytes, num;
 	gchar *notice;
+	gchar *topic_utf8;
 
 	g_return_if_fail(data != NULL && data_len > 0);
 	qd = (qq_data *) gc->proto_data;
@@ -196,16 +233,22 @@ void qq_process_room_cmd_get_info(guint8
 
 	qq_group_refresh(gc, group);
 
-	purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+	if (action == QQ_ROOM_INFO_DISPLAY) {
+		room_info_display(gc, group);
+	}
+
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
 			group->title_utf8, purple_connection_get_account(gc));
-	if(NULL == purple_conv) {
+	if(NULL == conv) {
 		purple_debug_warning("QQ",
 				"Conversation \"%s\" is not open, do not set topic\n", group->title_utf8);
 		return;
 	}
 
-	purple_debug_info("QQ", "Set chat topic to %s\n", group->notice_utf8);
-	purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8);
+	topic_utf8 = g_strdup_printf("%d %s", group->ext_id, group->notice_utf8);
+	purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8);
+	purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8);
+	g_free(topic_utf8);
 }
 
 void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc)
============================================================
--- libpurple/protocols/qq/group_info.h	5acd27bb23463327232e8d73f22c6b33c7a2fe6d
+++ libpurple/protocols/qq/group_info.h	611e8625eb38b8fa61dd92dd89d487b7f517473e
@@ -29,9 +29,14 @@
 #include "connection.h"
 #include "group.h"
 
+enum {
+	QQ_ROOM_INFO_UPDATE_ONLY = 0,
+	QQ_ROOM_INFO_DISPLAY,
+};
+
 gint qq_request_room_get_buddies(PurpleConnection *gc, qq_group *group, gint update_class);
 
-void qq_process_room_cmd_get_info(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_room_cmd_get_info(guint8 *data, gint len, guint32 action, PurpleConnection *gc);
 void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_room_cmd_get_buddies(guint8 *data, gint len, PurpleConnection *gc);
 #endif
============================================================
--- libpurple/protocols/qq/group_internal.c	1877bfc0f0735292c875f9c1e4c0a2b27b5463d7
+++ libpurple/protocols/qq/group_internal.c	506a1f5c336a811d396a585ffdb934edd34e8b3f
@@ -38,19 +38,19 @@ static gchar *get_role_desc(qq_group *gr
 
 	switch (group->my_role) {
 	case QQ_ROOM_ROLE_NO:
-		role_desc = _("I am not a member");
+		role_desc = _("Not member");
 		break;
 	case QQ_ROOM_ROLE_YES:
-		role_desc = _("I am a member");
+		role_desc = _("Member");
 		break;
 	case QQ_ROOM_ROLE_REQUESTING:
-		role_desc = _("I am requesting");
+		role_desc = _("Requesting");
 		break;
 	case QQ_ROOM_ROLE_ADMIN:
-		role_desc = _("I am the admin");
+		role_desc = _("Admin");
 		break;
 	default:
-		role_desc = _("Unknown status");
+		role_desc = _("Unknown");
 	}
 
 	return g_strdup(role_desc);
@@ -65,7 +65,7 @@ static void add_room_to_blist(PurpleConn
 	chat = purple_chat_new(purple_connection_get_account(gc), group->title_utf8, components);
 	g = qq_create_group(PURPLE_GROUP_QQ_QUN);
 	purple_blist_add_chat(chat, g, NULL);
-	purple_debug_info("QQ", "You have added group \"%s\" to blist locally\n", group->title_utf8);
+	purple_debug_info("QQ", "Added room \"%s\" to blist locally\n", group->title_utf8);
 }
 
 /* Create a dummy qq_group, which includes only internal_id, ext_id,
============================================================
--- libpurple/protocols/qq/group_join.c	752655badb239f6df8e7e9913f64947c318ecf7c
+++ libpurple/protocols/qq/group_join.c	63e1f568d5d68fde30ee60df8409c9e1dc7142be
@@ -30,6 +30,7 @@
 #include "server.h"
 
 #include "char_conv.h"
+#include "im.h"
 #include "group_conv.h"
 #include "group_find.h"
 #include "group_internal.h"
@@ -190,6 +191,7 @@ void qq_process_group_cmd_exit_group(gui
 	PurpleChat *chat;
 	qq_group *group;
 	qq_data *qd;
+	gchar *msg;
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
@@ -204,13 +206,18 @@ void qq_process_group_cmd_exit_group(gui
 
 	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
+		msg = g_strdup_printf(_("Successed quit Qun %s (%d)"),
+				group->title_utf8, group->ext_id);
 		chat = purple_blist_find_chat
 			    (purple_connection_get_account(gc), g_strdup_printf("%d", group->ext_id));
 		if (chat != NULL)
 			purple_blist_remove_chat(chat);
 		qq_group_delete_internal_record(qd, id);
+	} else {
+		msg = g_strdup(_("Successed quit Qun"));
 	}
-	purple_notify_info(gc, _("QQ Qun Operation"), _("Successed:"), _("Remove from Qun"));
+	qq_got_attention(gc, msg);
+	g_free(msg);
 }
 
 /* Process the reply to group_auth subcmd */
@@ -232,7 +239,7 @@ void qq_process_group_cmd_join_group_aut
 	bytes += qq_get32(&id, data + bytes);
 	g_return_if_fail(id > 0);
 
-	purple_notify_info(gc, _("QQ Qun Operation"), _("Successed:"), _("Join to Qun"));
+	qq_got_attention(gc, _("Successed join to Qun"));
 }
 
 /* process group cmd reply "join group" */
@@ -320,7 +327,7 @@ void qq_group_join(PurpleConnection *gc,
 	}
 }
 
-void qq_group_exit(PurpleConnection *gc, GHashTable *data)
+void qq_room_quit(PurpleConnection *gc, GHashTable *data)
 {
 	gchar *id_ptr;
 	guint32 id;
@@ -338,7 +345,7 @@ void qq_group_exit(PurpleConnection *gc,
 	add_req->uid = id;
 
 	purple_request_action(gc, _("QQ Qun Operation"),
-			    _("Are you sure you want to leave this Qun?"),
+			    _("Quit Qun"),
 			    _("Note, if you are the creator, \nthis operation will eventually remove this Qun."),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
============================================================
--- libpurple/protocols/qq/group_join.h	c66eec2d0af474f149a15208c3692f0348d6bc68
+++ libpurple/protocols/qq/group_join.h	484a35954c04266abee1f7f8d28a4758eb6af6db
@@ -44,7 +44,7 @@ void qq_request_room_join(PurpleConnecti
 void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8);
 void qq_group_join(PurpleConnection *gc, GHashTable *data);
 void qq_request_room_join(PurpleConnection *gc, qq_group *group);
-void qq_group_exit(PurpleConnection *gc, GHashTable *data);
+void qq_room_quit(PurpleConnection *gc, GHashTable *data);
 void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc);
============================================================
--- libpurple/protocols/qq/group_opt.c	9512e5600ad6681ad60d46e8aeccdc65ef8ebd78
+++ libpurple/protocols/qq/group_opt.c	7998997dd6b297225e1d46d6b2ebdb9821521401
@@ -34,6 +34,7 @@
 #include "group_internal.h"
 #include "group_info.h"
 #include "group_join.h"
+#include "group_im.h"
 #include "group_opt.h"
 #include "qq_define.h"
 #include "packet_parse.h"
@@ -199,6 +200,7 @@ void qq_group_process_modify_members_rep
 {
 	gint bytes;
 	guint32 id;
+	time_t now = time(NULL);
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
@@ -212,7 +214,7 @@ void qq_group_process_modify_members_rep
 
 	purple_debug_info("QQ", "Succeed in modify members for room %d\n", group->ext_id);
 
-	purple_notify_info(gc, _("QQ Qun Operation"), _("Successed:"), _("Change Qun member"));
+	qq_room_got_chat_in(gc, group, 0, _("Successed changing Qun member"), now);
 }
 
 void qq_room_change_info(PurpleConnection *gc, qq_group *group)
@@ -265,6 +267,8 @@ void qq_group_process_modify_info_reply(
 	gint bytes;
 	guint32 id;
 	qq_group *group;
+	time_t now = time(NULL);
+	
 	g_return_if_fail(data != NULL);
 
 	bytes = 0;
@@ -278,7 +282,7 @@ void qq_group_process_modify_info_reply(
 	purple_debug_info("QQ", "Succeed in modify info for Qun %d\n", group->ext_id);
 	qq_group_refresh(gc, group);
 
-	purple_notify_info(gc, _("QQ Qun Operation"), _("Successed:"), _("Change Qun information"));
+	qq_room_got_chat_in(gc, group, 0, _("Successed changing Qun information"), now);
 }
 
 /* we create a very simple group first, and then let the user to modify */
============================================================
--- libpurple/protocols/qq/im.c	12ab7161d337f989800516b234b5b1124cccc672
+++ libpurple/protocols/qq/im.c	d588d868095010ea523fc9eec5e4a6ae29ec562e
@@ -222,7 +222,7 @@ static const gchar *qq_get_recv_im_type_
 
 /* read the common parts of the normal_im,
  * returns the bytes read if succeed, or -1 if there is any error */
-static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common)
+static gint normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common)
 {
 	gint bytes;
 	g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1);
@@ -243,7 +243,7 @@ static gint _qq_normal_im_common_read(gu
 	return bytes;
 }
 
-static void _qq_process_recv_news(guint8 *data, gint data_len, PurpleConnection *gc)
+static void process_recv_news(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd = (qq_data *) gc->proto_data;
 	gint bytes;
@@ -278,11 +278,11 @@ static void _qq_process_recv_news(guint8
 	url = g_strndup((gchar *)temp, temp_len);
 
 	title_utf8 = qq_to_utf8(title, QQ_CHARSET_DEFAULT);
-	content = g_strdup_printf(_("%s\n\n%s"), brief, url);
+	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) {
-		purple_notify_info(gc, _("QQ Server News"), title_utf8, content_utf8);
+		qq_got_attention(gc, content_utf8);
 	} else {
 		purple_debug_info("QQ", "QQ Server news:\n%s\n%s", title_utf8, content_utf8);
 	}
@@ -294,8 +294,32 @@ static void _qq_process_recv_news(guint8
 	g_free(content_utf8);
 }
 
+void qq_got_attention(PurpleConnection *gc, const gchar *msg)
+{
+	qq_data *qd;
+	gchar *from;
+	PurpleBuddy *b;
+	qq_buddy *qq_b;
+	time_t now = time(NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+	from = uid_to_purple_name(qd->uid);
+	g_return_if_fail(qd->uid > 0);
+
+	b = purple_find_buddy(gc->account, from);
+	if (b == NULL) {
+		qq_create_buddy(gc, qd->uid, FALSE, TRUE);
+		b = purple_find_buddy(gc->account, from);
+	}
+	qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+	g_return_if_fail(qq_b != NULL);
+
+	serv_got_im(gc, from, msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY, now);
+}
+
 /* process received normal text IM */
-static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
+static void process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
 {
 	guint16 purple_msg_type;
 	gchar *name;
@@ -360,7 +384,7 @@ static void _qq_process_recv_normal_im_t
 	}
 	qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 	if (qq_b != NULL) {
-		qq_b->client_version = common->sender_ver;
+		qq_b->client_tag = common->sender_ver;
 	}
 
 	purple_msg_type = (im_text->msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0;
@@ -385,7 +409,7 @@ static void _qq_process_recv_normal_im_t
 }
 
 /* it is a normal IM, maybe text or video request */
-static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc)
+static void process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes = 0;
 	qq_recv_normal_im_common *common;
@@ -395,7 +419,7 @@ static void _qq_process_recv_normal_im(g
 
 	common = g_newa (qq_recv_normal_im_common, 1);
 
-	bytes = _qq_normal_im_common_read(data, len, common);
+	bytes = normal_im_common_read(data, len, common);
 	if (bytes < 0) {
 		purple_debug_error("QQ", "Fail read the common part of normal IM\n");
 		return;
@@ -411,7 +435,7 @@ static void _qq_process_recv_normal_im(g
 				purple_debug_warning("QQ", "Received normal IM text is empty\n");
 				return;
 			}
-			_qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc);
+			process_recv_normal_im_text(data + bytes, len - bytes, common, gc);
 			break;
 		case QQ_NORMAL_IM_FILE_REJECT_UDP:
 			qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc);
@@ -477,7 +501,7 @@ static void _qq_process_recv_normal_im(g
 }
 
 /* process im from system administrator */
-static void _qq_process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc)
+static void process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	gint len;
 	guint8 reply;
@@ -571,7 +595,7 @@ void qq_send_packet_im(PurpleConnection 
 	/* 004-007: sender uid */
 	bytes += qq_put32(raw_data + bytes, to_uid);
 	/* 008-009: sender client version */
-	bytes += qq_put16(raw_data + bytes, qd->client_version);
+	bytes += qq_put16(raw_data + bytes, qd->client_tag);
 	/* 010-013: receiver uid */
 	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
@@ -686,14 +710,14 @@ void qq_process_recv_im(guint8 *data, gi
 
 	switch (im_header->im_type) {
 		case QQ_RECV_IM_NEWS:
-			_qq_process_recv_news(data + bytes, data_len - bytes, gc);
+			process_recv_news(data + bytes, data_len - bytes, gc);
 			break;
 		case QQ_RECV_IM_FROM_BUDDY_2006:
 		case QQ_RECV_IM_FROM_UNKNOWN_2006:
 		case QQ_RECV_IM_TO_UNKNOWN:
 		case QQ_RECV_IM_TO_BUDDY:
 			purple_debug_info("QQ", "MSG from buddy [%d]\n", im_header->sender_uid);
-			_qq_process_recv_normal_im(data + bytes, data_len - bytes, gc);
+			process_recv_normal_im(data + bytes, data_len - bytes, gc);
 			break;
 		case QQ_RECV_IM_UNKNOWN_QUN_IM:
 		case QQ_RECV_IM_TEMP_QUN_IM:
@@ -732,7 +756,7 @@ void qq_process_recv_im(guint8 *data, gi
 			break;
 		case QQ_RECV_IM_SYS_NOTIFICATION:
 			purple_debug_info("QQ", "Admin notice from [%d]\n", im_header->sender_uid);
-			_qq_process_recv_sys_im(data + bytes, data_len - bytes, gc);
+			process_recv_sys_im(data + bytes, data_len - bytes, gc);
 			break;
 		default:
 			purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%02x]\n",
============================================================
--- libpurple/protocols/qq/im.h	0dbb0b4261a59a829f78c7b75a43c416ee868329
+++ libpurple/protocols/qq/im.h	0e8503aeb523371d6975e46fcff382849e1da18c
@@ -56,10 +56,12 @@ enum {
 	QQ_RECV_IM_FROM_UNKNOWN_2006 = 0x0085,
 };
 
+void qq_got_attention(PurpleConnection *gc, const gchar *msg);
+
 guint8 *qq_get_send_im_tail(const gchar *font_color,
-			    const gchar *font_size,
-			    const gchar *font_name,
-			    gboolean is_bold, gboolean is_italic, gboolean is_underline, gint len);
+		const gchar *font_size,
+		const gchar *font_name,
+		gboolean is_bold, gboolean is_italic, gboolean is_underline, gint len);
 
 void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type);
 void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
============================================================
--- libpurple/protocols/qq/qq.c	dc390467f68a19e02ea4906d2029990188041368
+++ libpurple/protocols/qq/qq.c	b7f925bb3f0d335b90d1d45b7e2b3e0a9cb6094d
@@ -44,6 +44,7 @@
 #include "group_info.h"
 #include "group_join.h"
 #include "group_opt.h"
+#include "group_internal.h"
 #include "qq_define.h"
 #include "im.h"
 #include "qq_process.h"
@@ -156,12 +157,15 @@ static void qq_login(PurpleAccount *acco
 	purple_debug_info("QQ", "Server list has %d\n", g_list_length(qd->servers));
 
 	version_str = purple_account_get_string(account, "client_version", NULL);
-	qd->client_version = QQ_CLIENT_0D55;	/* set default as QQ2005 */
-	qd->is_above_2007 = FALSE;
+	qd->client_tag = QQ_CLIENT_0D55;	/* set default as QQ2005 */
+	qd->client_version = 2005;
 	if (version_str != NULL && strlen(version_str) != 0) {
 		if (strcmp(version_str, "qq2007") == 0) {
-			qd->client_version = QQ_CLIENT_111D;
-			qd->is_above_2007 = TRUE;
+			qd->client_tag = QQ_CLIENT_111D;
+			qd->client_version = 2007;
+		} else if (strcmp(version_str, "qq2008") == 0) {
+			qd->client_tag = QQ_CLIENT_115B;
+			qd->client_version = 2008;
 		}
 	}
 
@@ -216,6 +220,7 @@ static void qq_close(PurpleConnection *g
 	qq_disconnect(gc);
 
 	if (qd->ld.token) g_free(qd->ld.token);
+	if (qd->ld.token_ex) g_free(qd->ld.token_ex);
 	if (qd->captcha.token) g_free(qd->captcha.token);
 	if (qd->captcha.data) g_free(qd->captcha.data);
 
@@ -226,14 +231,14 @@ static void qq_close(PurpleConnection *g
 }
 
 /* returns the icon name for a buddy or protocol */
-static const gchar *_qq_list_icon(PurpleAccount *a, PurpleBuddy *b)
+static const gchar *qq_list_icon(PurpleAccount *a, PurpleBuddy *b)
 {
 	return "qq";
 }
 
 
 /* a short status text beside buddy icon*/
-static gchar *_qq_status_text(PurpleBuddy *b)
+static gchar *qq_status_text(PurpleBuddy *b)
 {
 	qq_buddy *q_bud;
 	GString *status;
@@ -270,7 +275,7 @@ static gchar *_qq_status_text(PurpleBudd
 
 
 /* a floating text when mouse is on the icon, show connection status here */
-static void _qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
+static void qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
 	qq_buddy *q_bud;
 	gchar *tmp;
@@ -349,8 +354,8 @@ static void _qq_tooltip_text(PurpleBuddy
 
 #ifdef DEBUG
 	tmp = g_strdup_printf( "%s (%04X)",
-										qq_get_ver_desc(q_bud->client_version),
-										q_bud->client_version );
+										qq_get_ver_desc(q_bud->client_tag),
+										q_bud->client_tag );
 	purple_notify_user_info_add_pair(user_info, _("Ver"), tmp);
 	g_free(tmp);
 
@@ -362,7 +367,7 @@ static void _qq_tooltip_text(PurpleBuddy
 }
 
 /* we can show tiny icons on the four corners of buddy icon, */
-static const char *_qq_list_emblem(PurpleBuddy *b)
+static const char *qq_list_emblem(PurpleBuddy *b)
 {
 	/* each char** are refering to a filename in pixmaps/purple/status/default/ */
 	qq_buddy *q_bud;
@@ -382,7 +387,7 @@ static const char *_qq_list_emblem(Purpl
 }
 
 /* QQ away status (used to initiate QQ away packet) */
-static GList *_qq_away_states(PurpleAccount *ga)
+static GList *qq_status_types(PurpleAccount *ga)
 {
 	PurpleStatusType *status;
 	GList *types = NULL;
@@ -411,7 +416,7 @@ static GList *_qq_away_states(PurpleAcco
 }
 
 /* initiate QQ away with proper change_status packet */
-static void _qq_change_status(PurpleAccount *account, PurpleStatus *status)
+static void qq_change_status(PurpleAccount *account, PurpleStatus *status)
 {
 	PurpleConnection *gc = purple_account_get_connection(account);
 
@@ -420,7 +425,7 @@ static void _qq_change_status(PurpleAcco
 
 /* IMPORTANT: PurpleConvImFlags -> PurpleMessageFlags */
 /* send an instant msg to a buddy */
-static gint _qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags)
+static gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags)
 {
 	gint type, to_uid;
 	gchar *msg, *msg_with_qq_smiley;
@@ -450,7 +455,7 @@ static gint _qq_send_im(PurpleConnection
 }
 
 /* send a chat msg to a QQ Qun */
-static int _qq_chat_send(PurpleConnection *gc, int channel, const char *message, PurpleMessageFlags flags)
+static int qq_chat_send(PurpleConnection *gc, int channel, const char *message, PurpleMessageFlags flags)
 {
 	gchar *msg, *msg_with_qq_smiley;
 	qq_group *group;
@@ -561,7 +566,7 @@ static void action_show_account_info(Pur
 	g_string_append(info, "<hr>");
 
 	g_string_append_printf(info, _("<b>Server</b>: %s<br>\n"), qd->curr_server);
-	g_string_append_printf(info, _("<b>Client Version</b>: %s<br>\n"), qq_get_ver_desc(qd->client_version));
+	g_string_append_printf(info, _("<b>Client Tag</b>: %s<br>\n"), qq_get_ver_desc(qd->client_tag));
 	g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP");
 	g_string_append_printf(info, _("<b>My Internet IP</b>: %s<br>\n"), inet_ntoa(qd->my_ip));
 
@@ -605,7 +610,7 @@ static void _qq_menu_create_permanent_gr
 }
 */
 
-static void _qq_menu_unsubscribe_group(PurpleBlistNode * node)
+static void action_chat_quit(PurpleBlistNode * node)
 {
 	PurpleChat *chat = (PurpleChat *)node;
 	PurpleConnection *gc = purple_account_get_connection(chat->account);
@@ -614,22 +619,34 @@ static void _qq_menu_unsubscribe_group(P
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	g_return_if_fail(components != NULL);
-	qq_group_exit(gc, components);
+	qq_room_quit(gc, components);
 }
 
-/*
-static void _qq_menu_manage_group(PurpleBlistNode * node)
+static void action_chat_get_info(PurpleBlistNode * node)
 {
 	PurpleChat *chat = (PurpleChat *)node;
 	PurpleConnection *gc = purple_account_get_connection(chat->account);
 	GHashTable *components = chat -> components;
+	gchar *uid_str;
+	guint32 uid;
+	qq_group *group;
 
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	g_return_if_fail(components != NULL);
-	qq_group_manage_group(gc, components);
+
+	uid_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID);
+	uid = strtol(uid_str, NULL, 10);
+
+	group = qq_room_search_id(gc, uid);
+	if (group == NULL) {
+		return;
+	}
+	g_return_if_fail(group->id > 0);
+
+	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, group->id, NULL, 0,
+			QQ_CMD_CLASS_UPDATE_ROOM, QQ_ROOM_INFO_DISPLAY);
 }
-*/
 
 #if 0
 /* TODO: re-enable this */
@@ -690,31 +707,28 @@ static GList *qq_actions(PurplePlugin *p
 }
 
 /* chat-related (QQ Qun) menu shown up with right-click */
-static GList *_qq_chat_menu(PurpleBlistNode *node)
+static GList *qq_chat_menu(PurpleBlistNode *node)
 {
 	GList *m;
 	PurpleMenuAction *act;
 
 	m = NULL;
-	act = purple_menu_action_new(_("Leave the QQ Qun"), PURPLE_CALLBACK(_qq_menu_unsubscribe_group), NULL, NULL);
+	act = purple_menu_action_new(_("Get Info"), PURPLE_CALLBACK(action_chat_get_info), NULL, NULL);
 	m = g_list_append(m, act);
 
-	/* TODO: enable this
-	act = purple_menu_action_new(_("Show Details"), PURPLE_CALLBACK(_qq_menu_manage_group), NULL, NULL);
+	act = purple_menu_action_new(_("Quit Qun"), PURPLE_CALLBACK(action_chat_quit), NULL, NULL);
 	m = g_list_append(m, act);
-	*/
-
 	return m;
 }
 
 /* buddy-related menu shown up with right-click */
-static GList *_qq_buddy_menu(PurpleBlistNode * node)
+static GList *qq_buddy_menu(PurpleBlistNode * node)
 {
 	GList *m;
 	PurpleMenuAction *act;
 
 	if(PURPLE_BLIST_NODE_IS_CHAT(node))
-		return _qq_chat_menu(node);
+		return qq_chat_menu(node);
 
 	m = NULL;
 
@@ -732,9 +746,9 @@ static GList *_qq_buddy_menu(PurpleBlist
 	return m;
 }
 
-/* convert chat nickname to qq-uid to get this buddy info */
+/* convert chat nickname to uid to get this buddy info */
 /* who is the nickname of buddy in QQ chat-room (Qun) */
-static void _qq_get_chat_buddy_info(PurpleConnection *gc, gint channel, const gchar *who)
+static void qq_get_chat_buddy_info(PurpleConnection *gc, gint channel, const gchar *who)
 {
 	gchar *purple_name;
 	g_return_if_fail(who != NULL);
@@ -744,9 +758,9 @@ static void _qq_get_chat_buddy_info(Purp
 		qq_show_buddy_info(gc, purple_name);
 }
 
-/* convert chat nickname to qq-uid to invite individual IM to buddy */
+/* convert chat nickname to uid to invite individual IM to buddy */
 /* who is the nickname of buddy in QQ chat-room (Qun) */
-static gchar *_qq_get_chat_buddy_real_name(PurpleConnection *gc, gint channel, const gchar *who)
+static gchar *qq_get_chat_buddy_real_name(PurpleConnection *gc, gint channel, const gchar *who)
 {
 	g_return_val_if_fail(who != NULL, NULL);
 	return chat_name_to_purple_name(who);
@@ -758,21 +772,21 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,							/* user_splits	*/
 	NULL,							/* protocol_options */
 	{"png", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */
-	_qq_list_icon,						/* list_icon */
-	_qq_list_emblem,					/* list_emblems */
-	_qq_status_text,					/* status_text	*/
-	_qq_tooltip_text,					/* tooltip_text */
-	_qq_away_states,					/* away_states	*/
-	_qq_buddy_menu,						/* blist_node_menu */
+	qq_list_icon,						/* list_icon */
+	qq_list_emblem,					/* list_emblems */
+	qq_status_text,					/* status_text	*/
+	qq_tooltip_text,					/* tooltip_text */
+	qq_status_types,					/* away_states	*/
+	qq_buddy_menu,						/* blist_node_menu */
 	qq_chat_info,						/* chat_info */
 	qq_chat_info_defaults,					/* chat_info_defaults */
 	qq_login,							/* open */
 	qq_close,						/* close */
-	_qq_send_im,						/* send_im */
+	qq_send_im,						/* send_im */
 	NULL,							/* set_info */
 	NULL,							/* send_typing	*/
 	qq_show_buddy_info,						/* get_info */
-	_qq_change_status,						/* change status */
+	qq_change_status,						/* change status */
 	NULL,							/* set_idle */
 	NULL,							/* change_passwd */
 	qq_add_buddy,						/* add_buddy */
@@ -790,10 +804,10 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,							/* chat_invite	*/
 	NULL,							/* chat_leave */
 	NULL,							/* chat_whisper */
-	_qq_chat_send,			/* chat_send */
+	qq_chat_send,			/* chat_send */
 	NULL,							/* keepalive */
 	NULL,							/* register_user */
-	_qq_get_chat_buddy_info,				/* get_cb_info	*/
+	qq_get_chat_buddy_info,				/* get_cb_info	*/
 	NULL,							/* get_cb_away	*/
 	NULL,							/* alias_buddy	*/
 	qq_change_buddys_group,							/* group_buddy	*/
@@ -803,7 +817,7 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,							/* normalize */
 	qq_set_buddy_icon,					/* set_buddy_icon */
 	NULL,							/* remove_group */
-	_qq_get_chat_buddy_real_name,				/* get_cb_real_name */
+	qq_get_chat_buddy_real_name,				/* get_cb_real_name */
 	NULL,							/* set_chat_topic */
 	NULL,							/* find_blist_chat */
 	qq_roomlist_get_list,					/* roomlist_get_list */
@@ -908,6 +922,11 @@ static void init_plugin(PurplePlugin *pl
 	kvp->value = g_strdup("qq2007");
 	version_kv_list = g_list_append(version_kv_list, kvp);
 
+	kvp = g_new0(PurpleKeyValuePair, 1);
+	kvp->key = g_strdup(_("QQ2008"));
+	kvp->value = g_strdup("qq2008");
+	version_kv_list = g_list_append(version_kv_list, kvp);
+
 	option = purple_account_option_list_new(_("Client Version"), "client_version", version_kv_list);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 #endif
============================================================
--- libpurple/protocols/qq/qq.h	3c31740ed75db80caffc96a2decb799775baeec6
+++ libpurple/protocols/qq/qq.h	81f75e40c77db1d1d0011c8c7af40a544e866d1e
@@ -54,13 +54,16 @@ struct _qq_login_data {
 };
 
 struct _qq_login_data {
-	guint8 init_key[QQ_KEY_LENGTH];			/* first encrypt key generated by client */
-	guint8 *token;		/* get from server*/
+	guint8 random_key[QQ_KEY_LENGTH];			/* first encrypt key generated by client */
+	guint8 *token;				/* get from server */
 	guint8 token_len;
-	guint8 *token_ex;		/* get from server*/
+	guint8 *token_ex;			/* get from server */
 	guint16 token_ex_len;
-	guint8 captcha_key[QQ_KEY_LENGTH];	/* encrypt key used in captcha generated by client */
-	guint8 pwd_twice_md5[QQ_KEY_LENGTH];			/* password in md5 (or md5' md5) */
+	guint8 pwd_2nd_md5[QQ_KEY_LENGTH];			/* password in md5 (or md5' md5) */
+	guint8 pwd_4th_md5[QQ_KEY_LENGTH];
+	guint8 *login_token;
+	guint16 login_token_len;
+	guint8 login_key[QQ_KEY_LENGTH];
 };
 
 struct _qq_redirect_data {
@@ -101,7 +104,7 @@ struct _qq_buddy {
 	guint8 status;
 	guint8 ext_flag;
 	guint8 comm_flag;	/* details in qq_buddy_list.c */
-	guint16 client_version;
+	guint16 client_tag;
 	guint8 onlineTime;
 	guint16 level;
 	guint16 timeRemainder;
@@ -140,8 +143,8 @@ struct _qq_data {
 	GList *servers;
 	gchar *curr_server;		/* point to servers->data, do not free*/
 
-	guint16 client_version;
-	gboolean is_above_2007;
+	guint16 client_tag;
+	gint client_version;
 
 	struct in_addr redirect_ip;
 	guint16 redirect_port;
@@ -159,16 +162,16 @@ struct _qq_data {
 	GList *transactions;	/* check ack packet and resend */
 
 	guint32 uid;			/* QQ number */
-	
+
 	qq_login_data ld;
 	qq_captcha_data captcha;
-	
+
 	guint8 session_key[QQ_KEY_LENGTH];		/* later use this as key in this session */
 	guint8 session_md5[QQ_KEY_LENGTH];		/* concatenate my uid with session_key and md5 it */
 
 	guint16 send_seq;		/* send sequence number */
 	guint8 login_mode;		/* online of invisible */
-	gboolean is_login;		/* used by qq-add_buddy */
+	gboolean is_login;		/* used by qq_add_buddy */
 
 	PurpleXfer *xfer;			/* file transfer handler */
 
============================================================
--- libpurple/protocols/qq/qq_base.c	767955d22a2632010a419ef0aee3a009a6f9d029
+++ libpurple/protocols/qq/qq_base.c	9998ab6705011d4929fa854b0008db696be5f4ad
@@ -44,64 +44,6 @@
 #define QQ_LOGIN_REPLY_OK_PACKET_LEN        139
 #define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN  11
 
-/* for QQ 2003iii 0117, fixed value */
-/* static const guint8 login_23_51[29] = {
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
-	0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
-	0x95, 0x67, 0xda, 0x2c, 0x01
-}; */
-
-/* for QQ 2003iii 0304, fixed value */
-/*
-static const guint8 login_23_51[29] = {
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85,
-	0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6,
-	0x40, 0xb8, 0xac, 0x32, 0x01
-};
-*/
-
-/* for QQ 2005? copy from lumaqq */
-/* FIXME: change to guint8 */
-static const guint8 login_23_51[29] = {
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35,
-	0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf,
-	0xc3, 0xfa, 0x33, 0xa4, 0x01
-};
-
-static const guint8 login_53_68[16] = {
- 	0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
- 	0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
-};
-
-static const guint8 login_100_bytes[100] = {
-	0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED,
-	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00,
-	0x00, 0x00, 0x00, 0x00
-};
-
-
-/* fixed value, not affected by version, or mac address */
-/*
-static const guint8 login_53_68[16] = {
-	0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c,
-	0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf
-};
-*/
-
-
 /* generate a md5 key using uid and session_key */
 static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key)
 {
@@ -268,9 +210,38 @@ void qq_request_login(PurpleConnection *
 	qq_data *qd;
 	guint8 *buf, *raw_data;
 	gint bytes;
-	guint8 *encrypted_data;
+	guint8 *encrypted;
 	gint encrypted_len;
 
+	/* for QQ 2005? copy from lumaqq */
+	static const guint8 login_23_51[29] = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35,
+			0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf,
+			0xc3, 0xfa, 0x33, 0xa4, 0x01
+	};
+
+	static const guint8 login_53_68[16] = {
+ 			0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
+ 			0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
+	};
+
+	static const guint8 login_100_bytes[100] = {
+		0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED,
+		0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00,
+		0x00, 0x00, 0x00, 0x00
+	};
+
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
@@ -279,12 +250,12 @@ void qq_request_login(PurpleConnection *
 	raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
 	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
 
-	encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
+	encrypted = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
 
 	bytes = 0;
 	/* now generate the encrypted data
 	 * 000-015 use password_twice_md5 as key to encrypt empty string */
-	encrypted_len = qq_encrypt(raw_data + bytes, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
+	encrypted_len = qq_encrypt(raw_data + bytes, (guint8 *) "", 0, qd->ld.pwd_2nd_md5);
 	g_return_if_fail(encrypted_len == 16);
 	bytes += encrypted_len;
 
@@ -308,13 +279,13 @@ void qq_request_login(PurpleConnection *
 	bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
 	/* all zero left */
 
-	encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
 	bytes = 0;
-	bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH);
-	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
+	bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
 
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
@@ -327,9 +298,9 @@ guint8 qq_process_token(PurpleConnection
 	int token_len;
 	gchar *error_msg;
 
-	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
+	g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR);
 
-	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, -1);
+	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
 	qd = (qq_data *) gc->proto_data;
 
 	ret = buf[0];
@@ -343,7 +314,9 @@ guint8 qq_process_token(PurpleConnection
 		if (error_msg == NULL) {
 				error_msg = g_strdup_printf( _("Invalid token reply code, 0x%02X"), ret);
 		}
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+				error_msg);
 		g_free(error_msg);
 		return QQ_LOGIN_REPLY_ERR;
 	}
@@ -351,7 +324,9 @@ guint8 qq_process_token(PurpleConnection
 	token_len = buf_len-2;
 	if (token_len <= 0) {
 		error_msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+				error_msg);
 		g_free(error_msg);
 		return QQ_LOGIN_REPLY_ERR;
 	}
@@ -383,18 +358,45 @@ void qq_request_logout(PurpleConnection 
 
 	qd = (qq_data *) gc->proto_data;
 	for (i = 0; i < 4; i++)
-		qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->ld.pwd_twice_md5, QQ_KEY_LENGTH);
+		qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->ld.pwd_2nd_md5, QQ_KEY_LENGTH);
 
 	qd->is_login = FALSE;	/* update login status AFTER sending logout packets */
 }
 
+/* for QQ 2003iii 0117, fixed value */
+/* static const guint8 login_23_51[29] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
+	0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
+	0x95, 0x67, 0xda, 0x2c, 0x01
+}; */
+
+/* for QQ 2003iii 0304, fixed value */
+/*
+static const guint8 login_23_51[29] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85,
+	0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6,
+	0x40, 0xb8, 0xac, 0x32, 0x01
+};
+*/
+
+/* fixed value, not affected by version, or mac address */
+/*
+static const guint8 login_53_68[16] = {
+	0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c,
+	0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf
+};
+*/
+
 /* process the login reply packet */
 guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len)
 {
 	qq_data *qd;
 	guint8 ret = data[0];
-	gchar *server_reply, *server_reply_utf8;
-	gchar *error_msg;
+	gchar *msg, *msg_utf8;
+	gchar *error;
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
 
 	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
 
@@ -408,60 +410,42 @@ guint8 qq_process_login( PurpleConnectio
 			purple_debug_info("QQ", "Redirect new server\n");
 			return process_login_redirect(gc, data, data_len);
 
-		case QQ_LOGIN_REPLY_REDIRECT_EX:
-			purple_debug_error("QQ", "Extend redirect new server, not supported yet\n");
-			error_msg = g_strdup( _("Unable login for not support Redirect_EX now") );
-			return QQ_LOGIN_REPLY_REDIRECT_EX;
-
-		case QQ_LOGIN_REPLY_ERR_PWD:
-			server_reply = g_strndup((gchar *)data + 1, data_len - 1);
-			server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
-
-			purple_debug_error("QQ", "Error password: %s\n", server_reply_utf8);
-			error_msg = g_strdup_printf( _("Error password: %s"), server_reply_utf8);
-
-			g_free(server_reply);
-			g_free(server_reply_utf8);
-
+		case 0x0A:		/* extend redirect used in QQ2006 */
+			error = g_strdup( _("Not support Redirect_EX now") );
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+			break;
+		case 0x05:		/* invalid password */
 			if (!purple_account_get_remember_password(gc->account)) {
 				purple_account_set_password(gc->account, NULL);
 			}
-
-			purple_connection_error_reason(gc,
-				PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, error_msg);
-			g_free(error_msg);
-
-			return QQ_LOGIN_REPLY_ERR_PWD;
-
-		case QQ_LOGIN_REPLY_NEED_REACTIVE:
-			server_reply = g_strndup((gchar *)data + 1, data_len - 1);
-			server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
-
-			purple_debug_error("QQ", "Need active: %s\n", server_reply_utf8);
-			error_msg = g_strdup_printf( _("Need active: %s"), server_reply_utf8);
-
-			g_free(server_reply);
-			g_free(server_reply_utf8);
+			error = g_strdup( _("Error password"));
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
 			break;
+		case 0x06:		/* need activation */
+			error = g_strdup( _("Need active"));
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+			break;
 
 		default:
-			purple_debug_error("QQ",
-				"Unable login for unknow reply code 0x%02X\n", data[0]);
-			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
-				data, data_len,
-				">>> [default] decrypt and dump");
-			error_msg = try_dump_as_gbk(data, data_len);
-			if (error_msg == NULL) {
-				error_msg = g_strdup_printf(
-					_("Unable login for unknow reply code 0x%02X"), data[0] );
-			}
+			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+					">>> [default] decrypt and dump");
+			error = g_strdup_printf(
+						_("Unknow reply code when checking password (0x%02X)"),
+						ret );
+			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
 			break;
 	}
 
-	purple_connection_error_reason(gc,
-		PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
-	g_free(error_msg);
-	return ret;
+	msg = g_strndup((gchar *)data + 1, data_len - 1);
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+
+	purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
+	purple_connection_error_reason(gc, reason, msg_utf8);
+
+	g_free(error);
+	g_free(msg);
+	g_free(msg_utf8);
+	return QQ_LOGIN_REPLY_ERR;
 }
 
 /* send keep-alive packet to QQ server (it is a heart-beat) */
@@ -500,7 +484,8 @@ gboolean qq_process_keep_alive(guint8 *d
 	/* segments[0] and segment[1] are all 0x30 ("0") */
 	qd->online_total = strtol(segments[2], NULL, 10);
 	if(0 == qd->online_total) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Keep alive error"));
 	}
 	qd->my_ip.s_addr = inet_addr(segments[3]);
@@ -519,7 +504,7 @@ void qq_request_get_server(PurpleConnect
 	qq_data *qd;
 	guint8 *buf, *raw_data;
 	gint bytes;
-	guint8 *encrypted_data;
+	guint8 *encrypted;
 	gint encrypted_len;
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -528,44 +513,35 @@ void qq_request_get_server(PurpleConnect
 	raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
 	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
 
-	encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
+	encrypted = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
 
 	bytes = 0;
 	bytes += qq_putdata(raw_data + bytes, (guint8 *)&qd->redirect_data, sizeof(qd->redirect_data));
 
-	encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
 	bytes = 0;
-	bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH);
-	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
+	bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
 
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
 }
 
-guint16 qq_process_get_server(PurpleConnection *gc, guint8 *rcved, gint rcved_len)
+guint16 qq_process_get_server(PurpleConnection *gc, guint8 *data, gint data_len)
 {
 	qq_data *qd;
-	guint8 *data;
-	gint data_len;
 	qq_redirect_data *redirect;
 
 	g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
 	qd = (qq_data *) gc->proto_data;
 
-	data = g_newa(guint8, rcved_len);
-	/* May use password_twice_md5 in the past version like QQ2005*/
-	data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.init_key);
-	if (data_len < 0) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Can not decrypt get server reply"));
-		return QQ_LOGIN_REPLY_ERR;
-	}
-
+	g_return_val_if_fail (data != NULL, QQ_LOGIN_REPLY_ERR);
 	if (data_len < sizeof(qq_redirect_data)) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Can not decrypt get server reply"));
 		return QQ_LOGIN_REPLY_ERR;
 	}
@@ -587,7 +563,7 @@ void qq_request_token_ex(PurpleConnectio
 	qq_data *qd;
 	guint8 *buf, *raw_data;
 	gint bytes;
-	guint8 *encrypted_data;
+	guint8 *encrypted;
 	gint encrypted_len;
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -598,7 +574,7 @@ void qq_request_token_ex(PurpleConnectio
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
 	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
 
-	encrypted_data = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
 
 	bytes = 0;
 	bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
@@ -609,13 +585,13 @@ void qq_request_token_ex(PurpleConnectio
 	bytes += qq_put8(raw_data + bytes, 0); 		/* fragment index */
 	bytes += qq_put16(raw_data + bytes, 0); 	/* captcha token */
 
-	encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
 	bytes = 0;
-	bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH);
-	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
+	bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
 
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
@@ -626,7 +602,7 @@ void qq_request_token_ex_next(PurpleConn
 	qq_data *qd;
 	guint8 *buf, *raw_data;
 	gint bytes;
-	guint8 *encrypted_data;
+	guint8 *encrypted;
 	gint encrypted_len;
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -637,7 +613,7 @@ void qq_request_token_ex_next(PurpleConn
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
 	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
 
-	encrypted_data = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
 
 	bytes = 0;
 	bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
@@ -649,13 +625,13 @@ void qq_request_token_ex_next(PurpleConn
 	bytes += qq_put16(raw_data + bytes, qd->captcha.token_len); 	/* captcha token */
 	bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_len);
 
-	encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
 	bytes = 0;
-	bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH);
-	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
+	bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
 
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
@@ -669,7 +645,7 @@ static void request_token_ex_code(Purple
 	qq_data *qd;
 	guint8 *buf, *raw_data;
 	gint bytes;
-	guint8 *encrypted_data;
+	guint8 *encrypted;
 	gint encrypted_len;
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -681,7 +657,7 @@ static void request_token_ex_code(Purple
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
 	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
 
-	encrypted_data = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
 
 	bytes = 0;
 	bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
@@ -694,13 +670,13 @@ static void request_token_ex_code(Purple
 	bytes += qq_put16(raw_data + bytes, qd->captcha.token_len); 	/* captcha token */
 	bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_len);
 
-	encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
 	bytes = 0;
-	bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH);
-	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
+	bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
 
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
@@ -797,7 +773,7 @@ void qq_captcha_input_dialog(PurpleConne
 		captcha_req);
 }
 
-guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len)
+guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *data, gint data_len)
 {
 	qq_data *qd;
 	int bytes;
@@ -807,41 +783,41 @@ guint8 qq_process_token_ex(PurpleConnect
 	guint16 captcha_len;
 	guint8 curr_index;
 
-	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
+	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
 
-	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, -1);
+	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
 	qd = (qq_data *) gc->proto_data;
 
-	ret = buf[0];
+	ret = data[0];
 
 	bytes = 0;
-	bytes += qq_get8(&sub_cmd, buf + bytes);
+	bytes += qq_get8(&sub_cmd, data + bytes);
 	bytes += 2;
-	bytes += qq_get8(&reply, buf + bytes);
+	bytes += qq_get8(&reply, data + bytes);
 
-	bytes += qq_get16(&(qd->ld.token_ex_len), buf + bytes);
+	bytes += qq_get16(&(qd->ld.token_ex_len), data + bytes);
 	if (qd->ld.token_ex != NULL)	g_free(qd->ld.token_ex);
 	qd->ld.token_ex = g_new0(guint8, qd->ld.token_ex_len);
-	bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len , buf + bytes);
+	bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len , data + bytes);
 
 	if(reply != 1)
 	{
-		purple_debug_info("QQ", "All captchaes is verified\n");
+		purple_debug_info("QQ", "Captcha verified\n");
 		return QQ_LOGIN_REPLY_OK;
 	}
 
-	bytes += qq_get16(&captcha_len, buf + bytes);
+	bytes += qq_get16(&captcha_len, data + bytes);
 
 	qd->captcha.data = g_realloc(qd->captcha.data, qd->captcha.data_len + captcha_len);
-	bytes += qq_getdata(qd->captcha.data + qd->captcha.data_len, captcha_len, buf + bytes);
+	bytes += qq_getdata(qd->captcha.data + qd->captcha.data_len, captcha_len, data + bytes);
 	qd->captcha.data_len += captcha_len;
 
-	bytes += qq_get8(&curr_index, buf + bytes);
-	bytes += qq_get8(&qd->captcha.next_index, buf + bytes);
+	bytes += qq_get8(&curr_index, data + bytes);
+	bytes += qq_get8(&qd->captcha.next_index, data + bytes);
 
-	bytes += qq_get16(&qd->captcha.token_len, buf + bytes);
+	bytes += qq_get16(&qd->captcha.token_len, data + bytes);
 	qd->captcha.token = g_new0(guint8, qd->captcha.token_len);
-	bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, buf + bytes);
+	bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, data + bytes);
 
 	if(qd->captcha.next_index > 0)
 	{
@@ -850,3 +826,425 @@ guint8 qq_process_token_ex(PurpleConnect
 
 	return QQ_LOGIN_REPLY_CAPTCHA_DLG;
 }
+
+/* source copy from gg's common.c */
+static guint32 crc32_table[256];
+static int crc32_initialized = 0;
+
+static void crc32_make_table()
+{
+	guint32 h = 1;
+	unsigned int i, j;
+
+	memset(crc32_table, 0, sizeof(crc32_table));
+
+	for (i = 128; i; i >>= 1) {
+		h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
+
+		for (j = 0; j < 256; j += 2 * i)
+			crc32_table[i + j] = crc32_table[j] ^ h;
+	}
+
+	crc32_initialized = 1;
+}
+
+static guint32 crc32(guint32 crc, const guint8 *buf, int len)
+{
+	if (!crc32_initialized)
+		crc32_make_table();
+
+	if (!buf || len < 0)
+		return crc;
+
+	crc ^= 0xffffffffL;
+
+	while (len--)
+		crc = (crc >> 8) ^ crc32_table[(crc ^ *buf++) & 0xff];
+
+	return crc ^ 0xffffffffL;
+}
+
+void qq_request_check_pwd_2007(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted;
+	gint encrypted_len;
+	static guint8 header[] = { 0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0x0E };
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+
+	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+
+	/* Encrypted password and put in encrypted */
+	bytes = 0;
+	bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_2nd_md5, sizeof(qd->ld.pwd_2nd_md5));
+	bytes += qq_put16(raw_data + bytes, 0);
+	bytes += qq_put16(raw_data + bytes, 0);
+
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_4th_md5);
+
+	/* create packet */
+	bytes = 0;
+	bytes += qq_putdata(raw_data + bytes, header, sizeof(header));
+	/* token get from qq_request_token_ex */
+	bytes += qq_put16(raw_data + bytes, qd->ld.token_ex_len);
+	bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
+	/* password encrypted */
+	bytes += qq_put16(raw_data + bytes, encrypted_len);
+	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+	/* some random data */
+	bytes += qq_put16(raw_data + bytes, 0x0014);
+	bytes += qq_put32(raw_data + bytes, rand() & 0xffff);
+	bytes += qq_put32(raw_data + bytes, rand() & 0xffff);
+	bytes += qq_put32(raw_data + bytes, rand() & 0xffff);
+	bytes += qq_put32(raw_data + bytes, rand() & 0xffff);
+	bytes += qq_put32(raw_data + bytes, rand() & 0xffff);
+	/* put length into first 2 bytes */
+	qq_put16(raw_data, bytes - 2);
+	/* tail */
+	bytes += qq_put8(raw_data + bytes, 0);
+	bytes += qq_put8(raw_data + bytes, 0x03);
+	bytes += qq_put8(raw_data + bytes, 0);
+	bytes += qq_put8(raw_data + bytes, qd->ld.pwd_2nd_md5[1]);
+	bytes += qq_put8(raw_data + bytes, qd->ld.pwd_2nd_md5[2]);
+
+	/* Encrypted by random key*/
+	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
+	bytes = 0;
+	bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+	qd->send_seq++;
+	qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
+}
+
+guint8 qq_process_check_pwd_2007( PurpleConnection *gc, guint8 *data, gint data_len)
+{
+	qq_data *qd;
+	int bytes;
+	guint8 ret;
+	guint16 unknown_len;
+	gchar *error = NULL;
+	gchar *msg, *msg_utf8;
+	guint16 msg_len;
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+
+	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
+
+	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
+	qd = (qq_data *) gc->proto_data;
+
+	bytes = 2;	// skip 2 bytes
+	bytes += qq_get8(&ret, data + bytes);
+	bytes += 4;  // skip 4 bytes
+	/* 2 unknow */
+	bytes += qq_get16(&unknown_len, data + bytes);
+	bytes += unknown_len;
+	bytes += qq_get16(&unknown_len, data + bytes);
+	bytes += unknown_len;
+	if (ret == QQ_LOGIN_REPLY_OK) {
+		/* get login_token */
+		bytes += qq_get16(&qd->ld.login_token_len, data + bytes);
+		if (qd->ld.login_token != NULL) g_free(qd->ld.login_token);
+		qd->ld.login_token = g_new0(guint8, qd->ld.login_token_len);
+		bytes += qq_getdata(qd->ld.login_token, qd->ld.login_token_len, data + bytes);
+		/* get login_key */
+		bytes += qq_getdata(qd->ld.login_key, sizeof(qd->ld.login_key), data + bytes);
+		return QQ_LOGIN_REPLY_OK;
+	}
+
+	switch (ret)
+	{
+		case 0x34:		/* invalid password */
+			if (!purple_account_get_remember_password(gc->account)) {
+				purple_account_set_password(gc->account, NULL);
+			}
+			error = g_strdup(_("Error password"));
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+			break;
+		case 0x33:		/* need activation */
+		case 0x51:		/* need activation */
+			error = g_strdup(_("Need active"));
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+			break;
+		case 0xBF:		/* uid is not exist */
+			error = g_strdup(_("invalid user name"));
+			reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
+			break;
+		default:
+			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+					">>> [default] decrypt and dump");
+			error = g_strdup_printf(
+						_("Unknow reply code when checking password (0x%02X)"),
+						ret );
+			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+			break;
+	}
+	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+			">>> [default] decrypt and dump");
+	bytes = 1;
+	bytes += qq_get16(&msg_len, data + bytes);
+
+	msg = g_strndup((gchar *)data + bytes, msg_len);
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+
+	purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
+	purple_connection_error_reason(gc, reason, msg_utf8);
+
+	g_free(error);
+	g_free(msg);
+	g_free(msg_utf8);
+	return QQ_LOGIN_REPLY_ERR;
+}
+
+guint8 qq_process_check_pwd_2008( PurpleConnection *gc, guint8 *data, gint data_len)
+{
+	qq_data *qd;
+	int bytes;
+	guint8 ret;
+	gchar *error = NULL;
+	gchar *msg, *msg_utf8;
+	guint16 msg_len;
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+
+	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
+
+	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
+	qd = (qq_data *) gc->proto_data;
+
+	bytes = 1;	// skip 1 bytes, always 0
+	bytes += qq_get8(&ret, data + bytes);
+	if (ret == 0x97) {
+		/* get login_token */
+		bytes += qq_get16(&qd->ld.login_token_len, data);
+		if (qd->ld.login_token != NULL) g_free(qd->ld.login_token);
+		qd->ld.login_token = g_new0(guint8, qd->ld.login_token_len);
+		bytes += qq_getdata(qd->ld.login_token, qd->ld.login_token_len, data + bytes);
+		/* get login_key */
+		bytes += qq_getdata(qd->ld.login_key, sizeof(qd->ld.login_key), data + bytes);
+		return QQ_LOGIN_REPLY_OK;
+	}
+
+	switch (ret)
+	{
+		case 0xc6:		/* invalid password */
+			if (!purple_account_get_remember_password(gc->account)) {
+				purple_account_set_password(gc->account, NULL);
+			}
+			error = g_strdup(_("Error password"));
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+			break;
+		case 0x33:		/* need activation */
+		case 0x51:		/* need activation */
+			error = g_strdup(_("Need active"));
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+			break;
+		case 0xBF:		/* uid is not exist */
+			error = g_strdup(_("invalid user name"));
+			reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
+			break;
+		default:
+			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+					">>> [default] decrypt and dump");
+			error = g_strdup_printf(
+						_("Unknow reply code when checking password (0x%02X)"),
+						ret );
+			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+			break;
+	}
+
+	bytes = 11;
+	bytes += qq_get16(&msg_len, data + bytes);
+
+	msg = g_strndup((gchar *)data + bytes, msg_len);
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+
+	purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
+	purple_connection_error_reason(gc, reason, msg_utf8);
+
+	g_free(error);
+	g_free(msg);
+	g_free(msg_utf8);
+	return QQ_LOGIN_REPLY_ERR;
+}
+
+void qq_request_check_pwd_2008(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted;
+	gint encrypted_len;
+	static guint8 header[] = { 0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0xE0 };
+	static guint8 unknown[] = { 0xDB, 0xB9, 0xF3, 0x0B, 0xF9, 0x13, 0x87, 0xB2,
+		0xE6, 0x20, 0x43, 0xBE, 0x53, 0xCA, 0x65, 0x03 };
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+
+	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+
+	/* Encrypted password and put in encrypted */
+	bytes = 0;
+	bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_2nd_md5, sizeof(qd->ld.pwd_2nd_md5));
+	bytes += qq_put16(raw_data + bytes, 0);
+	bytes += qq_put16(raw_data + bytes, (guint16) (rand() & 0xffff));
+
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_4th_md5);
+
+	/* create packet */
+	bytes = 0;
+	bytes += qq_putdata(raw_data + bytes, header, sizeof(header));
+	/* token get from qq_request_token_ex */
+	bytes += qq_put16(raw_data + bytes, qd->ld.token_ex_len);
+	bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
+	bytes += qq_put8(raw_data + bytes, 0);
+	/* password encrypted */
+	bytes += qq_put16(raw_data + bytes, encrypted_len);
+	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+	bytes += qq_put8(raw_data + bytes, 0);
+	/* len of unknown + len of CRC32 */
+	bytes += qq_put8(raw_data + bytes, sizeof(unknown) + 4);
+	bytes += qq_putdata(raw_data + bytes, unknown, sizeof(unknown));
+	bytes += qq_put32(
+			raw_data + bytes, crc32(0xFFFFFFFF, unknown, sizeof(unknown)));
+	/* put length into first 2 bytes */
+	qq_put16(raw_data, bytes - 2);
+	/* tail */
+	bytes += qq_put8(raw_data + bytes, 0);
+	bytes += qq_put8(raw_data + bytes, 0x03);
+	bytes += qq_put8(raw_data + bytes, 0);
+	bytes += qq_put8(raw_data + bytes, qd->ld.pwd_2nd_md5[1]);
+	bytes += qq_put8(raw_data + bytes, qd->ld.pwd_2nd_md5[2]);
+
+	/* Encrypted by random key*/
+	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
+	bytes = 0;
+	bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+	qd->send_seq++;
+	qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
+}
+
+/*
+static void qq_send_packet_login2007(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 *buf, *cursor, *cursor_verify_data;
+	guint16 seq_ret;
+	gint encrypted_len, bytes;
+	gint pos, bodyOffset, encrypted_data_bytes, tail_offset, body_length, temp_pos;
+	guint8 verifyData[QQ_LOGIN_DATA_LENGTH], raw_data[QQ_LOGIN_ENCRYPT_BUFFER], encrypted_data[QQ_LOGIN_DATA_LENGTH];
+	
+	memset(raw_data, 0, QQ_LOGIN_ENCRYPT_BUFFER);
+	memset(verifyData, 0, QQ_LOGIN_DATA_LENGTH);
+	memset(encrypted_data, 0, QQ_LOGIN_DATA_LENGTH);
+	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_LOGIN, TRUE, &seq_ret);
+	bytes += create_packet_dw(buf, &cursor, qd->uid);
+
+	bodyOffset = bytes;
+
+	bytes += create_packet_w(buf, &cursor, qd->passport_key_lenght);
+	bytes += create_packet_data(buf, &cursor, qd->passport_key, qd->passport_key_lenght);
+	bytes += create_packet_w(buf, &cursor, 0);
+	pos = bytes;
+	bytes += 2;
+	cursor += 2;
+
+	encrypted_data_bytes = 0;
+	cursor_verify_data = verifyData;
+	encrypted_data_bytes += create_packet_data(verifyData, &cursor_verify_data, qd->pwkey, QQ_KEY_LENGTH);
+	encrypted_data_bytes += create_packet_b(verifyData, &cursor_verify_data, 0);
+	encrypted_data_bytes += create_packet_b(verifyData, &cursor_verify_data, 0);
+	encrypted_data_bytes += create_packet_b(verifyData, &cursor_verify_data, 255);
+	encrypted_data_bytes += create_packet_b(verifyData, &cursor_verify_data, 255);
+//	encrypted_data_bytes += create_packet_w(verifyData, &cursor_verify_data, 0);
+	
+	qq_encrypt(verifyData, encrypted_data_bytes, qd->pwkey_double, encrypted_data, &encrypted_len);
+	bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
+	
+	temp_pos = bytes;
+	bytes = pos;
+	cursor = buf+bytes;
+	bytes += create_packet_w(buf, &cursor, encrypted_len);
+	bytes = temp_pos;
+	cursor = buf+bytes;
+	
+	qq_encrypt((guint8 *) "", 0, qd->pwkey_double, raw_data, &encrypted_len);
+	bytes += create_packet_data(buf, &cursor, raw_data, encrypted_len);
+	
+	g_memmove(buf+bytes, kQQFixedContent1_35, 35);
+	bytes += 35;
+	cursor = buf+bytes;
+	
+	bytes += create_packet_b(buf, &cursor, g_random_int_range (0,255));
+	bytes += create_packet_b(buf, &cursor, qd->login_mode);
+	
+	bytes += create_packet_dw(buf, &cursor, 0);
+	bytes += create_packet_dw(buf, &cursor, 0);
+	bytes += create_packet_w(buf, &cursor, 0);
+	
+	bytes += create_packet_data(buf, &cursor, qd->selected_server, qd->selected_server_lenght);
+	
+	g_memmove(buf+bytes, kQQFixedContent2_16, 16);
+	bytes += 16;
+	cursor = buf+bytes;
+	
+	bytes += create_packet_b(buf, &cursor, qd->login_token_lenght);
+	bytes += create_packet_data(buf, &cursor, qd->login_token, qd->login_token_lenght);
+
+	g_memmove(buf+bytes, kQQFixedContent3_332, 332);
+	bytes += 332;
+	cursor = buf+bytes;
+	
+	tail_offset = bytes;
+	body_length = tail_offset - bodyOffset;
+
+	memset(raw_data, 0, QQ_LOGIN_ENCRYPT_BUFFER);
+	encrypted_len = body_length;
+//	qq_encrypt(buf+passport_length+2+11, body_length-passport_length-2 , qd->pwkey, raw_data, &encrypted_len);
+	qq_encrypt(buf+qd->passport_key_lenght+2+11, body_length-qd->passport_key_lenght-2 , qd->login_key, raw_data, &encrypted_len);
+	bytes = qd->passport_key_lenght+2+11;
+	cursor = buf+bytes;
+	bytes += create_packet_data(buf, &cursor, raw_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");
+	
+}
+ */
+void qq_request_login_2007(PurpleConnection *gc)
+{
+}
+
+void qq_request_login_2008(PurpleConnection *gc)
+{
+}
============================================================
--- libpurple/protocols/qq/qq_base.h	ea7caf396b13c3400b8d302a782cef8aa3761425
+++ libpurple/protocols/qq/qq_base.h	50d174aa4b537a8df7d8cc35713d83765e22b125
@@ -30,13 +30,9 @@
 
 #define QQ_LOGIN_REPLY_OK							0x00
 #define QQ_LOGIN_REPLY_REDIRECT				0x01
-#define QQ_LOGIN_REPLY_ERR_PWD					0x05
-#define QQ_LOGIN_REPLY_NEED_REACTIVE		0x06
-#define QQ_LOGIN_REPLY_REDIRECT_EX			0x0A
 /* defined by myself */
 #define QQ_LOGIN_REPLY_CAPTCHA_DLG			0xfc
 #define QQ_LOGIN_REPLY_NEXT_TOKEN_EX		0xfd
-#define QQ_LOGIN_REPLY_ERR_DECRYPT			0xfe
 #define QQ_LOGIN_REPLY_ERR							0xff
 
 #define QQ_LOGIN_MODE_NORMAL		0x0a
@@ -48,11 +44,6 @@ guint8 qq_process_token(PurpleConnection
 void qq_request_token(PurpleConnection *gc);
 guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len);
 
-void qq_request_token_ex(PurpleConnection *gc);
-void qq_request_token_ex_next(PurpleConnection *gc);
-guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len);
-void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha);
-
 void qq_request_login(PurpleConnection *gc);
 guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len);
 
@@ -61,7 +52,24 @@ gboolean qq_process_keep_alive(guint8 *d
 void qq_request_keep_alive(PurpleConnection *gc);
 gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc);
 
-/* for QQ2007 */
+/* for QQ2007/2008 */
 void qq_request_get_server(PurpleConnection *gc);
 guint16 qq_process_get_server(PurpleConnection *gc, guint8 *rcved, gint rcved_len);
+
+void qq_request_token_ex(PurpleConnection *gc);
+void qq_request_token_ex_next(PurpleConnection *gc);
+guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len);
+void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha);
+
+void qq_request_check_pwd_2007(PurpleConnection *gc);
+guint8 qq_process_check_pwd_2007( PurpleConnection *gc, guint8 *data, gint data_len);
+
+void qq_request_check_pwd_2008(PurpleConnection *gc);
+guint8 qq_process_check_pwd_2008( PurpleConnection *gc, guint8 *data, gint data_len);
+
+void qq_request_login_2007(PurpleConnection *gc);
+guint8 qq_process_login_2007( PurpleConnection *gc, guint8 *data, gint data_len);
+
+void qq_request_login_2008(PurpleConnection *gc);
+guint8 qq_process_login_2008( PurpleConnection *gc, guint8 *data, gint data_len);
 #endif
============================================================
--- libpurple/protocols/qq/qq_define.c	b0b876be933b82e32bcef47e673b4fabe5ab339a
+++ libpurple/protocols/qq/qq_define.c	a862490bbf4d332a5e148c48c33d811b812d1858
@@ -51,9 +51,8 @@
 #define QQ_CLIENT_0F4B 0x0F4B	/* QQ2006 Beta 3  */
 
 #define QQ_CLIENT_1105 0x1105	/* QQ2007 beta4*/
-#define QQ_CLIENT_115B 0x115B	/* QQ2008 */
 #define QQ_CLIENT_1203 0x1203	/* QQ2008 */
-#define QQ_CLIENT_1205 0x1205	/* QQ2008 */
+#define QQ_CLIENT_1205 0x1205	/* QQ2008 Qi Fu */
 #define QQ_CLIENT_120B 0x120B	/* QQ2008 July 8.0.978.400 */
 #define QQ_CLIENT_1412 0x1412	/* QQMac 1.0 preview1 build 670 */
 #define QQ_CLIENT_1441 0x1441	/* QQ2009 preview2 */
@@ -174,6 +173,18 @@ const gchar *qq_get_cmd_desc(gint cmd)
 		return "QQ_CMD_RECV_MSG_SYS";
 	case QQ_CMD_BUDDY_CHANGE_STATUS:
 		return "QQ_CMD_BUDDY_CHANGE_STATUS";
+	case QQ_CMD_GET_SERVER:
+		return "QQ_CMD_GET_SERVER";
+	case QQ_CMD_TOKEN_EX:
+		return "QQ_CMD_TOKEN_EX";
+	case QQ_CMD_CHECK_PWD:
+		return "QQ_CMD_CHECK_PWD";
+	case QQ_CMD_BUDDY_AUTH:
+		return "QQ_CMD_BUDDY_AUTH";
+	case QQ_CMD_BUDDY_ADD_NO_AUTH_EX:
+		return "QQ_CMD_BUDDY_ADD_NO_AUTH_EX";
+	case QQ_CMD_BUDDY_ADD_AUTH_EX:
+		return "QQ_CMD_BUDDY_ADD_AUTH_EX";
 	default:
 		return "Unknown CMD";
 	}
============================================================
--- libpurple/protocols/qq/qq_define.h	5048b85b1fc951d59bfc35da16f22c9f10cabfd2
+++ libpurple/protocols/qq/qq_define.h	bd128147801873bb5085307282051520167bf846
@@ -35,6 +35,7 @@
 
 #define QQ_CLIENT_0D55 0x0d55	/* QQ2005 used by openq before */
 #define QQ_CLIENT_111D 0x111D	/* QQ2007 */
+#define QQ_CLIENT_115B 0x115B	/* QQ2008 He Sui*/
 
 const gchar *qq_get_ver_desc(gint source);
 
@@ -69,7 +70,7 @@ enum {
 	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_GET_CAPTCHA = 0x00AE,				/* the request verification of information */
+	QQ_CMD_BUDDY_AUTH = 0x00AE,				/* the request verification of information */
 	QQ_CMD_BUDDY_ADD_NO_AUTH_EX = 0x00A7,			/* add friend without auth */
 	QQ_CMD_BUDDY_ADD_AUTH_EX = 0x00A8, 				/* add buddy with auth */
 };
============================================================
--- libpurple/protocols/qq/qq_network.c	dbde8647a131927c84dbcbf99f73ea6a03605ca2
+++ libpurple/protocols/qq/qq_network.c	633a2fb01e5d4d9570670883c0b7e96b4aea23fb
@@ -202,7 +202,8 @@ gboolean qq_connect_later(gpointer data)
 
 	if (qd->curr_server == NULL || strlen (qd->curr_server) == 0 || qd->connect_retry <= 0) {
 		if ( set_new_server(qd) != TRUE) {
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			purple_connection_error_reason(gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 					_("Failed to connect all servers"));
 			return FALSE;
 		}
@@ -220,7 +221,8 @@ gboolean qq_connect_later(gpointer data)
 
 	qd->connect_retry--;
 	if ( !connect_to_server(gc, server, port) ) {
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Unable to connect."));
 	}
 
@@ -353,7 +355,8 @@ static void tcp_pending(gpointer data, g
 	qd = (qq_data *) gc->proto_data;
 
 	if(cond != PURPLE_INPUT_READ) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Socket error"));
 		return;
 	}
@@ -377,7 +380,9 @@ static void tcp_pending(gpointer data, g
 			return;
 
 		error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno));
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				error_msg);
 		g_free(error_msg);
 		return;
 	} else if (buf_len == 0) {
@@ -479,7 +484,8 @@ static void udp_pending(gpointer data, g
 	qd = (qq_data *) gc->proto_data;
 
 	if(cond != PURPLE_INPUT_READ) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Socket error"));
 		return;
 	}
@@ -489,7 +495,8 @@ static void udp_pending(gpointer data, g
 	/* here we have UDP proxy suppport */
 	buf_len = read(source, buf, MAX_PACKET_SIZE);
 	if (buf_len <= 0) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Unable to read from socket"));
 		return;
 	}
@@ -537,7 +544,9 @@ static gint udp_send_out(PurpleConnectio
 	if (ret < 0) {
 		/* TODO: what to do here - do we really have to disconnect? */
 		purple_debug_error("UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno));
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				g_strerror(errno));
 	}
 	return ret;
 }
@@ -569,8 +578,9 @@ static void tcp_can_write(gpointer data,
 		return;
 	else if (ret < 0) {
 		/* TODO: what to do here - do we really have to disconnect? */
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-		                               _("Write Error"));
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		        _("Write Error"));
 		return;
 	}
 
@@ -614,7 +624,9 @@ static gint tcp_send_out(PurpleConnectio
 		/* TODO: what to do here - do we really have to disconnect? */
 		purple_debug_error("TCP_SEND_OUT",
 			"Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno));
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				g_strerror(errno));
 		return ret;
 	}
 
@@ -641,7 +653,8 @@ static gboolean network_timeout(gpointer
 	is_lost_conn = qq_trans_scan(gc);
 	if (is_lost_conn) {
 		purple_connection_error_reason(gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Connection lost"));
 		return TRUE;
 	}
 
@@ -674,6 +687,8 @@ static void set_all_keys(PurpleConnectio
 {
 	qq_data *qd;
 	const gchar *passwd;
+	guint8 *dest;
+	int dest_len = QQ_KEY_LENGTH;
 
 	/* _qq_show_socket("Got login socket", source); */
 
@@ -689,26 +704,24 @@ static void set_all_keys(PurpleConnectio
 	qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
 
 #ifdef DEBUG
-	memset(qd->ld.init_key, 0x01, sizeof(qd->ld.init_key));
-	memset(qd->ld.captcha_key, 0x02, sizeof(qd->ld.captcha_key));
+	memset(qd->ld.random_key, 0x01, sizeof(qd->ld.random_key));
 #else
-	for (bytes = 0; bytes < sizeof(qd->ld.init_key); bytes++)	{
-		qd->ld.init_key[bytes] = (guint8) (rand() & 0xff);
+	for (bytes = 0; bytes < sizeof(qd->ld.random_key); bytes++)	{
+		qd->ld.random_key[bytes] = (guint8) (rand() & 0xff);
 	}
-	for (bytes = 0; bytes < sizeof(qd->captcha_key); bytes++)	{
-		qd->captcha_key[bytes] = (guint8) (rand() & 0xff);
-	}
 #endif
 
 	/* now generate md5 processed passwd */
 	passwd = purple_account_get_password(purple_connection_get_account(gc));
 
 	/* use twice-md5 of user password as session key since QQ 2003iii */
-	qq_get_md5(qd->ld.pwd_twice_md5, sizeof(qd->ld.pwd_twice_md5),
-		(guint8 *)passwd, strlen(passwd));
-	qq_get_md5(qd->ld.pwd_twice_md5, sizeof(qd->ld.pwd_twice_md5),
-		qd->ld.pwd_twice_md5, sizeof(qd->ld.pwd_twice_md5));
+	dest = qd->ld.pwd_2nd_md5;
+	qq_get_md5(dest, dest_len, (guint8 *)passwd, strlen(passwd));
+	qq_get_md5(dest, dest_len, dest, dest_len);
 
+	dest = qd->ld.pwd_4th_md5;
+	qq_get_md5(dest, dest_len, qd->ld.pwd_2nd_md5, dest_len);
+	qq_get_md5(dest, dest_len, dest, dest_len);
 }
 
 /* the callback function after socket is built
@@ -758,7 +771,7 @@ static void connect_cb(gpointer data, gi
 
 	set_all_keys( gc );
 
-	if (qd->is_above_2007) {
+	if (qd->client_version > 2005) {
 		purple_connection_update_progress(gc, _("Get server ..."), 2, QQ_CONNECT_STEPS);
 		qq_request_get_server(gc);
 		return;
@@ -836,8 +849,8 @@ static void udp_host_resolved(GSList *ho
 
 	if (!hosts || !hosts->data) {
 		purple_connection_error_reason(gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Couldn't resolve host"));
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Couldn't resolve host"));
 		return;
 	}
 
@@ -915,7 +928,8 @@ gboolean connect_to_server(PurpleConnect
 	qd = (qq_data *) gc->proto_data;
 
 	if (server == NULL || strlen(server) == 0 || port == 0) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Invalid server or port"));
 		return FALSE;
 	}
@@ -1006,9 +1020,10 @@ void qq_disconnect(PurpleConnection *gc)
 
 	qq_trans_remove_all(gc);
 
-	memset(qd->ld.init_key, 0, sizeof(qd->ld.init_key));
-	memset(qd->ld.captcha_key, 0, sizeof(qd->ld.captcha_key));
-	memset(qd->ld.pwd_twice_md5, 0, sizeof(qd->ld.pwd_twice_md5));
+	memset(qd->ld.random_key, 0, sizeof(qd->ld.random_key));
+	memset(qd->ld.pwd_2nd_md5, 0, sizeof(qd->ld.pwd_2nd_md5));
+	memset(qd->ld.pwd_4th_md5, 0, sizeof(qd->ld.pwd_4th_md5));
+	memset(qd->ld.login_key, 0, sizeof(qd->ld.login_key));
 	memset(qd->session_key, 0, sizeof(qd->session_key));
 	memset(qd->session_md5, 0, sizeof(qd->session_md5));
 
@@ -1032,7 +1047,7 @@ static gint packet_encap(qq_data *qd, gu
 	}
 	/* now comes the normal QQ packet as UDP */
 	bytes += qq_put8(buf + bytes, QQ_PACKET_TAG);
-	bytes += qq_put16(buf + bytes, qd->client_version);
+	bytes += qq_put16(buf + bytes, qd->client_tag);
 	bytes += qq_put16(buf + bytes, cmd);
 
 	bytes += qq_put16(buf + bytes, seq);
@@ -1079,7 +1094,7 @@ gint qq_send_cmd_encrypted(PurpleConnect
 }
 
 gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq,
-	guint8 *encrypted_data, gint encrypted_len, gboolean is_save2trans)
+	guint8 *encrypted, gint encrypted_len, gboolean is_save2trans)
 {
 	gint sent_len;
 
@@ -1089,9 +1104,9 @@ gint qq_send_cmd_encrypted(PurpleConnect
 				seq, qq_get_cmd_desc(cmd), cmd, encrypted_len);
 #endif
 
-	sent_len = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
+	sent_len = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
 	if (is_save2trans)  {
-		qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len, 0, 0);
+		qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len, 0, 0);
 	}
 	return sent_len;
 }
@@ -1101,7 +1116,7 @@ static gint send_cmd_detail(PurpleConnec
 	guint8 *data, gint data_len, gboolean is_save2trans, gint update_class, guint32 ship32)
 {
 	qq_data *qd;
-	guint8 *encrypted_data;
+	guint8 *encrypted;
 	gint encrypted_len;
 	gint bytes_sent;
 
@@ -1110,18 +1125,18 @@ static gint send_cmd_detail(PurpleConnec
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
 	/* at most 16 bytes more */
-	encrypted_data = g_newa(guint8, data_len + 16);
-	encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key);
+	encrypted = g_newa(guint8, data_len + 16);
+	encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key);
 	if (encrypted_len < 16) {
 		purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n",
 				encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
 		return -1;
 	}
 
-	bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
+	bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
 
 	if (is_save2trans)  {
-		qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len,
+		qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len,
 				update_class, ship32);
 	}
 	return bytes_sent;
@@ -1174,7 +1189,7 @@ gint qq_send_server_reply(PurpleConnecti
 gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
 {
 	qq_data *qd;
-	guint8 *encrypted_data;
+	guint8 *encrypted;
 	gint encrypted_len;
 	gint bytes_sent;
 
@@ -1187,16 +1202,16 @@ gint qq_send_server_reply(PurpleConnecti
 				seq, qq_get_cmd_desc(cmd), cmd, data_len);
 #endif
 	/* at most 16 bytes more */
-	encrypted_data = g_newa(guint8, data_len + 16);
-	encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key);
+	encrypted = g_newa(guint8, data_len + 16);
+	encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key);
 	if (encrypted_len < 16) {
 		purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n",
 				encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
 		return -1;
 	}
 
-	bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
-	qq_trans_add_server_reply(gc, cmd, seq, encrypted_data, encrypted_len);
+	bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
+	qq_trans_add_server_reply(gc, cmd, seq, encrypted, encrypted_len);
 
 	return bytes_sent;
 }
@@ -1207,7 +1222,7 @@ static gint send_room_cmd(PurpleConnecti
 	qq_data *qd;
 	guint8 *buf;
 	gint buf_len;
-	guint8 *encrypted_data;
+	guint8 *encrypted;
 	gint encrypted_len;
 	gint bytes_sent;
 	guint16 seq;
@@ -1232,17 +1247,17 @@ static gint send_room_cmd(PurpleConnecti
 	qd->send_seq++;
 	seq = qd->send_seq;
 
-	/* Encrypt to encrypted_data with session_key */
+	/* Encrypt to encrypted with session_key */
 	/* at most 16 bytes more */
-	encrypted_data = g_newa(guint8, buf_len + 16);
-	encrypted_len = qq_encrypt(encrypted_data, buf, buf_len, qd->session_key);
+	encrypted = g_newa(guint8, buf_len + 16);
+	encrypted_len = qq_encrypt(encrypted, buf, buf_len, qd->session_key);
 	if (encrypted_len < 16) {
 		purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] %s (0x%02X)\n",
 				encrypted_len, seq, qq_get_room_cmd_desc(room_cmd), room_cmd);
 		return -1;
 	}
 
-	bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len);
+	bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted, encrypted_len);
 #if 1
 		/* qq_show_packet("send_room_cmd", buf, buf_len); */
 		purple_debug_info("QQ",
@@ -1250,7 +1265,7 @@ static gint send_room_cmd(PurpleConnecti
 				seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
 #endif
 
-	qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, encrypted_data, encrypted_len,
+	qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, encrypted, encrypted_len,
 			update_class, ship32);
 	return bytes_sent;
 }
@@ -1258,6 +1273,7 @@ gint qq_send_room_cmd_mess(PurpleConnect
 gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
 		guint8 *data, gint data_len, gint update_class, guint32 ship32)
 {
+	g_return_val_if_fail(room_id > 0, -1);
 	return send_room_cmd(gc, room_cmd, room_id, data, data_len, update_class, ship32);
 }
 
============================================================
--- libpurple/protocols/qq/qq_process.c	3d29b82d408d5a9504ea576ebac6eed88032156e
+++ libpurple/protocols/qq/qq_process.c	f299a881d70bbf12c5a9dfdb4f3b90fc65cdc5b8
@@ -82,7 +82,7 @@ static void process_cmd_unknow(PurpleCon
 }
 
 /* Send ACK if the sys message needs an ACK */
-static void _qq_send_packet_ack_msg_sys(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
+static void ack_server_msg(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
 {
 	qq_data *qd;
 	guint8 bar, *ack;
@@ -112,7 +112,7 @@ static void _qq_send_packet_ack_msg_sys(
 			   "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
 }
 
-static void _qq_process_msg_sys_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+static void process_server_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
 {
 	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar *title, *content;
@@ -120,13 +120,13 @@ static void _qq_process_msg_sys_notice(P
 	g_return_if_fail(from != NULL && to != NULL);
 
 	title = g_strdup_printf(_("From %s:"), from);
-	content = g_strdup_printf(_("%s"), msg_utf8);
+	content = g_strdup_printf(_("Server notice From %s: \n%s"), from, msg_utf8);
 
 	if (qd->is_show_notice) {
-		purple_notify_info(gc, _("QQ Server Notice"), title, content);
+		qq_got_attention(gc, content);
 	} else {
 		purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8);
-}
+	}
 	g_free(title);
 	g_free(content);
 }
@@ -148,7 +148,7 @@ static void process_server_msg(guint8 *d
 	to = segments[2];
 	msg = segments[3];
 
-	_qq_send_packet_ack_msg_sys(gc, code[0], strtol(from, NULL, 10), seq);
+	ack_server_msg(gc, code[0], strtol(from, NULL, 10), seq);
 
 	if (strtol(to, NULL, 10) != qd->uid) {	/* not to me */
 		purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
@@ -173,13 +173,14 @@ static void process_server_msg(guint8 *d
 			qq_process_buddy_from_server(gc,  funct, from, to, msg_utf8);
 			break;
 		case QQ_SERVER_NOTICE:
-			_qq_process_msg_sys_notice(gc, from, to, msg_utf8);
+			process_server_notice(gc, from, to, msg_utf8);
 			break;
 		case QQ_SERVER_NEW_CLIENT:
 			purple_debug_warning("QQ", "QQ Server has newer client version\n");
 			break;
 		default:
 			purple_debug_warning("QQ", "Recv unknown sys msg code: %s\nMsg: %s\n", code, msg_utf8);
+			break;
 	}
 	g_free(msg_utf8);
 	g_strfreev(segments);
@@ -509,7 +510,7 @@ void qq_proc_room_cmds(PurpleConnection 
 	/* seems ok so far, so we process the reply according to sub_cmd */
 	switch (reply_cmd) {
 	case QQ_ROOM_CMD_GET_INFO:
-		qq_process_room_cmd_get_info(data + bytes, data_len - bytes, gc);
+		qq_process_room_cmd_get_info(data + bytes, data_len - bytes, ship32, gc);
 		break;
 	case QQ_ROOM_CMD_CREATE:
 		qq_group_process_create_group_reply(data + bytes, data_len - bytes, gc);
@@ -588,7 +589,7 @@ guint8 qq_proc_login_cmds(PurpleConnecti
 	switch (cmd) {
 		case QQ_CMD_TOKEN:
 			if (qq_process_token(gc, rcved, rcved_len) == QQ_LOGIN_REPLY_OK) {
-				if (qd->is_above_2007) {
+				if (qd->client_version > 2005) {
 					qq_request_token_ex(gc);
 				} else {
 					qq_request_login(gc);
@@ -598,17 +599,29 @@ guint8 qq_proc_login_cmds(PurpleConnecti
 			return QQ_LOGIN_REPLY_ERR;
 		case QQ_CMD_GET_SERVER:
 		case QQ_CMD_TOKEN_EX:
-			data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.init_key);
+			data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
 			break;
+		case QQ_CMD_CHECK_PWD:
+			/* May use password_twice_md5 in the past version like QQ2005 */
+			data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
+			if (data_len >= 0) {
+				purple_debug_warning("QQ", "Decrypt login packet by random_key, %d bytes\n", data_len);
+			} else {
+				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_4th_md5);
+				if (data_len >= 0) {
+					purple_debug_warning("QQ", "Decrypt login packet by pwd_4th_md5, %d bytes\n", data_len);
+				}
+			}
+			break;
 		default:
 			/* May use password_twice_md5 in the past version like QQ2005 */
-			data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.init_key);
+			data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
 			if (data_len >= 0) {
-				purple_debug_warning("QQ", "Decrypt login packet by init_key, %d bytes\n", data_len);
+				purple_debug_warning("QQ", "Decrypt login packet by random_key, %d bytes\n", data_len);
 			} else {
-				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
+				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_2nd_md5);
 				if (data_len >= 0) {
-					purple_debug_warning("QQ", "Decrypt login packet by password_twice_md5, %d bytes\n", data_len);
+					purple_debug_warning("QQ", "Decrypt login packet by pwd_2nd_md5, %d bytes\n", data_len);
 				}
 			}
 			break;
@@ -620,9 +633,9 @@ guint8 qq_proc_login_cmds(PurpleConnecti
 				seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
 		qq_show_packet("Can not decrypted", rcved, rcved_len);
 		purple_connection_error_reason(gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
 				_("Can not decrypt login reply"));
-		return QQ_LOGIN_REPLY_ERR_DECRYPT;
+		return QQ_LOGIN_REPLY_ERR;
 	}
 
 	switch (cmd) {
@@ -637,7 +650,11 @@ guint8 qq_proc_login_cmds(PurpleConnecti
 		case QQ_CMD_TOKEN_EX:
 			ret_8 = qq_process_token_ex(gc, data, data_len);
 			if (ret_8 == QQ_LOGIN_REPLY_OK) {
-				//qq_request_check_password(gc);
+				if (qd->client_version == 2008) {
+					qq_request_check_pwd_2008(gc);
+				} else {
+					qq_request_check_pwd_2007(gc);
+				}
 			} else if (ret_8 == QQ_LOGIN_REPLY_NEXT_TOKEN_EX) {
 				qq_request_token_ex_next(gc);
 			} else if (ret_8 == QQ_LOGIN_REPLY_CAPTCHA_DLG) {
@@ -647,6 +664,21 @@ guint8 qq_proc_login_cmds(PurpleConnecti
 				memset(&qd->captcha, 0, sizeof(qd->captcha));
 			}
 			break;
+		case QQ_CMD_CHECK_PWD:
+			if (qd->client_version == 2008) {
+				ret_8 = qq_process_check_pwd_2008(gc, data, data_len);
+			} else {
+				ret_8 = qq_process_check_pwd_2007(gc, data, data_len);
+			}
+			if (ret_8 != QQ_LOGIN_REPLY_OK) {
+				return  ret_8;
+			}
+			if (qd->client_version == 2008) {
+				qq_request_login_2008(gc);
+			} else {
+				qq_request_login_2007(gc);
+			}
+			break;
 		case QQ_CMD_LOGIN:
 			ret_8 = qq_process_login(gc, data, data_len);
 			if (ret_8 != QQ_LOGIN_REPLY_OK) {
============================================================
--- libpurple/protocols/qq/send_file.c	591962a2446a469711b3abaf0c80d7a4b76faf81
+++ libpurple/protocols/qq/send_file.c	271b750f28e52519c0b0cdef1c6ca880d5b418dc
@@ -301,7 +301,7 @@ static gint _qq_create_packet_file_heade
 	/* 004-007: sender uid */
 	bytes += qq_put32 (raw_data + bytes, to_uid);
 	/* 008-009: sender client version */
-	bytes += qq_put16 (raw_data + bytes, qd->client_version);
+	bytes += qq_put16 (raw_data + bytes, qd->client_tag);
 	/* 010-013: receiver uid */
 	bytes += qq_put32 (raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
@@ -804,7 +804,7 @@ void qq_process_recv_file_request(guint8
 	/* FACE from IP detector, ignored by gfhuang */
 	if(g_ascii_strcasecmp(fileinfo[0], "FACE") == 0) {
 		purple_debug_warning("QQ",
-			    "Received a FACE ip detect from qq-%d, so he/she must be online :)\n", sender_uid);
+			    "Received a FACE ip detect from %d, so he/she must be online :)\n", sender_uid);
 
 		b = purple_find_buddy(gc->account, sender_name);
 		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
============================================================
--- libpurple/protocols/qq/utils.c	4a81d7cc987d03659ec91b083f2fb6ab3e706b92
+++ libpurple/protocols/qq/utils.c	4e79bc0cd7a01c7cf733cb0e2cf94c8301fb696d
@@ -191,7 +191,7 @@ gchar *chat_name_to_purple_name(const gc
 
 	g_return_val_if_fail(name != NULL, NULL);
 
-	tmp = (gchar *) purple_strcasestr(name, "(qq-");
+	tmp = (gchar *) purple_strcasestr(name, "(");
 	ret = g_strndup(tmp + 4, strlen(name) - (tmp - name) - 4 - 1);
 
 	return ret;
@@ -335,7 +335,7 @@ void qq_hex_dump(PurpleDebugLevel level,
 }
 
 void qq_hex_dump(PurpleDebugLevel level, const char *category,
-		const guint8 *pdata, gint bytes,	
+		const guint8 *pdata, gint bytes,
 		const char *format, ...)
 {
 	va_list args;


More information about the Commits mailing list