changeset 24070:832178d951ca

2008.09.30 - ccpaging <ccpaging(at)gmail.com> * Successfully login using 2007/2008 protocols
author SHiNE CsyFeK <csyfek@gmail.com>
date Wed, 22 Oct 2008 14:42:23 +0000
parents dc112387190f
children 619ac2303c46
files libpurple/protocols/qq/ChangeLog 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_process.c
diffstat 6 files changed, 132 insertions(+), 275 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:41:13 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:42:23 2008 +0000
@@ -1,3 +1,6 @@
+2008.09.30 - ccpaging <ccpaging(at)gmail.com>
+	* Successfully login using 2007/2008 protocols
+
 2008.09.29 - ccpaging <ccpaging(at)gmail.com>
 	* 'Check Password' function for protocol 2007/2008
 
--- a/libpurple/protocols/qq/qq.c	Wed Oct 22 14:41:13 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Wed Oct 22 14:42:23 2008 +0000
@@ -219,6 +219,7 @@
 
 	qq_disconnect(gc);
 
+	if (qd->redirect) g_free(qd->redirect);
 	if (qd->ld.token) g_free(qd->ld.token);
 	if (qd->ld.token_ex) g_free(qd->ld.token_ex);
 	if (qd->captcha.token) g_free(qd->captcha.token);
--- a/libpurple/protocols/qq/qq.h	Wed Oct 22 14:41:13 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Wed Oct 22 14:42:23 2008 +0000
@@ -41,7 +41,6 @@
 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;
 
@@ -68,14 +67,6 @@
 	guint8 login_key[QQ_KEY_LENGTH];
 };
 
-struct _qq_redirect_data {
-	guint16 ret;
-	guint8 b1;
-	guint32 w1;
-	guint32 w2;
-	struct in_addr ip;
-};
-
 struct _qq_add_request {
 	guint32 uid;
 	PurpleConnection *gc;
@@ -150,7 +141,8 @@
 
 	struct in_addr redirect_ip;
 	guint16 redirect_port;
-	qq_redirect_data redirect_data;
+	guint8 *redirect;
+	guint8 redirect_len;
 
 	guint check_watcher;
 	guint connect_watcher;
--- a/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:41:13 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:42:23 2008 +0000
@@ -509,17 +509,19 @@
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	raw_data = g_newa(guint8, sizeof(qd->redirect_data));
-	memset(raw_data, 0, sizeof(qd->redirect_data));
+	raw_data = g_newa(guint8, 128);
+	memset(raw_data, 0, 128);
 
-	encrypted = g_newa(guint8, sizeof(qd->redirect_data) + 16);	/* 16 bytes more */
+	encrypted = g_newa(guint8, 128 + 16);	/* 16 bytes more */
 
 	bytes = 0;
-	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));
+	if (qd->redirect == NULL) {
+		/* first packet to get server */
+		qd->redirect_len = 15;
+		qd->redirect = g_realloc(qd->redirect, qd->redirect_len);
+		memset(qd->redirect, 0, qd->redirect_len);
+	}
+	bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
 
 	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
@@ -537,6 +539,7 @@
 {
 	qq_data *qd;
 	gint bytes;
+	guint16 ret;
 
 	g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
 	qd = (qq_data *) gc->proto_data;
@@ -545,9 +548,9 @@
 	
 	/* 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));
+	bytes += qq_get16(&ret, data + bytes);
+	if (ret == 0) {
+		/* Notice: do not clear redirect_data here. It will be used in login*/
 		qd->redirect_ip.s_addr = 0;
 		return QQ_LOGIN_REPLY_OK;
 	}
@@ -559,15 +562,13 @@
 		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));
+	qd->redirect_len = data_len;
+	qd->redirect = g_realloc(qd->redirect, qd->redirect_len);
+	qq_getdata(qd->redirect, qd->redirect_len, data);
+	qq_show_packet("Redirect to", qd->redirect, qd->redirect_len);
 
-	g_memmove(&qd->redirect_ip, &qd->redirect_data.ip, sizeof(qd->redirect_ip));
+	qq_getIP(&qd->redirect_ip, data + 11);
+	purple_debug_info("QQ", "Get server %s\n", inet_ntoa(qd->redirect_ip));
 	return QQ_LOGIN_REPLY_REDIRECT;
 }
 
@@ -809,10 +810,8 @@
 	bytes += qq_get8(&reply, data + bytes);
 
 	bytes += qq_get16(&(qd->ld.token_ex_len), data + bytes);
-	if (qd->ld.token_ex != NULL)	g_free(qd->ld.token_ex);
-	qd->ld.token_ex = g_new0(guint8, qd->ld.token_ex_len);
+	qd->ld.token_ex = g_realloc(qd->ld.token_ex, qd->ld.token_ex_len);
 	bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len , data + bytes);
-
 	if(reply != 1)
 	{
 		purple_debug_info("QQ", "Captcha verified, result %d\n", reply);
@@ -877,221 +876,7 @@
 	return crc ^ 0xffffffffL;
 }
 
-void qq_request_check_pwd_2007(PurpleConnection *gc)
-{
-	qq_data *qd;
-	guint8 *buf, *raw_data;
-	gint bytes;
-	guint8 *encrypted;
-	gint encrypted_len;
-	gint count;
-	static guint8 header[] = {
-			0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0xE0
-	};
-
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
-
-	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
-	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
-
-	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
-
-	/* Encrypted password and put in encrypted */
-	bytes = 0;
-	bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_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_twice_md5);
-	/* create packet */
-	bytes = 0;
-	bytes += qq_putdata(raw_data + bytes, header, sizeof(header));
-	/* token get from qq_process_token_ex */
-	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);
-	/* 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);	/* 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_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, bytes, qd->ld.random_key);
-
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
-	memset(buf, 0, MAX_PACKET_SIZE);
-	bytes = 0;
-	bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
-	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
-
-	qd->send_seq++;
-	qq_send_cmd_encrypted(gc, QQ_CMD_CHECK_PWD, qd->send_seq, buf, bytes, TRUE);
-}
-
-guint8 qq_process_check_pwd_2007( PurpleConnection *gc, guint8 *data, gint data_len)
-{
-	qq_data *qd;
-	int bytes;
-	guint8 ret;
-	guint16 unknown_len;
-	gchar *error = NULL;
-	gchar *msg, *msg_utf8;
-	guint16 msg_len;
-	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-
-	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
-
-	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
-	qd = (qq_data *) gc->proto_data;
-
-	bytes = 2;	// skip 2 bytes
-	bytes += qq_get8(&ret, data + bytes);
-	bytes += 4;  // skip 4 bytes
-	/* 2 unknow */
-	bytes += qq_get16(&unknown_len, data + bytes);
-	bytes += unknown_len;
-	bytes += qq_get16(&unknown_len, data + bytes);
-	bytes += unknown_len;
-	if (ret == QQ_LOGIN_REPLY_OK) {
-		/* get login_token */
-		bytes += qq_get16(&qd->ld.login_token_len, data + bytes);
-		if (qd->ld.login_token != NULL) g_free(qd->ld.login_token);
-		qd->ld.login_token = g_new0(guint8, qd->ld.login_token_len);
-		bytes += qq_getdata(qd->ld.login_token, qd->ld.login_token_len, data + bytes);
-		/* get login_key */
-		bytes += qq_getdata(qd->ld.login_key, sizeof(qd->ld.login_key), data + bytes);
-		return QQ_LOGIN_REPLY_OK;
-	}
-
-	switch (ret)
-	{
-		case 0x34:		/* invalid password, 2nd byte is 0xc6 */
-			if (!purple_account_get_remember_password(gc->account)) {
-				purple_account_set_password(gc->account, NULL);
-			}
-			error = g_strdup(_("Error password"));
-			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-			break;
-		case 0x33:		/* need activation */
-		case 0x51:		/* need activation */
-			error = g_strdup(_("Need active"));
-			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-			break;
-		case 0xBF:		/* uid is not exist */
-			error = g_strdup(_("invalid user name"));
-			reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
-			break;
-		default:
-			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
-					">>> [default] decrypt and dump");
-			error = g_strdup_printf(
-						_("Unknow reply code when checking password (0x%02X)"),
-						ret );
-			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
-			break;
-	}
-	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
-			">>> [default] decrypt and dump");
-	bytes = 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:\n%s\n", error, msg_utf8);
-	purple_connection_error_reason(gc, reason, msg_utf8);
-
-	g_free(error);
-	g_free(msg);
-	g_free(msg_utf8);
-	return QQ_LOGIN_REPLY_ERR;
-}
-
-guint8 qq_process_check_pwd_2008( PurpleConnection *gc, guint8 *data, gint data_len)
-{
-	qq_data *qd;
-	int bytes;
-	guint8 ret;
-	gchar *error = NULL;
-	gchar *msg, *msg_utf8;
-	guint16 msg_len;
-	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-
-	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
-
-	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
-	qd = (qq_data *) gc->proto_data;
-
-	bytes = 1;	// skip 1 bytes, always 0
-	bytes += qq_get8(&ret, data + bytes);
-	if (ret == 0x97) {
-		/* get login_token */
-		bytes += qq_get16(&qd->ld.login_token_len, data);
-		if (qd->ld.login_token != NULL) g_free(qd->ld.login_token);
-		qd->ld.login_token = g_new0(guint8, qd->ld.login_token_len);
-		bytes += qq_getdata(qd->ld.login_token, qd->ld.login_token_len, data + bytes);
-		/* get login_key */
-		bytes += qq_getdata(qd->ld.login_key, sizeof(qd->ld.login_key), data + bytes);
-		return QQ_LOGIN_REPLY_OK;
-	}
-
-	switch (ret)
-	{
-		case 0xc6:		/* invalid password */
-			if (!purple_account_get_remember_password(gc->account)) {
-				purple_account_set_password(gc->account, NULL);
-			}
-			error = g_strdup(_("Error password"));
-			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-			break;
-		case 0x33:		/* need activation */
-		case 0x51:		/* need activation */
-			error = g_strdup(_("Need active"));
-			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-			break;
-		case 0xBF:		/* uid is not exist */
-			error = g_strdup(_("invalid user name"));
-			reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
-			break;
-		default:
-			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
-					">>> [default] decrypt and dump");
-			error = g_strdup_printf(
-						_("Unknow reply code when checking password (0x%02X)"),
-						ret );
-			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
-			break;
-	}
-
-	bytes = 11;
-	bytes += qq_get16(&msg_len, data + bytes);
-
-	msg = g_strndup((gchar *)data + bytes, msg_len);
-	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-
-	purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
-	purple_connection_error_reason(gc, reason, msg_utf8);
-
-	g_free(error);
-	g_free(msg);
-	g_free(msg_utf8);
-	return QQ_LOGIN_REPLY_ERR;
-}
-
-void qq_request_check_pwd_2008(PurpleConnection *gc)
+void qq_request_check_pwd(PurpleConnection *gc)
 {
 	qq_data *qd;
 	guint8 *buf, *raw_data;
@@ -1138,15 +923,17 @@
 	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);
+	qq_put8(raw_data + 1, bytes - 2);
+	
 	/* tail */
 	bytes += qq_put16(raw_data + bytes, 0x0003);
 	bytes += qq_put8(raw_data + bytes, 0);
 	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);
+	/* qq_show_packet("Check password", raw_data, bytes); */
 	/* Encrypted by random key*/
 	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
@@ -1160,6 +947,89 @@
 	qq_send_cmd_encrypted(gc, QQ_CMD_CHECK_PWD, qd->send_seq, buf, bytes, TRUE);
 }
 
+guint8 qq_process_check_pwd( PurpleConnection *gc, guint8 *data, gint data_len)
+{
+	qq_data *qd;
+	int bytes;
+	guint8 ret;
+	gchar *error = NULL;
+	guint16 unknow_token_len;
+	gchar *msg, *msg_utf8;
+	guint16 msg_len;
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+
+	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
+
+	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
+	qd = (qq_data *) gc->proto_data;
+
+	/* qq_show_packet("Check password reply", data, data_len); */
+
+	bytes = 0;
+	bytes += qq_get16(&unknow_token_len, data + bytes);	/* maybe total length */
+	bytes += qq_get8(&ret, data + bytes);
+	bytes += 4; /* 0x(00 00 6d b9) */
+	/* unknow_token_len may 0 when not reply ok*/
+	bytes += qq_get16(&unknow_token_len, data + bytes);	/* 0x0020 */
+	bytes += unknow_token_len;
+	bytes += qq_get16(&unknow_token_len, data + bytes);	/* 0x0020 */
+	bytes += unknow_token_len;
+
+	if (ret == 0) {
+		/* get login_token */
+		bytes += qq_get16(&qd->ld.login_token_len, data + bytes);
+		if (qd->ld.login_token != NULL) g_free(qd->ld.login_token);
+		qd->ld.login_token = g_new0(guint8, qd->ld.login_token_len);
+		bytes += qq_getdata(qd->ld.login_token, qd->ld.login_token_len, data + bytes);
+		qq_show_packet("Get login token", qd->ld.login_token, qd->ld.login_token_len);
+		/* get login_key */
+		bytes += qq_getdata(qd->ld.login_key, sizeof(qd->ld.login_key), data + bytes);
+		qq_show_packet("Get login key", qd->ld.login_key, sizeof(qd->ld.login_key));
+		return QQ_LOGIN_REPLY_OK;
+	}
+
+	switch (ret)
+	{
+		case 0x34:		/* invalid password */
+			if (!purple_account_get_remember_password(gc->account)) {
+				purple_account_set_password(gc->account, NULL);
+			}
+			error = g_strdup(_("Error password"));
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+			break;
+		case 0x33:		/* need activation */
+		case 0x51:		/* need activation */
+			error = g_strdup(_("Need active"));
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+			break;
+		case 0xBF:		/* uid is not exist */
+			error = g_strdup(_("invalid user name"));
+			reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
+			break;
+		default:
+			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+					">>> [default] decrypt and dump");
+			error = g_strdup_printf(
+						_("Unknow reply code when checking password (0x%02X)"),
+						ret );
+			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+			break;
+	}
+
+	bytes += qq_get16(&msg_len, data + bytes);
+
+	msg = g_strndup((gchar *)data + bytes, msg_len);
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+
+	purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
+	purple_connection_error_reason(gc, reason, msg_utf8);
+
+	g_free(error);
+	g_free(msg);
+	g_free(msg_utf8);
+	return QQ_LOGIN_REPLY_ERR;
+}
+
 void qq_request_login_2007(PurpleConnection *gc)
 {
 	qq_data *qd;
@@ -1208,7 +1078,7 @@
 
 	/* create packet */
 	bytes = 0;
-	bytes += qq_put16(raw_data + bytes, 0);
+	bytes += qq_put16(raw_data + bytes, 0);		/* Unknow */
 	/* password encrypted */
 	bytes += qq_put16(raw_data + bytes, encrypted_len);
 	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
@@ -1216,28 +1086,30 @@
 	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 */
+	/* unknow fill 0 */
 	memset(raw_data + bytes, 0, 19);
 	bytes += 19;
 	bytes += qq_putdata(raw_data + bytes, login_1_16, sizeof(login_1_16));
 
-	bytes += qq_put8(raw_data + bytes, (guint8)rand() & 0xff);
+	bytes += qq_put8(raw_data + bytes, rand() & 0xff);
 	bytes += qq_put8(raw_data + bytes, qd->login_mode);
 	/* unknow 10 bytes zero filled*/
 	memset(raw_data + bytes, 0, 10);
 	bytes += 10;
 	/* redirect data, 15 bytes */
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)&qd->redirect_data, sizeof(qd->redirect_data));
+	bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
 	/* unknow fill */
 	bytes += qq_putdata(raw_data + bytes, login_2_16, sizeof(login_2_16));
 	/* captcha token get from qq_process_token_ex */
-	bytes += qq_put8(raw_data + bytes, qd->captcha.token_len);
-	bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_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);
 	/* unknow fill */
 	bytes += qq_putdata(raw_data + bytes, login_3_83, sizeof(login_3_83));
 	memset(raw_data + bytes, 0, 332 - sizeof(login_3_83));
 	bytes += 332 - sizeof(login_3_83);
 
+	qq_show_packet("Login", raw_data, bytes);
+
 	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
@@ -1362,7 +1234,6 @@
 	/* create packet */
 	bytes = 0;
 	bytes += qq_put16(raw_data + bytes, 0);		/* Unknow */
-	bytes += qq_put8(raw_data + bytes, 0);		/* Separator */
 	/* password encrypted */
 	bytes += qq_put16(raw_data + bytes, encrypted_len);
 	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
@@ -1387,12 +1258,12 @@
 	memset(raw_data + bytes, 0, 10);
 	bytes += 10;
 	/* redirect data, 15 bytes */
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)&qd->redirect_data, sizeof(qd->redirect_data));
+	bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
 	/* unknow fill */
 	bytes += qq_putdata(raw_data + bytes, login_2_16, sizeof(login_2_16));
 	/* captcha token get from qq_process_token_ex */
-	bytes += qq_put8(raw_data + bytes, qd->captcha.token_len);
-	bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_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);
 	/* unknow fill */
 	bytes += qq_putdata(raw_data + bytes, login_3_18, sizeof(login_3_18));
 	bytes += qq_put8(raw_data + bytes , sizeof(login_4_16));
@@ -1401,7 +1272,7 @@
 	memset(raw_data + bytes, 0, 10);
 	bytes += 10;
 	/* redirect data, 15 bytes */
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)&qd->redirect_data, sizeof(qd->redirect_data));
+	bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
 	/* unknow fill */
 	bytes += qq_putdata(raw_data + bytes, login_5_6, sizeof(login_5_6));
 	bytes += qq_put8(raw_data + bytes , sizeof(login_6_16));
@@ -1410,6 +1281,7 @@
 	memset(raw_data + bytes, 0, 249);
 	bytes += 249;
 
+	qq_show_packet("Login request", raw_data, bytes);
 	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
--- a/libpurple/protocols/qq/qq_base.h	Wed Oct 22 14:41:13 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.h	Wed Oct 22 14:42:23 2008 +0000
@@ -61,11 +61,8 @@
 guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len);
 void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha);
 
-void qq_request_check_pwd_2007(PurpleConnection *gc);
-guint8 qq_process_check_pwd_2007( PurpleConnection *gc, guint8 *data, gint data_len);
-
-void qq_request_check_pwd_2008(PurpleConnection *gc);
-guint8 qq_process_check_pwd_2008( PurpleConnection *gc, guint8 *data, gint data_len);
+void qq_request_check_pwd(PurpleConnection *gc);
+guint8 qq_process_check_pwd( PurpleConnection *gc, guint8 *data, gint data_len);
 
 void qq_request_login_2007(PurpleConnection *gc);
 guint8 qq_process_login_2007( PurpleConnection *gc, guint8 *data, gint data_len);
--- a/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:41:13 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:42:23 2008 +0000
@@ -663,11 +663,7 @@
 		case QQ_CMD_TOKEN_EX:
 			ret_8 = qq_process_token_ex(gc, data, data_len);
 			if (ret_8 == QQ_LOGIN_REPLY_OK) {
-				if (qd->client_version == 2008) {
-					qq_request_check_pwd_2008(gc);
-				} else {
-					qq_request_check_pwd_2007(gc);
-				}
+				qq_request_check_pwd(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) {
@@ -678,11 +674,7 @@
 			}
 			break;
 		case QQ_CMD_CHECK_PWD:
-			if (qd->client_version == 2008) {
-				ret_8 = qq_process_check_pwd_2008(gc, data, data_len);
-			} else {
-				ret_8 = qq_process_check_pwd_2007(gc, data, data_len);
-			}
+			ret_8 = qq_process_check_pwd(gc, data, data_len);
 			if (ret_8 != QQ_LOGIN_REPLY_OK) {
 				return  ret_8;
 			}