changeset 24138:dc112387190f

2008.09.29 - ccpaging <ccpaging(at)gmail.com> * 'Check Password' function for protocol 2007/2008
author SHiNE CsyFeK <csyfek@gmail.com>
date Wed, 22 Oct 2008 14:41:13 +0000
parents 87e61a85f5dd
children 832178d951ca
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/buddy_opt.c libpurple/protocols/qq/group_join.c libpurple/protocols/qq/im.c libpurple/protocols/qq/qq.h libpurple/protocols/qq/qq_base.c libpurple/protocols/qq/qq_network.c libpurple/protocols/qq/qq_process.c
diffstat 8 files changed, 145 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:40:04 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:41:13 2008 +0000
@@ -1,3 +1,6 @@
+2008.09.29 - ccpaging <ccpaging(at)gmail.com>
+	* 'Check Password' function for protocol 2007/2008
+
 2008.09.28 - ccpaging <ccpaging(at)gmail.com>
 	* The source is only for debug, not for user:
 		1. Implement new QQ protocol 2007/2008, include login and change status
--- a/libpurple/protocols/qq/buddy_opt.c	Wed Oct 22 14:40:04 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Wed Oct 22 14:41:13 2008 +0000
@@ -512,7 +512,7 @@
 	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	if (is_known || uid == qd->uid) {
+	if (is_known) {
 		group_name = g_strdup_printf(PURPLE_GROUP_QQ_FORMAT,
 				purple_account_get_username(gc->account));
 	 } else {
@@ -529,7 +529,7 @@
 		purple_blist_remove_buddy(buddy);
 
 	buddy = purple_buddy_new(gc->account, buddy_name, NULL);
-	if ( !is_known && uid != qd->uid) {
+	if ( !is_known) {
 		if (purple_privacy_check(gc->account, buddy_name)) {
 			purple_privacy_deny(gc->account, buddy_name, TRUE, FALSE);
 		} else {
--- a/libpurple/protocols/qq/group_join.c	Wed Oct 22 14:40:04 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Wed Oct 22 14:41:13 2008 +0000
@@ -39,6 +39,7 @@
 #include "group_opt.h"
 #include "group_conv.h"
 #include "group_search.h"
+#include "group_im.h"
 #include "qq_define.h"
 #include "packet_parse.h"
 #include "qq_network.h"
@@ -226,6 +227,9 @@
 	gint bytes;
 	guint32 id;
 	qq_data *qd;
+	qq_group *group;
+	gchar *msg;
+	time_t now = time(NULL);
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
@@ -239,7 +243,14 @@
 	bytes += qq_get32(&id, data + bytes);
 	g_return_if_fail(id > 0);
 
-	qq_got_attention(gc, _("Successed join to Qun"));
+	group = qq_room_search_id(gc, id);
+	if (group != NULL) {
+		msg = g_strdup_printf(_("Successed join to Qun %s (%d)"), group->title_utf8, group->ext_id);
+		qq_room_got_chat_in(gc, group, 0, msg, now);
+		g_free(msg);
+	} else {
+		qq_got_attention(gc, _("Successed join to Qun"));
+	}
 }
 
 /* process group cmd reply "join group" */
--- a/libpurple/protocols/qq/im.c	Wed Oct 22 14:40:04 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Wed Oct 22 14:41:13 2008 +0000
@@ -298,24 +298,21 @@
 {
 	qq_data *qd;
 	gchar *from;
-	PurpleBuddy *b;
-	qq_buddy *qq_b;
+	PurpleBuddy *buddy;
 	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);
+	from = uid_to_purple_name(qd->uid);
+	buddy = purple_find_buddy(gc->account, from);
+	if (buddy == NULL) {
+		qq_create_buddy(gc, qd->uid, TRUE, TRUE);
 	}
-	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);
+	g_free(from);
 }
 
 /* process received normal text IM */
--- a/libpurple/protocols/qq/qq.h	Wed Oct 22 14:40:04 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Wed Oct 22 14:41:13 2008 +0000
@@ -60,8 +60,8 @@
 	guint8 *token_ex;			/* get from server */
 	guint16 token_ex_len;
 
-	guint8 pwd_2nd_md5[QQ_KEY_LENGTH];			/* password in md5 (or md5' md5) */
-	guint8 pwd_4th_md5[QQ_KEY_LENGTH];
+	guint8 pwd_md5[QQ_KEY_LENGTH];			/* password in md5 (or md5' md5) */
+	guint8 pwd_twice_md5[QQ_KEY_LENGTH];
 
 	guint8 *login_token;
 	guint16 login_token_len;
@@ -73,7 +73,7 @@
 	guint8 b1;
 	guint32 w1;
 	guint32 w2;
-	guint32 ip;
+	struct in_addr ip;
 };
 
 struct _qq_add_request {
--- a/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:40:04 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:41:13 2008 +0000
@@ -34,16 +34,13 @@
 #include "qq_crypt.h"
 #include "group.h"
 #include "qq_define.h"
+#include "qq_network.h"
 #include "qq_base.h"
 #include "packet_parse.h"
 #include "qq.h"
 #include "qq_network.h"
 #include "utils.h"
 
-#define QQ_LOGIN_DATA_LENGTH		    416
-#define QQ_LOGIN_REPLY_OK_PACKET_LEN        139
-#define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN  11
-
 /* generate a md5 key using uid and session_key */
 static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key)
 {
@@ -71,7 +68,13 @@
 	qd = (qq_data *) gc->proto_data;
 	qq_show_packet("Login reply", data, len);
 
-	/* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
+	if (len < 139) {
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+				_("Can not decrypt get server reply"));
+		return QQ_LOGIN_REPLY_ERR;
+	}
+
 	bytes = 0;
 	bytes += qq_get8(&ret, data + bytes);
 	bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
@@ -134,11 +137,9 @@
 			tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
 	/* unknow 9 bytes, 0x(00 0a 00 0a 01 00 00 0e 10) */
 
-	if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) {	/* fail parsing login info */
-		purple_debug_warning("QQ",
-			   "Fail parsing login info, expect %d bytes, read %d bytes\n",
-			   QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
-	}			/* but we still go on as login OK */
+	if (len > 139) {
+		purple_debug_warning("QQ", "Login reply more than expected %d bytes, read %d bytes\n", 139, bytes);
+	}
 	return QQ_LOGIN_REPLY_OK;
 }
 
@@ -155,6 +156,13 @@
 	} packet;
 
 
+	if (len < 11) {
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+				_("Can not decrypt get server reply"));
+		return QQ_LOGIN_REPLY_ERR;
+	}
+
 	qd = (qq_data *) gc->proto_data;
 	bytes = 0;
 	/* 000-000: reply code */
@@ -166,11 +174,8 @@
 	/* 009-010: redirected new server port */
 	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;
+	if (len > 11) {
+		purple_debug_error("QQ", "Login redirect more than expected %d bytes, read %d bytes\n", 11, bytes);
 	}
 
 	/* redirect to new server, do not disconnect or connect here
@@ -239,15 +244,15 @@
 
 	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);
+	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
 
-	encrypted = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
+	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 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(encrypted, (guint8 *) "", 0, qd->ld.pwd_2nd_md5);
+	encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
 	g_return_if_fail(encrypted_len == 16);
 	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
 
@@ -270,8 +275,10 @@
 	/* 100 bytes unknown */
 	bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
 	/* all zero left */
+	memset(raw_data + bytes, 0, 416 - bytes);
+	bytes = 416;
 
-	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
@@ -350,7 +357,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 	for (i = 0; i < 4; i++)
-		qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->ld.pwd_2nd_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 */
 }
@@ -502,15 +509,19 @@
 	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);
+	raw_data = g_newa(guint8, sizeof(qd->redirect_data));
+	memset(raw_data, 0, sizeof(qd->redirect_data));
 
-	encrypted = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
+	encrypted = g_newa(guint8, sizeof(qd->redirect_data) + 16);	/* 16 bytes more */
 
 	bytes = 0;
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)&qd->redirect_data, sizeof(qd->redirect_data));
+	bytes += qq_put16(raw_data + bytes, qd->redirect_data.ret);
+	bytes += qq_put8(raw_data + bytes, qd->redirect_data.b1);
+	bytes += qq_put32(raw_data + bytes, qd->redirect_data.w1);
+	bytes += qq_put32(raw_data + bytes, qd->redirect_data.w2);
+	bytes += qq_putIP(raw_data + bytes, &(qd->redirect_data.ip));
 
-	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
@@ -519,34 +530,44 @@
 	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);
+	qq_send_cmd_encrypted(gc, QQ_CMD_GET_SERVER, qd->send_seq, buf, bytes, TRUE);
 }
 
 guint16 qq_process_get_server(PurpleConnection *gc, guint8 *data, gint data_len)
 {
 	qq_data *qd;
-	qq_redirect_data *redirect;
+	gint bytes;
 
 	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 (data != NULL, 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) {
+	
+	/* qq_show_packet("Get Server", data, data_len); */
+	bytes = 0;
+	bytes += qq_get16(&qd->redirect_data.ret, data + bytes);
+	if (qd->redirect_data.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));
+	if (data_len < 15) {
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+				_("Can not decrypt get server reply"));
+		return QQ_LOGIN_REPLY_ERR;
+	}
+
+	bytes += qq_get8(&qd->redirect_data.b1, data + bytes);
+	bytes += qq_get32(&qd->redirect_data.w1, data + bytes);
+	bytes += qq_get32(&qd->redirect_data.w2, data + bytes);
+	bytes += qq_getIP(&qd->redirect_data.ip, data + bytes);
+	purple_debug_info("QQ", "Get server %d-%d-%d%d, %s\n", 
+			qd->redirect_data.ret, qd->redirect_data.b1, qd->redirect_data.w1, qd->redirect_data.w2, 
+			inet_ntoa(qd->redirect_data.ip));
+
+	g_memmove(&qd->redirect_ip, &qd->redirect_data.ip, sizeof(qd->redirect_ip));
 	return QQ_LOGIN_REPLY_REDIRECT;
 }
 
@@ -577,7 +598,7 @@
 	bytes += qq_put8(raw_data + bytes, 0); 		/* fragment index */
 	bytes += qq_put16(raw_data + bytes, 0); 	/* captcha token */
 
-	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
@@ -617,7 +638,7 @@
 	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, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
@@ -662,7 +683,7 @@
 	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, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
@@ -783,8 +804,8 @@
 	ret = data[0];
 
 	bytes = 0;
-	bytes += qq_get8(&sub_cmd, data + bytes);
-	bytes += 2;
+	bytes += qq_get8(&sub_cmd, data + bytes); /* 03: ok; 04: need verifying */
+	bytes += 2;	/* 0x(00 05) */
 	bytes += qq_get8(&reply, data + bytes);
 
 	bytes += qq_get16(&(qd->ld.token_ex_len), data + bytes);
@@ -794,7 +815,7 @@
 
 	if(reply != 1)
 	{
-		purple_debug_info("QQ", "Captcha verified\n");
+		purple_debug_info("QQ", "Captcha verified, result %d\n", reply);
 		return QQ_LOGIN_REPLY_OK;
 	}
 
@@ -863,8 +884,9 @@
 	gint bytes;
 	guint8 *encrypted;
 	gint encrypted_len;
+	gint count;
 	static guint8 header[] = {
-			0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0x0E
+			0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0xE0
 	};
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -879,39 +901,35 @@
 
 	/* 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);
+	bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
+	bytes += qq_put16(raw_data + bytes, rand() & 0);
+	bytes += qq_put16(raw_data + bytes, rand() & 0xffff);
 
-	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_4th_md5);
-
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
 	/* create packet */
 	bytes = 0;
 	bytes += qq_putdata(raw_data + bytes, header, sizeof(header));
 	/* token get from qq_process_token_ex */
-	bytes += qq_put16(raw_data + bytes, qd->ld.token_ex_len);
+	bytes += qq_put8(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);
+	/* random data, 20 bytes */
+	bytes += qq_put16(raw_data + bytes, 0x0014);	/* length of next segment*/
+	count = 0x14;
+	while (count--) bytes += qq_put8(raw_data + bytes, rand() & 0xff);
 	/* 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);	/* length of next segment */
+	bytes += qq_put8(raw_data + bytes, 0x03);	/* length of next segment */
 	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]);
-
+	bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[1]);
+	bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[2]);
+	qq_show_packet("QQ", raw_data, bytes);
 	/* Encrypted by random key*/
-	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
@@ -920,7 +938,7 @@
 	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);
+	qq_send_cmd_encrypted(gc, QQ_CMD_CHECK_PWD, qd->send_seq, buf, bytes, TRUE);
 }
 
 guint8 qq_process_check_pwd_2007( PurpleConnection *gc, guint8 *data, gint data_len)
@@ -960,7 +978,7 @@
 
 	switch (ret)
 	{
-		case 0x34:		/* invalid password */
+		case 0x34:		/* invalid password, 2nd byte is 0xc6 */
 			if (!purple_account_get_remember_password(gc->account)) {
 				purple_account_set_password(gc->account, NULL);
 			}
@@ -987,13 +1005,13 @@
 	}
 	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
 			">>> [default] decrypt and dump");
-	bytes = 1;
+	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_debug_error("QQ", "%s:\n%s\n", error, msg_utf8);
 	purple_connection_error_reason(gc, reason, msg_utf8);
 
 	g_free(error);
@@ -1100,39 +1118,37 @@
 
 	/* 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_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
 	bytes += qq_put16(raw_data + bytes, 0);
-	bytes += qq_put16(raw_data + bytes, (guint16) (rand() & 0xffff));
+	bytes += qq_put16(raw_data + bytes, rand() & 0xffff);
 
-	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_4th_md5);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_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_put8(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_put16(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_put16(raw_data + bytes, 0x0003);
 	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]);
+	bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[1]);
+	bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[2]);
 
+	qq_show_packet("Check password", raw_data, bytes);
 	/* Encrypted by random key*/
-	encrypted_len = qq_encrypt(encrypted, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.random_key);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
@@ -1141,7 +1157,7 @@
 	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);
+	qq_send_cmd_encrypted(gc, QQ_CMD_CHECK_PWD, qd->send_seq, buf, bytes, TRUE);
 }
 
 void qq_request_login_2007(PurpleConnection *gc)
@@ -1184,11 +1200,11 @@
 
 	/* 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_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
 	bytes += qq_put16(raw_data + bytes, 0);
 	bytes += qq_put16(raw_data + bytes, 0xffff);
 
-	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_4th_md5);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
 
 	/* create packet */
 	bytes = 0;
@@ -1196,8 +1212,8 @@
 	/* password encrypted */
 	bytes += qq_put16(raw_data + bytes, encrypted_len);
 	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
-	/* put data which NULL string encrypted by key pwd_4th_md5 */
-	encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_4th_md5);
+	/* put data which NULL string encrypted by key pwd_twice_md5 */
+	encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
 	g_return_if_fail(encrypted_len == 16);
 	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
 	/* unknow fill */
@@ -1337,11 +1353,11 @@
 
 	/* 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_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
 	bytes += qq_put16(raw_data + bytes, 0);
 	bytes += qq_put16(raw_data + bytes, 0xffff);
 
-	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_4th_md5);
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
 
 	/* create packet */
 	bytes = 0;
@@ -1350,8 +1366,8 @@
 	/* password encrypted */
 	bytes += qq_put16(raw_data + bytes, encrypted_len);
 	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
-	/* put data which NULL string encrypted by key pwd_4th_md5 */
-	encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_4th_md5);
+	/* put data which NULL string encrypted by key pwd_twice_md5 */
+	encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
 	g_return_if_fail(encrypted_len == 16);
 	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
 	/* unknow 19 bytes zero filled*/
--- a/libpurple/protocols/qq/qq_network.c	Wed Oct 22 14:40:04 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Wed Oct 22 14:41:13 2008 +0000
@@ -309,6 +309,8 @@
 	switch (cmd) {
 		case QQ_CMD_TOKEN:
 		case QQ_CMD_GET_SERVER:
+		case QQ_CMD_TOKEN_EX:
+		case QQ_CMD_CHECK_PWD:
 		case QQ_CMD_LOGIN:
 			ret = qq_proc_login_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
 			if (ret != QQ_LOGIN_REPLY_OK) {
@@ -715,13 +717,11 @@
 	passwd = purple_account_get_password(purple_connection_get_account(gc));
 
 	/* use twice-md5 of user password as session key since QQ 2003iii */
-	dest = qd->ld.pwd_2nd_md5;
+	dest = qd->ld.pwd_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);
+	dest = qd->ld.pwd_twice_md5;
+	qq_get_md5(dest, dest_len, qd->ld.pwd_md5, dest_len);
 }
 
 /* the callback function after socket is built
@@ -1021,8 +1021,8 @@
 	qq_trans_remove_all(gc);
 
 	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.pwd_md5, 0, sizeof(qd->ld.pwd_md5));
+	memset(qd->ld.pwd_twice_md5, 0, sizeof(qd->ld.pwd_twice_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));
--- a/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:40:04 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:41:13 2008 +0000
@@ -607,18 +607,18 @@
 			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);
+				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 pwd_4th_md5, %d bytes\n", data_len);
+					purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5, %d bytes\n", data_len);
 				}
 			}
 			break;
 		case QQ_CMD_LOGIN:
 		default:
 			if (qd->client_version > 2005) {
-				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_4th_md5);
+				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 pwd_4th_md5\n");
+					purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n");
 				} else {
 					data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.login_key);
 					if (data_len >= 0) {
@@ -631,9 +631,9 @@
 				if (data_len >= 0) {
 					purple_debug_warning("QQ", "Decrypt login packet by random_key\n");
 				} else {
-					data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_2nd_md5);
+					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 pwd_2nd_md5\n");
+						purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n");
 					}
 				}
 			}