changeset 24065:bdfcfd71449c

patch 20080922 from ccpaging <ccpaging(at)gmail.com>
author SHiNE CsyFeK <csyfek@gmail.com>
date Wed, 22 Oct 2008 14:33:20 +0000
parents a3cd7c3d9da1
children dbc7a9742f8d
files libpurple/protocols/qq/Makefile.am libpurple/protocols/qq/Makefile.mingw 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.c libpurple/protocols/qq/group_conv.c libpurple/protocols/qq/group_im.c libpurple/protocols/qq/group_info.c libpurple/protocols/qq/group_join.c libpurple/protocols/qq/group_opt.c libpurple/protocols/qq/group_search.c libpurple/protocols/qq/im.c 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/qq_process.h libpurple/protocols/qq/qq_trans.c libpurple/protocols/qq/send_file.c
diffstat 25 files changed, 788 insertions(+), 296 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/Makefile.am	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Wed Oct 22 14:33:20 2008 +0000
@@ -36,8 +36,8 @@
 	group_opt.h \
 	group_search.c \
 	group_search.h \
-	header_info.c \
-	header_info.h \
+	qq_define.c \
+	qq_define.h \
 	im.c \
 	im.h \
 	qq_process.c \
--- a/libpurple/protocols/qq/Makefile.mingw	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Wed Oct 22 14:33:20 2008 +0000
@@ -55,7 +55,7 @@
 	group_join.c \
 	group_opt.c \
 	group_search.c \
-	header_info.c \
+	qq_define.c \
 	im.c \
 	packet_parse.c \
 	qq.c \
--- a/libpurple/protocols/qq/buddy_info.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Wed Oct 22 14:33:20 2008 +0000
@@ -32,7 +32,7 @@
 #include "buddy_list.h"
 #include "buddy_info.h"
 #include "char_conv.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "qq_base.h"
 #include "qq_network.h"
 
@@ -426,7 +426,11 @@
 	info_request->iclass = iclass;
 	info_request->segments = segments;
 
-	purple_request_fields(gc, utf8_title, utf8_prim, NULL,	fields,
+	purple_request_fields(gc,
+			utf8_title,
+			utf8_prim,
+			NULL,
+			fields,
 			_("Update"), G_CALLBACK(info_modify_ok_cb),
 			_("Cancel"), G_CALLBACK(info_modify_cancel_cb),
 			purple_connection_get_account(gc), NULL, NULL,
--- a/libpurple/protocols/qq/buddy_list.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Wed Oct 22 14:33:20 2008 +0000
@@ -34,7 +34,7 @@
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "qq_base.h"
 #include "group.h"
 #include "group_find.h"
@@ -78,7 +78,6 @@
 	bytes += qq_put16(raw_data + bytes, 0x0000);
 
 	qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5, update_class, 0);
-	qd->last_get_online = time(NULL);
 }
 
 /* position starts with 0x0000,
--- a/libpurple/protocols/qq/buddy_opt.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Wed Oct 22 14:33:20 2008 +0000
@@ -32,7 +32,7 @@
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "im.h"
 #include "qq_base.h"
 #include "packet_parse.h"
--- a/libpurple/protocols/qq/file_trans.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/file_trans.c	Wed Oct 22 14:33:20 2008 +0000
@@ -30,7 +30,7 @@
 
 #include "qq_crypt.h"
 #include "file_trans.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "im.h"
 #include "packet_parse.h"
 #include "proxy.h"
@@ -81,7 +81,7 @@
 	const gint QQ_MAX_FILE_MD5_LENGTH = 10002432;
 
 	g_return_if_fail(filename != NULL && md5 != NULL);
-	if (filelen > QQ_MAX_FILE_MD5_LENGTH) 
+	if (filelen > QQ_MAX_FILE_MD5_LENGTH)
 		filelen = QQ_MAX_FILE_MD5_LENGTH;
 
 	fp = fopen(filename, "rb");
@@ -161,7 +161,7 @@
 		fd = open(purple_xfer_get_local_filename(xfer), O_RDONLY);
 		info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ, MAP_PRIVATE, fd, 0);
 	}
-	else 
+	else
 	{
 		fd = open(purple_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644);
 		info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0);
@@ -248,7 +248,7 @@
 	file_key = _gen_file_key();
 
 	bytes += qq_put8(raw_data + bytes, packet_type);
-	bytes += qq_put16(raw_data + bytes, QQ_CLIENT);
+	bytes += qq_put16(raw_data + bytes, qd->client_version);
 	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));
@@ -364,7 +364,7 @@
 }
 
 /* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */
-static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, 
+static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type,
 		guint32 fragment_index, guint16 seq, guint8 *data, gint len)
 {
 	guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
@@ -402,11 +402,11 @@
 					_fill_file_md5(purple_xfer_get_local_filename(qd->xfer),
 							purple_xfer_get_size(qd->xfer),
 							file_md5);
-					
+
 					info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1;
 					info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN;
 
-					purple_debug_info("QQ", 
+					purple_debug_info("QQ",
 							"start transfering data, %d fragments with %d length each\n",
 							info->fragment_num, info->fragment_len);
 					/* Unknown */
@@ -431,7 +431,7 @@
 							filename_len);
 					break;
 				case QQ_FILE_DATA_INFO:
-					purple_debug_info("QQ", 
+					purple_debug_info("QQ",
 							"sending %dth fragment with length %d, offset %d\n",
 							fragment_index, len, (fragment_index-1)*fragment_size);
 					/* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */
@@ -532,7 +532,7 @@
 			decryped_bytes = 0;
 			qq_get_conn_info(info, decrypted_data + decryped_bytes);
 			/* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
-			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);	
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);
 			break;
 		case QQ_FILE_CMD_SENDER_SAY_HELLO:
 			/* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
@@ -573,8 +573,8 @@
 	ft_info *info = (ft_info *) xfer->data;
 	guint32 mask;
 
-	purple_debug_info("QQ", 
-			"receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n", 
+	purple_debug_info("QQ",
+			"receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n",
 			index, len, info->window, info->max_fragment_index);
 	if (info->window == 0 && info->max_fragment_index == 0) {
 		if (_qq_xfer_open_file(purple_xfer_get_local_filename(xfer), "wb", xfer) == -1) {
@@ -605,7 +605,7 @@
 		if (mask & 0x8000) mask = 0x0001;
 		else mask = mask << 1;
 	}
-	purple_debug_info("QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n", 
+	purple_debug_info("QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n",
 			index, info->window, info->max_fragment_index);
 }
 
@@ -650,10 +650,10 @@
 	PurpleXfer *xfer = qd->xfer;
 	ft_info *info = (ft_info *) xfer->data;
 
-	purple_debug_info("QQ", 
-			"receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n", 
+	purple_debug_info("QQ",
+			"receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n",
 			fragment_index, info->window, info->max_fragment_index);
-	if (fragment_index < info->max_fragment_index || 
+	if (fragment_index < info->max_fragment_index ||
 			fragment_index >= info->max_fragment_index + sizeof(info->window)) {
 		purple_debug_info("QQ", "duplicate %dth fragment, drop it!\n", fragment_index+1);
 		return;
@@ -681,7 +681,7 @@
 			info->window &= ~mask;
 
 			buffer = g_newa(guint8, info->fragment_len);
-			readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window), 
+			readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window),
 					info->fragment_len, xfer);
 			if (readbytes > 0)
 				_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
@@ -692,8 +692,8 @@
 			else mask = mask << 1;
 		}
 	}
-	purple_debug_info("QQ", 
-			"procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n", 
+	purple_debug_info("QQ",
+			"procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n",
 			fragment_index, info->window, info->max_fragment_index);
 }
 
@@ -727,13 +727,13 @@
 					bytes += qq_get32(&info->fragment_num, data + bytes);
 					bytes += qq_get32(&info->fragment_len, data + bytes);
 
-					/* FIXME: We must check the md5 here, 
-					 * if md5 doesn't match we will ignore 
+					/* FIXME: We must check the md5 here,
+					 * if md5 doesn't match we will ignore
 					 * the packet or send sth as error number */
 
 					info->max_fragment_index = 0;
 					info->window = 0;
-					purple_debug_info("QQ", 
+					purple_debug_info("QQ",
 							"start receiving data, %d fragments with %d length each\n",
 							info->fragment_num, info->fragment_len);
 					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
@@ -743,7 +743,7 @@
 					bytes += qq_get32(&fragment_index, data + bytes);
 					bytes += qq_get32(&fragment_offset, data + bytes);
 					bytes += qq_get16(&fragment_len, data + bytes);
-					purple_debug_info("QQ", 
+					purple_debug_info("QQ",
 							"received %dth fragment with length %d, offset %d\n",
 							fragment_index, fragment_len, fragment_offset);
 
--- a/libpurple/protocols/qq/group.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/group.c	Wed Oct 22 14:33:20 2008 +0000
@@ -33,7 +33,7 @@
 #include "group_search.h"
 #include "utils.h"
 #include "qq_network.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "group_free.h"
 
 static void _qq_group_search_callback(PurpleConnection *gc, const gchar *input)
--- a/libpurple/protocols/qq/group_conv.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/group_conv.c	Wed Oct 22 14:33:20 2008 +0000
@@ -27,7 +27,7 @@
 
 #include "group_conv.h"
 #include "buddy_list.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "qq_network.h"
 #include "qq_process.h"
 #include "utils.h"
--- a/libpurple/protocols/qq/group_im.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Wed Oct 22 14:33:20 2008 +0000
@@ -39,7 +39,7 @@
 #include "group_opt.h"
 #include "group_conv.h"
 #include "im.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "packet_parse.h"
 #include "qq_network.h"
 #include "qq_process.h"
--- a/libpurple/protocols/qq/group_info.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Wed Oct 22 14:33:20 2008 +0000
@@ -32,7 +32,7 @@
 #include "group_internal.h"
 #include "group_info.h"
 #include "buddy_list.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "packet_parse.h"
 #include "qq_network.h"
 #include "utils.h"
--- a/libpurple/protocols/qq/group_join.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Wed Oct 22 14:33:20 2008 +0000
@@ -38,7 +38,7 @@
 #include "group_opt.h"
 #include "group_conv.h"
 #include "group_search.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "packet_parse.h"
 #include "qq_network.h"
 #include "qq_process.h"
--- a/libpurple/protocols/qq/group_opt.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Wed Oct 22 14:33:20 2008 +0000
@@ -35,7 +35,7 @@
 #include "group_info.h"
 #include "group_join.h"
 #include "group_opt.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "packet_parse.h"
 #include "qq_network.h"
 #include "qq_process.h"
--- a/libpurple/protocols/qq/group_search.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/group_search.c	Wed Oct 22 14:33:20 2008 +0000
@@ -33,7 +33,7 @@
 #include "group_join.h"
 #include "group_search.h"
 #include "utils.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "packet_parse.h"
 #include "qq_network.h"
 
--- a/libpurple/protocols/qq/im.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Wed Oct 22 14:33:20 2008 +0000
@@ -36,7 +36,7 @@
 #include "buddy_opt.h"
 #include "char_conv.h"
 #include "group_im.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "im.h"
 #include "packet_parse.h"
 #include "qq_network.h"
@@ -502,7 +502,7 @@
 {
 	qq_data *qd;
 	guint8 *raw_data, *send_im_tail;
-	guint16 client_tag, normal_im_type;
+	guint16 normal_im_type;
 	gint msg_len, raw_len, font_name_len, tail_len, bytes;
 	time_t now;
 	gchar *msg_filtered;
@@ -512,7 +512,6 @@
 	const gchar *start, *end, *last;
 
 	qd = (qq_data *) gc->proto_data;
-	client_tag = QQ_CLIENT;
 	normal_im_type = QQ_NORMAL_IM_TEXT;
 
 	last = msg;
@@ -572,7 +571,7 @@
 	/* 004-007: sender uid */
 	bytes += qq_put32(raw_data + bytes, to_uid);
 	/* 008-009: sender client version */
-	bytes += qq_put16(raw_data + bytes, client_tag);
+	bytes += qq_put16(raw_data + bytes, qd->client_version);
 	/* 010-013: receiver uid */
 	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
--- a/libpurple/protocols/qq/qq.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Wed Oct 22 14:33:20 2008 +0000
@@ -44,7 +44,7 @@
 #include "group_info.h"
 #include "group_join.h"
 #include "group_opt.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "im.h"
 #include "qq_process.h"
 #include "qq_base.h"
@@ -128,6 +128,7 @@
 	PurpleConnection *gc;
 	qq_data *qd;
 	PurplePresence *presence;
+	const gchar *version_str;
 
 	g_return_if_fail(account != NULL);
 
@@ -154,6 +155,16 @@
 	server_list_create(account);
 	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;
+	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->is_show_notice = purple_account_get_bool(account, "show_notice", TRUE);
 	qd->is_show_news = purple_account_get_bool(account, "show_news", TRUE);
 
@@ -203,6 +214,11 @@
 	}
 
 	qq_disconnect(gc);
+
+	if (qd->ld.token) g_free(qd->ld.token);
+	if (qd->captcha.token) g_free(qd->captcha.token);
+	if (qd->captcha.data) g_free(qd->captcha.data);
+
 	server_list_remove_all(qd);
 
 	g_free(qd);
@@ -536,18 +552,20 @@
 	GString *info;
 
 	qd = (qq_data *) gc->proto_data;
-	info = g_string_new("<html><body>\n");
+	info = g_string_new("<html><body>");
 
-	g_string_append_printf(info, _("<b>Current Online</b>: %d<br>\n"), qd->total_online);
-	g_string_append_printf(info, _("<b>Last Refresh</b>: %s<br>\n"), ctime(&qd->last_get_online));
+	g_string_append_printf(info, _("<b>This Login</b>: %s<br>\n"), ctime(&qd->login_time));
+	g_string_append_printf(info, _("<b>Online Buddies</b>: %d<br>\n"), qd->online_total);
+	g_string_append_printf(info, _("<b>Last Refresh</b>: %s<br>\n"), ctime(&qd->online_last_update));
 
-	g_string_append(info, "<hr>\n");
+	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>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP");
-	g_string_append_printf(info, _("<b>My Internet Address</b>: %s<br>\n"), inet_ntoa(qd->my_ip));
+	g_string_append_printf(info, _("<b>My Internet IP</b>: %s<br>\n"), inet_ntoa(qd->my_ip));
 
-	g_string_append(info, "<hr>\n");
+	g_string_append(info, "<hr>");
 	g_string_append(info, "<i>Network Status</i><br>\n");
 	g_string_append_printf(info, _("<b>Sent</b>: %lu<br>\n"), qd->net_stat.sent);
 	g_string_append_printf(info, _("<b>Resend</b>: %lu<br>\n"), qd->net_stat.resend);
@@ -555,12 +573,11 @@
 	g_string_append_printf(info, _("<b>Received</b>: %lu<br>\n"), qd->net_stat.rcved);
 	g_string_append_printf(info, _("<b>Received Duplicate</b>: %lu<br>\n"), qd->net_stat.rcved_dup);
 
-	g_string_append(info, "<hr>\n");
+	g_string_append(info, "<hr>");
 	g_string_append(info, "<i>Information below may not be accurate</i><br>\n");
 
-	g_string_append_printf(info, _("<b>Login Time</b>: %s<br>\n"), ctime(&qd->login_time));
+	g_string_append_printf(info, _("<b>Last Login</b>: %s\n"), ctime(&qd->last_login_time));
 	g_string_append_printf(info, _("<b>Last Login IP</b>: %s<br>\n"), qd->last_login_ip);
-	g_string_append_printf(info, _("<b>Last Login Time</b>: %s\n"), ctime(&qd->last_login_time));
 
 	g_string_append(info, "</body></html>");
 
@@ -848,35 +865,53 @@
 {
 	PurpleAccountOption *option;
 	PurpleKeyValuePair *kvp;
-	GList *list = NULL;
-	GList *kvlist = NULL;
-	GList *entry;
+	GList *server_list = NULL;
+	GList *server_kv_list = NULL;
+	GList *it;
+	GList *version_kv_list = NULL;
 
-	list = server_list_build('A');
+	server_list = server_list_build('A');
 
-	purple_prefs_add_string_list("/plugins/prpl/qq/serverlist", list);
-	list = purple_prefs_get_string_list("/plugins/prpl/qq/serverlist");
+	purple_prefs_add_string_list("/plugins/prpl/qq/serverlist", server_list);
+	server_list = purple_prefs_get_string_list("/plugins/prpl/qq/serverlist");
 
-	kvlist = NULL;
+	server_kv_list = NULL;
 	kvp = g_new0(PurpleKeyValuePair, 1);
 	kvp->key = g_strdup(_("Auto"));
 	kvp->value = g_strdup("auto");
-	kvlist = g_list_append(kvlist, kvp);
+	server_kv_list = g_list_append(server_kv_list, kvp);
 
-	entry = list;
-	while(entry) {
-		if (entry->data != NULL && strlen(entry->data) > 0) {
+	it = server_list;
+	while(it) {
+		if (it->data != NULL && strlen(it->data) > 0) {
 			kvp = g_new0(PurpleKeyValuePair, 1);
-			kvp->key = g_strdup(entry->data);
-			kvp->value = g_strdup(entry->data);
-			kvlist = g_list_append(kvlist, kvp);
+			kvp->key = g_strdup(it->data);
+			kvp->value = g_strdup(it->data);
+			server_kv_list = g_list_append(server_kv_list, kvp);
 		}
-		entry = entry->next;
+		it = it->next;
 	}
 
-	option = purple_account_option_list_new(_("Server"), "server", kvlist);
+	g_list_free(server_list);
+
+	option = purple_account_option_list_new(_("Select Server"), "server", server_kv_list);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
+#ifdef DEBUG
+	kvp = g_new0(PurpleKeyValuePair, 1);
+	kvp->key = g_strdup(_("QQ2005"));
+	kvp->value = g_strdup("qq2005");
+	version_kv_list = g_list_append(version_kv_list, kvp);
+
+	kvp = g_new0(PurpleKeyValuePair, 1);
+	kvp->key = g_strdup(_("QQ2007"));
+	kvp->value = g_strdup("qq2007");
+	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
+
 	option = purple_account_option_bool_new(_("Connect by TCP"), "use_tcp", TRUE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
--- a/libpurple/protocols/qq/qq.h	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Wed Oct 22 14:33:20 2008 +0000
@@ -41,6 +41,35 @@
 typedef struct _qq_interval qq_interval;
 typedef struct _qq_net_stat qq_net_stat;
 typedef struct _qq_add_request qq_add_request;
+typedef struct _qq_redirect_data qq_redirect_data;
+typedef struct _qq_login_data qq_login_data;
+typedef struct _qq_captcha_data qq_captcha_data;
+
+struct _qq_captcha_data {
+	guint8 *token;
+	guint16 token_len;
+	guint8 next_index;
+	guint8 *data;
+	guint16 data_len;
+};
+
+struct _qq_login_data {
+	guint8 init_key[QQ_KEY_LENGTH];			/* first encrypt key generated by client */
+	guint8 *token;		/* get from server*/
+	guint8 token_len;
+	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) */
+};
+
+struct _qq_redirect_data {
+	guint16 ret;
+	guint8 b1;
+	guint32 w1;
+	guint32 w2;
+	guint32 ip;
+};
 
 struct _qq_add_request {
 	guint32 uid;
@@ -111,8 +140,13 @@
 	GList *servers;
 	gchar *curr_server;		/* point to servers->data, do not free*/
 
+	guint16 client_version;
+	gboolean is_above_2007;
+
 	struct in_addr redirect_ip;
 	guint16 redirect_port;
+	qq_redirect_data redirect_data;
+
 	guint check_watcher;
 	guint connect_watcher;
 	gint connect_retry;
@@ -125,10 +159,10 @@
 	GList *transactions;	/* check ack packet and resend */
 
 	guint32 uid;			/* QQ number */
-	guint8 *token;		/* get from server*/
-	int token_len;
-	guint8 inikey[QQ_KEY_LENGTH];			/* initial key to encrypt login packet */
-	guint8 password_twice_md5[QQ_KEY_LENGTH];			/* password in md5 (or md5' md5) */
+	
+	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 */
 
@@ -147,8 +181,8 @@
 	guint16 my_port;		/* my port detected by server */
 	guint16 my_icon;		/* my icon index */
 	guint16 my_level;		/* my level */
-	guint32 total_online;		/* the number of online QQ users */
-	time_t last_get_online;		/* last time send get_friends_online packet */
+	guint32 online_total;		/* the number of online QQ users */
+	time_t online_last_update;		/* last time send get_friends_online packet */
 
 	PurpleRoomlist *roomlist;
 	gint channel;			/* the id for opened chat conversation */
--- a/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:33:20 2008 +0000
@@ -26,13 +26,14 @@
 #include "internal.h"
 #include "server.h"
 #include "cipher.h"
+#include "request.h"
 
 #include "buddy_info.h"
 #include "buddy_list.h"
 #include "char_conv.h"
 #include "qq_crypt.h"
 #include "group.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "qq_base.h"
 #include "packet_parse.h"
 #include "qq.h"
@@ -101,39 +102,6 @@
 */
 
 
-typedef struct _qq_login_reply_ok qq_login_reply_ok_packet;
-typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet;
-
-struct _qq_login_reply_ok {
-	guint8 result;
-	guint8 session_key[QQ_KEY_LENGTH];
-	guint32 uid;
-	struct in_addr client_ip;	/* those detected by server */
-	guint16 client_port;
-	struct in_addr server_ip;
-	guint16 server_port;
-	time_t login_time;
-	guint8 unknown1[26];
-	struct in_addr unknown_server1_ip;
-	guint16 unknown_server1_port;
-	struct in_addr unknown_server2_ip;
-	guint16 unknown_server2_port;
-	guint16 unknown2;	/* 0x0001 */
-	guint16 unknown3;	/* 0x0000 */
-	guint8 unknown4[32];
-	guint8 unknown5[12];
-	struct in_addr last_client_ip;
-	time_t last_login_time;
-	guint8 unknown6[8];
-};
-
-struct _qq_login_reply_redirect {
-	guint8 result;
-	guint32 uid;
-	struct in_addr new_server_ip;
-	guint16 new_server_port;
-};
-
 /* generate a md5 key using uid and session_key */
 static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key)
 {
@@ -151,53 +119,75 @@
 {
 	gint bytes;
 	qq_data *qd;
-	qq_login_reply_ok_packet lrop;
+
+	struct {
+		guint8 result;
+		guint8 session_key[QQ_KEY_LENGTH];
+		guint32 uid;
+		struct in_addr client_ip;	/* those detected by server */
+		guint16 client_port;
+		struct in_addr server_ip;
+		guint16 server_port;
+		time_t login_time;
+		guint8 unknown1[26];
+		struct in_addr unknown_server1_ip;
+		guint16 unknown_server1_port;
+		struct in_addr unknown_server2_ip;
+		guint16 unknown_server2_port;
+		guint16 unknown2;	/* 0x0001 */
+		guint16 unknown3;	/* 0x0000 */
+		guint8 unknown4[32];
+		guint8 unknown5[12];
+		struct in_addr last_client_ip;
+		time_t last_login_time;
+		guint8 unknown6[8];
+	} packet;
 
 	qd = (qq_data *) gc->proto_data;
 	/* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
 	bytes = 0;
 
 	/* 000-000: reply code */
-	bytes += qq_get8(&lrop.result, data + bytes);
+	bytes += qq_get8(&packet.result, data + bytes);
 	/* 001-016: session key */
-	bytes += qq_getdata(lrop.session_key, sizeof(lrop.session_key), data + bytes);
+	bytes += qq_getdata(packet.session_key, sizeof(packet.session_key), data + bytes);
 	purple_debug_info("QQ", "Got session_key\n");
 	/* 017-020: login uid */
-	bytes += qq_get32(&lrop.uid, data + bytes);
+	bytes += qq_get32(&packet.uid, data + bytes);
 	/* 021-024: server detected user public IP */
-	bytes += qq_getIP(&lrop.client_ip, data + bytes);
+	bytes += qq_getIP(&packet.client_ip, data + bytes);
 	/* 025-026: server detected user port */
-	bytes += qq_get16(&lrop.client_port, data + bytes);
+	bytes += qq_get16(&packet.client_port, data + bytes);
 	/* 027-030: server detected itself ip 127.0.0.1 ? */
-	bytes += qq_getIP(&lrop.server_ip, data + bytes);
+	bytes += qq_getIP(&packet.server_ip, data + bytes);
 	/* 031-032: server listening port */
-	bytes += qq_get16(&lrop.server_port, data + bytes);
+	bytes += qq_get16(&packet.server_port, data + bytes);
 	/* 033-036: login time for current session */
-	bytes += qq_getime(&lrop.login_time, data + bytes);
+	bytes += qq_getime(&packet.login_time, data + bytes);
 	/* 037-062: 26 bytes, unknown */
-	bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes);
+	bytes += qq_getdata((guint8 *) &packet.unknown1, 26, data + bytes);
 	/* 063-066: unknown server1 ip address */
-	bytes += qq_getIP(&lrop.unknown_server1_ip, data + bytes);
+	bytes += qq_getIP(&packet.unknown_server1_ip, data + bytes);
 	/* 067-068: unknown server1 port */
-	bytes += qq_get16(&lrop.unknown_server1_port, data + bytes);
+	bytes += qq_get16(&packet.unknown_server1_port, data + bytes);
 	/* 069-072: unknown server2 ip address */
-	bytes += qq_getIP(&lrop.unknown_server2_ip, data + bytes);
+	bytes += qq_getIP(&packet.unknown_server2_ip, data + bytes);
 	/* 073-074: unknown server2 port */
-	bytes += qq_get16(&lrop.unknown_server2_port, data + bytes);
+	bytes += qq_get16(&packet.unknown_server2_port, data + bytes);
 	/* 075-076: 2 bytes unknown */
-	bytes += qq_get16(&lrop.unknown2, data + bytes);
+	bytes += qq_get16(&packet.unknown2, data + bytes);
 	/* 077-078: 2 bytes unknown */
-	bytes += qq_get16(&lrop.unknown3, data + bytes);
+	bytes += qq_get16(&packet.unknown3, data + bytes);
 	/* 079-110: 32 bytes unknown */
-	bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes);
+	bytes += qq_getdata((guint8 *) &packet.unknown4, 32, data + bytes);
 	/* 111-122: 12 bytes unknown */
-	bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes);
+	bytes += qq_getdata((guint8 *) &packet.unknown5, 12, data + bytes);
 	/* 123-126: login IP of last session */
-	bytes += qq_getIP(&lrop.last_client_ip, data + bytes);
+	bytes += qq_getIP(&packet.last_client_ip, data + bytes);
 	/* 127-130: login time of last session */
-	bytes += qq_getime(&lrop.last_login_time, data + bytes);
+	bytes += qq_getime(&packet.last_login_time, data + bytes);
 	/* 131-138: 8 bytes unknown */
-	bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes);
+	bytes += qq_getdata((guint8 *) &packet.unknown6, 8, data + bytes);
 
 	if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) {	/* fail parsing login info */
 		purple_debug_warning("QQ",
@@ -205,15 +195,15 @@
 			   QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
 	}			/* but we still go on as login OK */
 
-	memcpy(qd->session_key, lrop.session_key, sizeof(qd->session_key));
+	memcpy(qd->session_key, packet.session_key, sizeof(qd->session_key));
 	get_session_md5(qd->session_md5, qd->uid, qd->session_key);
 
-	qd->my_ip.s_addr = lrop.client_ip.s_addr;
+	qd->my_ip.s_addr = packet.client_ip.s_addr;
 
-	qd->my_port = lrop.client_port;
-	qd->login_time = lrop.login_time;
-	qd->last_login_time = lrop.last_login_time;
-	qd->last_login_ip = g_strdup( inet_ntoa(lrop.last_client_ip) );
+	qd->my_port = packet.client_port;
+	qd->login_time = packet.login_time;
+	qd->last_login_time = packet.last_login_time;
+	qd->last_login_ip = g_strdup( inet_ntoa(packet.last_client_ip) );
 
 	return QQ_LOGIN_REPLY_OK;
 }
@@ -223,35 +213,41 @@
 {
 	qq_data *qd;
 	gint bytes;
-	qq_login_reply_redirect_packet lrrp;
+	struct {
+		guint8 result;
+		guint32 uid;
+		struct in_addr new_server_ip;
+		guint16 new_server_port;
+	} packet;
+
 
 	qd = (qq_data *) gc->proto_data;
 	bytes = 0;
 	/* 000-000: reply code */
-	bytes += qq_get8(&lrrp.result, data + bytes);
+	bytes += qq_get8(&packet.result, data + bytes);
 	/* 001-004: login uid */
-	bytes += qq_get32(&lrrp.uid, data + bytes);
+	bytes += qq_get32(&packet.uid, data + bytes);
 	/* 005-008: redirected new server IP */
-	bytes += qq_getIP(&lrrp.new_server_ip, data + bytes);
+	bytes += qq_getIP(&packet.new_server_ip, data + bytes);
 	/* 009-010: redirected new server port */
-	bytes += qq_get16(&lrrp.new_server_port, data + bytes);
+	bytes += qq_get16(&packet.new_server_port, data + bytes);
 
 	if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
 		purple_debug_error("QQ",
 			   "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n",
 			   QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes);
-		return QQ_LOGIN_REPLY_ERR_MISC;
+		return QQ_LOGIN_REPLY_ERR;
 	}
 
 	/* redirect to new server, do not disconnect or connect here
 	 * those connect should be called at packet_process */
-	qd->redirect_ip.s_addr = lrrp.new_server_ip.s_addr;
-	qd->redirect_port = lrrp.new_server_port;
+	qd->redirect_ip.s_addr = packet.new_server_ip.s_addr;
+	qd->redirect_port = packet.new_server_port;
 	return QQ_LOGIN_REPLY_REDIRECT;
 }
 
 /* request before login */
-void qq_send_packet_token(PurpleConnection *gc)
+void qq_request_token(PurpleConnection *gc)
 {
 	qq_data *qd;
 	guint8 buf[16] = {0};
@@ -267,7 +263,7 @@
 }
 
 /* send login packet to QQ server */
-void qq_send_packet_login(PurpleConnection *gc)
+void qq_request_login(PurpleConnection *gc)
 {
 	qq_data *qd;
 	guint8 *buf, *raw_data;
@@ -278,15 +274,7 @@
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	g_return_if_fail(qd->token != NULL && qd->token_len > 0);
-
-#ifdef DEBUG
-	memset(qd->inikey, 0x01, sizeof(qd->inikey));
-#else
-	for (bytes = 0; bytes < sizeof(qd->inikey); bytes++)	{
-		qd->inikey[bytes] = (guint8) (rand() & 0xff);
-	}
-#endif
+	g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
 
 	raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
 	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
@@ -296,7 +284,7 @@
 	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->password_twice_md5);
+	encrypted_len = qq_encrypt(raw_data + bytes, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
 	g_return_if_fail(encrypted_len == 16);
 	bytes += encrypted_len;
 
@@ -313,26 +301,26 @@
 	/* 053-068, fixed value, maybe related to per machine */
 	bytes += qq_putdata(raw_data + bytes, login_53_68, 16);
 	/* 069, login token length */
-	bytes += qq_put8(raw_data + bytes, qd->token_len);
+	bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
 	/* 070-093, login token, normally 24 bytes */
-	bytes += qq_putdata(raw_data + bytes, qd->token, qd->token_len);
+	bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
 	/* 100 bytes unknown */
 	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->inikey);
+	encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
 	bytes = 0;
-	bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH);
 	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
 
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
 }
 
-guint8 qq_process_token_reply(PurpleConnection *gc, guint8 *buf, gint buf_len)
+guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len)
 {
 	qq_data *qd;
 	guint8 ret;
@@ -346,7 +334,7 @@
 
 	ret = buf[0];
 
-	if (ret != QQ_TOKEN_REPLY_OK) {
+	if (ret != QQ_LOGIN_REPLY_OK) {
 		purple_debug_error("QQ", "Failed to request token: %d\n", buf[0]);
 		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
 				buf, buf_len,
@@ -357,7 +345,7 @@
 		}
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
 		g_free(error_msg);
-		return ret;
+		return QQ_LOGIN_REPLY_ERR;
 	}
 
 	token_len = buf_len-2;
@@ -365,7 +353,7 @@
 		error_msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
 		g_free(error_msg);
-		return -1;
+		return QQ_LOGIN_REPLY_ERR;
 	}
 
 	if (buf[1] != token_len) {
@@ -376,34 +364,39 @@
 			buf+2, token_len,
 			"<<< got a token -> [default] decrypt and dump");
 
-	qd->token = g_new0(guint8, token_len);
-	qd->token_len = token_len;
-	g_memmove(qd->token, buf + 2, qd->token_len);
+	if (qd->ld.token != NULL) {
+		g_free(qd->ld.token);
+		qd->ld.token = NULL;
+		qd->ld.token_len = 0;
+	}
+	qd->ld.token = g_new0(guint8, token_len);
+	qd->ld.token_len = token_len;
+	g_memmove(qd->ld.token, buf + 2, qd->ld.token_len);
 	return ret;
 }
 
 /* send logout packets to QQ server */
-void qq_send_packet_logout(PurpleConnection *gc)
+void qq_request_logout(PurpleConnection *gc)
 {
 	gint i;
 	qq_data *qd;
 
 	qd = (qq_data *) gc->proto_data;
 	for (i = 0; i < 4; i++)
-		qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->password_twice_md5, QQ_KEY_LENGTH);
+		qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->ld.pwd_twice_md5, QQ_KEY_LENGTH);
 
 	qd->is_login = FALSE;	/* update login status AFTER sending logout packets */
 }
 
 /* process the login reply packet */
-guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_len)
+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;
 
-	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR_MISC);
+	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
 
 	qd = (qq_data *) gc->proto_data;
 
@@ -472,7 +465,7 @@
 }
 
 /* send keep-alive packet to QQ server (it is a heart-beat) */
-void qq_send_packet_keep_alive(PurpleConnection *gc)
+void qq_request_keep_alive(PurpleConnection *gc)
 {
 	qq_data *qd;
 	guint8 raw_data[16] = {0};
@@ -505,8 +498,8 @@
 			return TRUE;
 
 	/* segments[0] and segment[1] are all 0x30 ("0") */
-	qd->total_online = strtol(segments[2], NULL, 10);
-	if(0 == qd->total_online) {
+	qd->online_total = strtol(segments[2], NULL, 10);
+	if(0 == qd->online_total) {
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Keep alive error"));
 	}
@@ -519,3 +512,341 @@
 	g_strfreev(segments);
 	return TRUE;
 }
+
+/* For QQ2007/2008 */
+void qq_request_get_server(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted_data;
+	gint encrypted_len;
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	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 */
+
+	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);
+
+	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);
+
+	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)
+{
+	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;
+	}
+
+	if (data_len < sizeof(qq_redirect_data)) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Can not decrypt get server reply"));
+		return QQ_LOGIN_REPLY_ERR;
+	}
+
+	redirect = (qq_redirect_data *)data;
+	if (redirect->ret == 0) {
+		memset(&qd->redirect_data, 0, sizeof(qd->redirect_data));
+		qd->redirect_ip.s_addr = 0;
+		return QQ_LOGIN_REPLY_OK;
+	}
+
+	g_memmove(&qd->redirect_data, redirect, sizeof(qd->redirect_data));
+	g_memmove(&qd->redirect_ip, &redirect->ip, sizeof(qd->redirect_ip));
+	return QQ_LOGIN_REPLY_REDIRECT;
+}
+
+void qq_request_token_ex(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted_data;
+	gint encrypted_len;
+
+	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_data = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
+	bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
+	bytes += qq_put8(raw_data + bytes, 3); 		/* Subcommand */
+	bytes += qq_put16(raw_data + bytes, 5);
+	bytes += qq_put32(raw_data + bytes, 0);
+	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);
+
+	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);
+
+	qd->send_seq++;
+	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
+}
+
+void qq_request_token_ex_next(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted_data;
+	gint encrypted_len;
+
+	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_data = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
+	bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
+	bytes += qq_put8(raw_data + bytes, 3); 		/* Subcommand */
+	bytes += qq_put16(raw_data + bytes, 5);
+	bytes += qq_put32(raw_data + bytes, 0);
+	bytes += qq_put8(raw_data + bytes, qd->captcha.next_index); 		/* fragment index */
+	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);
+
+	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);
+
+	qd->send_seq++;
+	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
+
+	purple_connection_update_progress(gc, _("Requesting captcha ..."), 3, QQ_CONNECT_STEPS);
+}
+
+static void request_token_ex_code(PurpleConnection *gc,
+		guint8 *token, guint16 token_len, guint8 *code, guint16 code_len)
+{
+	qq_data *qd;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted_data;
+	gint encrypted_len;
+
+	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);
+	g_return_if_fail(code != NULL && code_len > 0);
+
+	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 */
+
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
+	bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
+	bytes += qq_put8(raw_data + bytes, 4); 		/* Subcommand */
+	bytes += qq_put16(raw_data + bytes, 5);
+	bytes += qq_put32(raw_data + bytes, 0);
+	bytes += qq_put16(raw_data + bytes, code_len);
+	bytes += qq_putdata(raw_data + bytes, code, code_len);
+	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);
+
+	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);
+
+	qd->send_seq++;
+	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
+
+	purple_connection_update_progress(gc, _("Checking code of  captcha ..."), 3, QQ_CONNECT_STEPS);
+}
+
+typedef struct {
+	PurpleConnection *gc;
+	guint8 *token;
+	guint16 token_len;
+} qq_captcha_request;
+
+static void captcha_request_destory(qq_captcha_request *captcha_req)
+{
+	g_return_if_fail(captcha_req != NULL);
+	if (captcha_req->token) g_free(captcha_req->token);
+	g_free(captcha_req);
+}
+
+static void captcha_input_cancel_cb(qq_captcha_request *captcha_req,
+		PurpleRequestFields *fields)
+{
+	captcha_request_destory(captcha_req);
+
+	purple_connection_error_reason(captcha_req->gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+			_("Failed captcha verify"));
+}
+
+static void captcha_input_ok_cb(qq_captcha_request *captcha_req,
+		PurpleRequestFields *fields)
+{
+	gchar *code;
+
+	g_return_if_fail(captcha_req != NULL && captcha_req->gc != NULL);
+
+	code = utf8_to_qq(
+			purple_request_fields_get_string(fields, "captcha_code"),
+			QQ_CHARSET_DEFAULT);
+
+	if (strlen(code) <= 0) {
+		captcha_input_cancel_cb(captcha_req, fields);
+		return;
+	}
+
+	request_token_ex_code(captcha_req->gc,
+			captcha_req->token, captcha_req->token_len,
+			(guint8 *)code, strlen(code));
+
+	captcha_request_destory(captcha_req);
+}
+
+void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha)
+{
+	PurpleAccount *account;
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+	qq_captcha_request *captcha_req;
+
+	g_return_if_fail(captcha->token != NULL && captcha->token_len > 0);
+	g_return_if_fail(captcha->data != NULL && captcha->data_len > 0);
+
+	captcha_req = g_new0(qq_captcha_request, 1);
+	captcha_req->gc = gc;
+	captcha_req->token = g_new0(guint8, captcha->token_len);
+	g_memmove(captcha_req->token, captcha->token, captcha->token_len);
+	captcha_req->token_len = captcha->token_len;
+
+	account = purple_connection_get_account(gc);
+
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+
+	field = purple_request_field_image_new("captcha_img",
+			_("Captcha Image"), (char *)captcha->data, captcha->data_len);
+	purple_request_field_group_add_field(group, field);
+
+	field = purple_request_field_string_new("captcha_code",
+			_("Enter code"), "", FALSE);
+	purple_request_field_string_set_masked(field, FALSE);
+	purple_request_field_group_add_field(group, field);
+
+	purple_request_fields(account,
+		_("QQ Captcha Verifing"),
+		_("QQ Captcha Verifing"),
+		_("Please fill code according to image"),
+		fields,
+		_("OK"), G_CALLBACK(captcha_input_ok_cb),
+		_("Cancel"), G_CALLBACK(captcha_input_cancel_cb),
+		purple_connection_get_account(gc), NULL, NULL,
+		captcha_req);
+}
+
+guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len)
+{
+	qq_data *qd;
+	int bytes;
+	guint8 ret;
+	guint8 sub_cmd;
+	guint8 reply;
+	guint16 captcha_len;
+	guint8 curr_index;
+
+	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
+
+	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, -1);
+	qd = (qq_data *) gc->proto_data;
+
+	ret = buf[0];
+
+	bytes = 0;
+	bytes += qq_get8(&sub_cmd, buf + bytes);
+	bytes += 2;
+	bytes += qq_get8(&reply, buf + bytes);
+
+	bytes += qq_get16(&(qd->ld.token_ex_len), buf + 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);
+
+	if(reply != 1)
+	{
+		purple_debug_info("QQ", "All captchaes is verified\n");
+		return QQ_LOGIN_REPLY_OK;
+	}
+
+	bytes += qq_get16(&captcha_len, buf + 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);
+	qd->captcha.data_len += captcha_len;
+
+	bytes += qq_get8(&curr_index, buf + bytes);
+	bytes += qq_get8(&qd->captcha.next_index, buf + bytes);
+
+	bytes += qq_get16(&qd->captcha.token_len, buf + bytes);
+	qd->captcha.token = g_new0(guint8, qd->captcha.token_len);
+	bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, buf + bytes);
+
+	if(qd->captcha.next_index > 0)
+	{
+		return QQ_LOGIN_REPLY_NEXT_TOKEN_EX;
+	}
+
+	return QQ_LOGIN_REPLY_CAPTCHA_DLG;
+}
--- a/libpurple/protocols/qq/qq_base.h	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.h	Wed Oct 22 14:33:20 2008 +0000
@@ -28,29 +28,40 @@
 #include <glib.h>
 #include "connection.h"
 
-#define QQ_TOKEN_REPLY_OK 	0x00
-
 #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
-#define QQ_LOGIN_REPLY_ERR_MISC				0xff	/* defined by myself */
+/* 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
-#define QQ_LOGIN_MODE_AWAY	    0x1e
-#define QQ_LOGIN_MODE_HIDDEN        0x28
+#define QQ_LOGIN_MODE_NORMAL		0x0a
+#define QQ_LOGIN_MODE_AWAY	    	0x1e
+#define QQ_LOGIN_MODE_HIDDEN		0x28
 
 #define QQ_UPDATE_ONLINE_INTERVAL   300	/* in sec */
 
-void qq_send_packet_token(PurpleConnection *gc);
-guint8 qq_process_token_reply(PurpleConnection *gc, guint8 *buf, gint buf_len);
+void qq_request_token(PurpleConnection *gc);
+guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len);
 
-void qq_send_packet_login(PurpleConnection *gc);
-guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_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_send_packet_logout(PurpleConnection *gc);
+void qq_request_login(PurpleConnection *gc);
+guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len);
+
+void qq_request_logout(PurpleConnection *gc);
 
-void qq_send_packet_keep_alive(PurpleConnection *gc);
+void qq_request_keep_alive(PurpleConnection *gc);
 gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc);
+
+/* for QQ2007 */
+void qq_request_get_server(PurpleConnection *gc);
+guint16 qq_process_get_server(PurpleConnection *gc, guint8 *rcved, gint rcved_len);
 #endif
--- a/libpurple/protocols/qq/qq_define.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.c	Wed Oct 22 14:33:20 2008 +0000
@@ -1,5 +1,5 @@
 /**
- * @file header_info.c
+ * @file qq_define.c
  *
  * purple
  *
@@ -24,7 +24,7 @@
 
 #include "internal.h"
 
-#include "header_info.h"
+#include "qq_define.h"
 
 #define QQ_CLIENT_062E 0x062e	/* GB QQ2000c build 0630 */
 #define QQ_CLIENT_072E 0x072e	/* EN QQ2000c build 0305 */
@@ -51,7 +51,6 @@
 #define QQ_CLIENT_0F4B 0x0F4B	/* QQ2006 Beta 3  */
 
 #define QQ_CLIENT_1105 0x1105	/* QQ2007 beta4*/
-#define QQ_CLIENT_111D 0x111D	/* QQ2007 */
 #define QQ_CLIENT_115B 0x115B	/* QQ2008 */
 #define QQ_CLIENT_1203 0x1203	/* QQ2008 */
 #define QQ_CLIENT_1205 0x1205	/* QQ2008 */
@@ -94,6 +93,7 @@
 		return "QQ2005 beta1";
 	case QQ_CLIENT_0D51:
 		return "QQ2005 beta2";
+	case QQ_CLIENT_0D55:
 	case QQ_CLIENT_0D61:
 		return "QQ2005";
 	case QQ_CLIENT_0E1B:
--- a/libpurple/protocols/qq/qq_define.h	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Wed Oct 22 14:33:20 2008 +0000
@@ -1,5 +1,5 @@
 /**
- * @file header_info.h
+ * @file qq_define.h
  *
  * purple
  *
@@ -30,10 +30,11 @@
 #define QQ_UDP_HEADER_LENGTH    7
 #define QQ_TCP_HEADER_LENGTH    9
 
-#define QQ_PACKET_TAG           0x02	/* all QQ text packets starts with it */
-#define QQ_PACKET_TAIL          0x03	/* all QQ text packets end with it */
+#define QQ_PACKET_TAG			0x02	/* all QQ text packets starts with it */
+#define QQ_PACKET_TAIL			0x03	/* all QQ text packets end with it */
 
-#define QQ_CLIENT       0x0d55
+#define QQ_CLIENT_0D55 0x0d55	/* QQ2005 used by openq before */
+#define QQ_CLIENT_111D 0x111D	/* QQ2007 */
 
 const gchar *qq_get_ver_desc(gint source);
 
@@ -64,6 +65,13 @@
 	QQ_CMD_TOKEN  = 0x0062, 		/* get login token */
 	QQ_CMD_RECV_MSG_SYS = 0x0080,			/* receive a system message */
 	QQ_CMD_BUDDY_CHANGE_STATUS = 0x0081,	/* buddy change status */
+	/* for QQ2007*/
+	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_ADD_NO_AUTH_EX = 0x00A7,			/* add friend without auth */
+	QQ_CMD_BUDDY_ADD_AUTH_EX = 0x00A8, 				/* add buddy with auth */
 };
 
 const gchar *qq_get_cmd_desc(gint type);
--- a/libpurple/protocols/qq/qq_network.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Wed Oct 22 14:33:20 2008 +0000
@@ -30,7 +30,7 @@
 #include "group_info.h"
 #include "group_free.h"
 #include "qq_crypt.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "qq_base.h"
 #include "buddy_list.h"
 #include "packet_parse.h"
@@ -160,7 +160,7 @@
 		qd->connect_watcher = 0;
 	}
 
-	if (qd->fd >= 0 && qd->token != NULL && qd->token_len >= 0) {
+	if (qd->fd >= 0 && qd->ld.token != NULL && qd->ld.token_len > 0) {
 		purple_debug_info("QQ", "Connect ok\n");
 		return FALSE;
 	}
@@ -228,6 +228,19 @@
 	return FALSE;	/* timeout callback stops */
 }
 
+static void redirect_server(PurpleConnection *gc)
+{
+	qq_data *qd;
+	qd = (qq_data *) gc->proto_data;
+
+	if (qd->check_watcher > 0) {
+			purple_timeout_remove(qd->check_watcher);
+			qd->check_watcher = 0;
+	}
+	if (qd->connect_watcher > 0)	purple_timeout_remove(qd->connect_watcher);
+	qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc);
+}
+
 /* process the incoming packet from qq_pending */
 static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
 {
@@ -242,6 +255,7 @@
 	guint32 room_id;
 	gint update_class;
 	guint32 ship32;
+	int ret;
 
 	qq_transaction *trans;
 
@@ -292,20 +306,13 @@
 
 	switch (cmd) {
 		case QQ_CMD_TOKEN:
-			if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) {
-				qq_send_packet_login(gc);
-			}
-			break;
+		case QQ_CMD_GET_SERVER:
 		case QQ_CMD_LOGIN:
-			qq_proc_login_cmd(gc, buf + bytes, bytes_not_read);
-			/* check is redirect or not, and do it now */
-			if (qd->redirect_ip.s_addr != 0) {
-				if (qd->check_watcher > 0) {
-					purple_timeout_remove(qd->check_watcher);
-					qd->check_watcher = 0;
+			ret = qq_proc_login_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
+			if (ret != QQ_LOGIN_REPLY_OK) {
+				if (ret == QQ_LOGIN_REPLY_REDIRECT) {
+					redirect_server(gc);
 				}
-				if (qd->connect_watcher > 0)	purple_timeout_remove(qd->connect_watcher);
-				qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc);
 				return FALSE;	/* do nothing after this function and return now */
 			}
 			break;
@@ -316,10 +323,10 @@
 			purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n",
 					qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
 #endif
-			qq_proc_room_cmd(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
+			qq_proc_room_cmds(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
 			break;
 		default:
-			qq_proc_client_cmd(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
+			qq_proc_client_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
 			break;
 	}
 
@@ -645,7 +652,7 @@
 	qd->itv_count.keep_alive--;
 	if (qd->itv_count.keep_alive <= 0) {
 		qd->itv_count.keep_alive = qd->itv_config.keep_alive;
-		qq_send_packet_keep_alive(gc);
+		qq_request_keep_alive(gc);
 		return TRUE;
 	}
 
@@ -663,10 +670,9 @@
 	return TRUE;		/* if return FALSE, timeout callback stops */
 }
 
-static void do_request_token(PurpleConnection *gc)
+static void set_all_keys(PurpleConnection *gc)
 {
 	qq_data *qd;
-	gchar *conn_msg;
 	const gchar *passwd;
 
 	/* _qq_show_socket("Got login socket", source); */
@@ -682,24 +688,27 @@
 	qd->channel = 1;
 	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));
+#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->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->password_twice_md5, sizeof(qd->password_twice_md5),
+	qq_get_md5(qd->ld.pwd_twice_md5, sizeof(qd->ld.pwd_twice_md5),
 		(guint8 *)passwd, strlen(passwd));
-	qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
-		qd->password_twice_md5, sizeof(qd->password_twice_md5));
+	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));
 
-	g_return_if_fail(qd->network_watcher == 0);
-	qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc);
-
-	/* Update the login progress status display */
-	conn_msg = g_strdup_printf(_("Request token"));
-	purple_connection_update_progress(gc, conn_msg, 2, QQ_CONNECT_STEPS);
-	g_free(conn_msg);
-
-	qq_send_packet_token(gc);
 }
 
 /* the callback function after socket is built
@@ -744,7 +753,19 @@
 		conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, udp_pending, gc);
 	}
 
-	do_request_token( gc );
+	g_return_if_fail(qd->network_watcher == 0);
+	qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc);
+
+	set_all_keys( gc );
+
+	if (qd->is_above_2007) {
+		purple_connection_update_progress(gc, _("Get server ..."), 2, QQ_CONNECT_STEPS);
+		qq_request_get_server(gc);
+		return;
+	}
+
+	purple_connection_update_progress(gc, _("Request token"), 2, QQ_CONNECT_STEPS);
+	qq_request_token(gc);
 }
 
 #ifndef purple_proxy_connect_udp
@@ -888,7 +909,6 @@
 {
 	PurpleAccount *account ;
 	qq_data *qd;
-	gchar *conn_msg;
 
 	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
 	account = purple_connection_get_account(gc);
@@ -900,9 +920,7 @@
 		return FALSE;
 	}
 
-	conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), server, port);
-	purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS);
-	g_free(conn_msg);
+	purple_connection_update_progress(gc, _("Connecting server ..."), 1, QQ_CONNECT_STEPS);
 
 	purple_debug_info("QQ", "Connect to %s:%d\n", server, port);
 
@@ -963,7 +981,7 @@
 
 	/* finish  all I/O */
 	if (qd->fd >= 0 && qd->is_login) {
-		qq_send_packet_logout(gc);
+		qq_request_logout(gc);
 	}
 
 	/* not connected */
@@ -988,14 +1006,9 @@
 
 	qq_trans_remove_all(gc);
 
-	if (qd->token) {
-		purple_debug_info("QQ", "free token\n");
-		g_free(qd->token);
-		qd->token = NULL;
-		qd->token_len = 0;
-	}
-	memset(qd->inikey, 0, sizeof(qd->inikey));
-	memset(qd->password_twice_md5, 0, sizeof(qd->password_twice_md5));
+	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->session_key, 0, sizeof(qd->session_key));
 	memset(qd->session_md5, 0, sizeof(qd->session_md5));
 
@@ -1019,7 +1032,7 @@
 	}
 	/* now comes the normal QQ packet as UDP */
 	bytes += qq_put8(buf + bytes, QQ_PACKET_TAG);
-	bytes += qq_put16(buf + bytes, QQ_CLIENT);
+	bytes += qq_put16(buf + bytes, qd->client_version);
 	bytes += qq_put16(buf + bytes, cmd);
 
 	bytes += qq_put16(buf + bytes, seq);
--- a/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:33:20 2008 +0000
@@ -42,7 +42,7 @@
 #include "group_join.h"
 #include "group_opt.h"
 
-#include "header_info.h"
+#include "qq_define.h"
 #include "qq_base.h"
 #include "im.h"
 #include "qq_process.h"
@@ -176,8 +176,7 @@
 			_qq_process_msg_sys_notice(gc, from, to, msg_utf8);
 			break;
 		case QQ_SERVER_NEW_CLIENT:
-			purple_debug_warning("QQ",
-				   "QQ Server has newer client than %s\n", qq_get_ver_desc(QQ_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);
@@ -407,6 +406,10 @@
 
 void qq_update_online(PurpleConnection *gc, guint16 cmd)
 {
+	qq_data *qd;
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
 	switch (cmd) {
 		case 0:
 			qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ONLINE);
@@ -414,13 +417,14 @@
 		case QQ_CMD_GET_BUDDIES_ONLINE:
 			/* last command */
 			update_all_rooms_online(gc, 0, 0);
+			qd->online_last_update = time(NULL);
 			break;
 		default:
 			break;
 	}
 }
 
-void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
+void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq,
 		guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
 		gint update_class, guint32 ship32)
 {
@@ -567,55 +571,108 @@
 	}
 }
 
-void qq_proc_login_cmd(PurpleConnection *gc, guint8 *rcved, gint rcved_len)
+guint8 qq_proc_login_cmds(PurpleConnection *gc,  guint16 cmd, guint16 seq,
+		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32)
 {
 	qq_data *qd;
-	guint8 *data;
-	gint data_len;
-	guint ret_8;
+	guint8 *data = NULL;
+	gint data_len = 0;
+	guint ret_8 = QQ_LOGIN_REPLY_ERR;
 
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
 	qd = (qq_data *) gc->proto_data;
 
+	g_return_val_if_fail(rcved_len > 0, QQ_LOGIN_REPLY_ERR);
 	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->inikey);
-	if (data_len >= 0) {
+
+	switch (cmd) {
+		case QQ_CMD_TOKEN:
+			if (qq_process_token(gc, rcved, rcved_len) == QQ_LOGIN_REPLY_OK) {
+				if (qd->is_above_2007) {
+					qq_request_token_ex(gc);
+				} else {
+					qq_request_login(gc);
+				}
+				return QQ_LOGIN_REPLY_OK;
+			}
+			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);
+			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);
+			if (data_len >= 0) {
+				purple_debug_warning("QQ", "Decrypt login packet by init_key, %d bytes\n", data_len);
+			} else {
+				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
+				if (data_len >= 0) {
+					purple_debug_warning("QQ", "Decrypt login packet by password_twice_md5, %d bytes\n", data_len);
+				}
+			}
+			break;
+	}
+
+	if (data_len < 0) {
 		purple_debug_warning("QQ",
-				"Decrypt login reply packet with inikey, %d bytes\n", data_len);
-	} else {
-		data_len = qq_decrypt(data, rcved, rcved_len, qd->password_twice_md5);
-		if (data_len >= 0) {
-			purple_debug_warning("QQ",
-				"Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len);
-		} else {
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				"Can not decrypt login cmd, [%05d], 0x%04X %s, len %d\n",
+				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,
 				_("Can not decrypt login reply"));
-			return;
-		}
+		return QQ_LOGIN_REPLY_ERR_DECRYPT;
 	}
 
-	ret_8 = qq_process_login_reply(gc, data, data_len);
-	if (ret_8 != QQ_LOGIN_REPLY_OK) {
-		return;
-	}
-
-	purple_debug_info("QQ", "Login repliess OK; everything is fine\n");
+	switch (cmd) {
+		case QQ_CMD_GET_SERVER:
+			ret_8 = qq_process_get_server(gc, data, data_len);
+			if ( ret_8 == QQ_LOGIN_REPLY_OK) {
+				qq_request_token(gc);
+			} else if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
+				return QQ_LOGIN_REPLY_REDIRECT;
+			}
+			break;
+		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);
+			} 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) {
+				qq_captcha_input_dialog(gc, &(qd->captcha));
+				g_free(qd->captcha.token);
+				g_free(qd->captcha.data);
+				memset(&qd->captcha, 0, sizeof(qd->captcha));
+			}
+			break;
+		case QQ_CMD_LOGIN:
+			ret_8 = qq_process_login(gc, data, data_len);
+			if (ret_8 != QQ_LOGIN_REPLY_OK) {
+				return  ret_8;
+			}
 
-	purple_connection_set_state(gc, PURPLE_CONNECTED);
-	qd->is_login = TRUE;	/* must be defined after sev_finish_login */
+			purple_debug_info("QQ", "Login repliess OK; everything is fine\n");
+			purple_connection_set_state(gc, PURPLE_CONNECTED);
+			qd->is_login = TRUE;	/* must be defined after sev_finish_login */
 
-	/* now initiate QQ Qun, do it first as it may take longer to finish */
-	qq_group_init(gc);
+			/* now initiate QQ Qun, do it first as it may take longer to finish */
+			qq_group_init(gc);
 
-	/* is_login, but we have packets before login */
-	qq_trans_process_remained(gc);
+			/* is_login, but we have packets before login */
+			qq_trans_process_remained(gc);
 
-	qq_update_all(gc, 0);
-	return;
+			qq_update_all(gc, 0);
+			break;
+		default:
+			process_cmd_unknow(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq);
+			return QQ_LOGIN_REPLY_ERR;
+	}
+	return QQ_LOGIN_REPLY_OK;
 }
 
-void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
+void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
 		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32)
 {
 	qq_data *qd;
@@ -710,7 +767,7 @@
 			purple_debug_info("QQ", "All buddies and groups received\n");
 			break;
 		default:
-			process_cmd_unknow(gc, _("Unknow reply CMD"), data, data_len, cmd, seq);
+			process_cmd_unknow(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq);
 			is_unknow = TRUE;
 			break;
 	}
--- a/libpurple/protocols/qq/qq_process.h	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.h	Wed Oct 22 14:33:20 2008 +0000
@@ -38,10 +38,11 @@
 	QQ_CMD_CLASS_UPDATE_ROOM,
 };
 
-void qq_proc_login_cmd(PurpleConnection *gc, guint8 *rcved, gint rcved_len);
-void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
+guint8 qq_proc_login_cmds(PurpleConnection *gc,  guint16 cmd, guint16 seq,
 		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32);
-void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
+void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
+		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32);
+void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq,
 		guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
 		gint update_class, guint32 ship32);
 
--- a/libpurple/protocols/qq/qq_trans.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/qq_trans.c	Wed Oct 22 14:33:20 2008 +0000
@@ -30,7 +30,7 @@
 #include "prefs.h"
 #include "request.h"
 
-#include "header_info.h"
+#include "qq_define.h"
 #include "qq_network.h"
 #include "qq_process.h"
 #include "qq_trans.h"
--- a/libpurple/protocols/qq/send_file.c	Wed Oct 22 14:28:17 2008 +0000
+++ b/libpurple/protocols/qq/send_file.c	Wed Oct 22 14:33:20 2008 +0000
@@ -31,7 +31,7 @@
 
 #include "buddy_list.h"
 #include "file_trans.h"
-#include "header_info.h"
+#include "qq_define.h"
 #include "im.h"
 #include "qq_base.h"
 #include "packet_parse.h"
@@ -54,8 +54,8 @@
 static int _qq_in_same_lan(ft_info *info)
 {
 	if (info->remote_internet_ip == info->local_internet_ip) return 1;
-	purple_debug_info("QQ", 
-			"Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n",  
+	purple_debug_info("QQ",
+			"Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n",
 			info->remote_internet_ip
 			, info->local_internet_ip);
 	return 0;
@@ -87,7 +87,7 @@
 	info = (ft_info *) xfer->data;
 	sinlen = sizeof(sin);
 	r = recvfrom(info->recv_fd, buf, len, 0, (struct sockaddr *) &sin, &sinlen);
-	purple_debug_info("QQ", 
+	purple_debug_info("QQ",
 			"==> recv %d bytes from File UDP Channel, remote ip[%s], remote port[%d]\n",
 			r, inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
 	return r;
@@ -301,7 +301,7 @@
 	/* 004-007: sender uid */
 	bytes += qq_put32 (raw_data + bytes, to_uid);
 	/* 008-009: sender client version */
-	bytes += qq_put16 (raw_data + bytes, QQ_CLIENT);
+	bytes += qq_put16 (raw_data + bytes, qd->client_version);
 	/* 010-013: receiver uid */
 	bytes += qq_put32 (raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
@@ -380,7 +380,7 @@
 
 static void _qq_xfer_init_socket(PurpleXfer *xfer)
 {
-	gint sockfd, listen_port = 0, i; 
+	gint sockfd, listen_port = 0, i;
 	socklen_t sin_len;
 	struct sockaddr_in sin;
 	ft_info *info;
@@ -389,7 +389,7 @@
 	g_return_if_fail(xfer->data != NULL);
 	info = (ft_info *) xfer->data;
 
-	/* debug 
+	/* debug
 	info->local_real_ip = 0x7f000001;
 	*/
 	info->local_real_ip = g_ntohl(inet_addr(purple_network_get_my_ip(-1)));
@@ -460,7 +460,7 @@
 	raw_data = g_newa(guint8, packet_len);
 	bytes = 0;
 
-	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, 
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid,
 			QQ_FILE_TRANS_REQ, qd, FALSE);
 	bytes += qq_fill_conn_info(raw_data + bytes, info);
 	/* 079: 0x20 */
@@ -682,7 +682,7 @@
 }
 
 /* process reject im for file transfer request */
-void qq_process_recv_file_reject (guint8 *data, gint data_len, 
+void qq_process_recv_file_reject (guint8 *data, gint data_len,
 		guint32 sender_uid, PurpleConnection *gc)
 {
 	gchar *msg, *filename;
@@ -711,7 +711,7 @@
 }
 
 /* process cancel im for file transfer request */
-void qq_process_recv_file_cancel (guint8 *data, gint data_len, 
+void qq_process_recv_file_cancel (guint8 *data, gint data_len,
 		guint32 sender_uid, PurpleConnection *gc)
 {
 	gchar *msg, *filename;
@@ -785,7 +785,7 @@
 	info->local_internet_port = qd->my_port;
 	info->local_real_ip = 0x00000000;
 	info->to_uid = sender_uid;
-	
+
 	if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
 		purple_debug_warning("QQ", "Received file request message is empty\n");
 		return;
@@ -822,18 +822,18 @@
 				q_bud->status = QQ_BUDDY_ONLINE_INVISIBLE;
 				qq_update_buddy_contact(gc, q_bud);
 			}
-			else 
+			else
 				purple_debug_info("QQ", "buddy %d is already online\n", sender_uid);
 
 		}
-		else 
+		else
 			purple_debug_warning("QQ", "buddy %d is not in list\n", sender_uid);
 
-		g_free(sender_name);	    
+		g_free(sender_name);
 		g_strfreev(fileinfo);
 		return;
 	}
-	
+
 	xfer = purple_xfer_new(purple_connection_get_account(gc),
 			PURPLE_XFER_RECEIVE,
 			sender_name);
@@ -875,7 +875,7 @@
 	*/
 }
 
-void qq_process_recv_file_notify(guint8 *data, gint data_len, 
+void qq_process_recv_file_notify(guint8 *data, gint data_len,
 		guint32 sender_uid, PurpleConnection *gc)
 {
 	gint bytes;
@@ -892,7 +892,7 @@
 		purple_debug_warning("QQ", "Received file notify message is empty\n");
 		return;
 	}
-	
+
 	bytes = 0;
 	bytes += qq_get16(&(info->send_seq), data + bytes);