pidgin.openq: 3b08f755: 2008.09.28 - ccpaging <ccpaging(at)gmail...

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


-----------------------------------------------------------------
Revision: 3b08f7550aaf9d7fee867e516c48c1b499acf866
Ancestor: 1258cdc04b52798a308318cced149ec8845b43df
Author: csyfek at gmail.com
Date: 2008-10-22T14:40:04
Branch: im.pidgin.pidgin.openq
URL: http://d.pidgin.im/viewmtn/revision/info/3b08f7550aaf9d7fee867e516c48c1b499acf866

Modified files:
        libpurple/protocols/qq/ChangeLog
        libpurple/protocols/qq/buddy_list.c
        libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq.h
        libpurple/protocols/qq/qq_base.c
        libpurple/protocols/qq/qq_network.c
        libpurple/protocols/qq/qq_process.c

ChangeLog: 

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
		2. Check 2005's login reply packet, get last 3 login time.
		3. Server's notice and news is displayed in self buddy (The new buddy
created in buddy list).
		4. The notice messages when adding/removing QQ Qun's buddy displayed
in char conversation. They are displayed as purple notify windows in the past.
		5. The notice messages when adding/removing buddy displayed in self
buddy's conversation. They are displayed as purple notify windows in the past.
		6. Client version can be selected in  account option. Now only qq2005 is
working, other new version is only for debug.

-------------- next part --------------
============================================================
--- libpurple/protocols/qq/ChangeLog	0391dfa8d1fe53311f1f248e76537e382d52d784
+++ libpurple/protocols/qq/ChangeLog	8accecbe4de9e216da003053988ae37e8ef3ee41
@@ -1,3 +1,12 @@
+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
+		2. Check 2005's login reply packet, get last 3 login time.
+		3. Server's notice and news is displayed in self buddy (The new buddy created in buddy list).
+		4. The notice messages when adding/removing QQ Qun's buddy displayed in char conversation. They are displayed as purple notify windows in the past.
+		5. The notice messages when adding/removing buddy displayed in self buddy's conversation. They are displayed as purple notify windows in the past.
+		6. Client version can be selected in  account option. Now only qq2005 is working, other new version is only for debug.
+
 2008.09.26 - ccpaging <ccpaging(at)gmail.com>
 	* Added 'Request/Add/Remove Buddy' functions
 
============================================================
--- libpurple/protocols/qq/buddy_list.c	d1094ce99ac6dc4f9e8d4b8ef21bda69c9b833c7
+++ libpurple/protocols/qq/buddy_list.c	ced41b6fcd418107900307abbb3c333e2cfa689a
@@ -482,10 +482,20 @@ void qq_request_change_status(PurpleConn
 	if (fake_video)
 		misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO;
 
-	bytes = 0;
-	bytes += qq_put8(raw_data + bytes, away_cmd);
-	bytes += qq_put32(raw_data + bytes, misc_status);
-
+	if (qd->client_version > 2005) {
+		bytes = 0;
+		bytes += qq_put8(raw_data + bytes, away_cmd);
+		/* status version */
+		bytes += qq_put16(raw_data + bytes, 0);
+		bytes += qq_put16(raw_data + bytes, 0);
+		bytes += qq_put32(raw_data + bytes, misc_status);
+		/* Fixme: custom status message, now is empty */
+		bytes += qq_put16(raw_data + bytes, 0);
+	} else {
+		bytes = 0;
+		bytes += qq_put8(raw_data + bytes, away_cmd);
+		bytes += qq_put32(raw_data + bytes, misc_status);
+	}
 	qq_send_cmd_mess(gc, QQ_CMD_CHANGE_STATUS, raw_data, bytes, update_class, 0);
 }
 
============================================================
--- libpurple/protocols/qq/qq.c	b7f925bb3f0d335b90d1d45b7e2b3e0a9cb6094d
+++ libpurple/protocols/qq/qq.c	3ef664c2e4c8d8df0d6170a45b2b2ca9c726d650
@@ -555,13 +555,21 @@ static void action_show_account_info(Pur
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	qq_data *qd;
 	GString *info;
+	struct tm *tm_local;
+	int index;
 
 	qd = (qq_data *) gc->proto_data;
 	info = g_string_new("<html><body>");
 
-	g_string_append_printf(info, _("<b>This Login</b>: %s<br>\n"), ctime(&qd->login_time));
+	tm_local = localtime(&qd->login_time);
+	g_string_append_printf(info, _("<b>Login time</b>: %d-%d-%d, %d:%d:%d<br>\n"),
+			(1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+			tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
 	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));
+	tm_local = localtime(&qd->online_last_update);
+	g_string_append_printf(info, _("<b>Last Refresh</b>: %d-%d-%d, %d:%d:%d<br>\n"),
+			(1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+			tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
 
 	g_string_append(info, "<hr>");
 
@@ -579,10 +587,17 @@ static void action_show_account_info(Pur
 	g_string_append_printf(info, _("<b>Received Duplicate</b>: %lu<br>\n"), qd->net_stat.rcved_dup);
 
 	g_string_append(info, "<hr>");
-	g_string_append(info, "<i>Information below may not be accurate</i><br>\n");
+	g_string_append(info, "<i>Last Login Information</i><br>\n");
 
-	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);
+	for (index = 0; index < sizeof(qd->last_login_time) / sizeof(time_t); index++) {
+		tm_local = localtime(&qd->last_login_time[index]);
+		g_string_append_printf(info, _("<b>Time</b>: %d-%d-%d, %d:%d:%d<br>\n"),
+				(1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+				tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+	}
+	if (qd->last_login_ip.s_addr != 0) {
+		g_string_append_printf(info, _("<b>IP</b>: %s<br>\n"), inet_ntoa(qd->last_login_ip));
+	}
 
 	g_string_append(info, "</body></html>");
 
============================================================
--- libpurple/protocols/qq/qq.h	81f75e40c77db1d1d0011c8c7af40a544e866d1e
+++ libpurple/protocols/qq/qq.h	3c49a8915a49a2154922055de6d87055f1ad02e8
@@ -59,8 +59,10 @@ struct _qq_login_data {
 	guint8 token_len;
 	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 *login_token;
 	guint16 login_token_len;
 	guint8 login_key[QQ_KEY_LENGTH];
@@ -176,9 +178,11 @@ struct _qq_data {
 	PurpleXfer *xfer;			/* file transfer handler */
 
 	/* get from login reply packet */
+	struct in_addr my_local_ip;			/* my local ip address detected by server */
+	guint16 my_local_port;		/* my lcoal port detected by server */
 	time_t login_time;
-	time_t last_login_time;
-	gchar *last_login_ip;
+	time_t last_login_time[3];
+	struct in_addr last_login_ip;
 	/* get from keep_alive packet */
 	struct in_addr my_ip;			/* my ip address detected by server */
 	guint16 my_port;		/* my port detected by server */
============================================================
--- libpurple/protocols/qq/qq_base.c	9998ab6705011d4929fa854b0008db696be5f4ad
+++ libpurple/protocols/qq/qq_base.c	e15aef0275ac56af6b9891d8ee558e0d1ed8b5fe
@@ -59,94 +59,86 @@ static gint8 process_login_ok(PurpleConn
 /* process login reply which says OK */
 static gint8 process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
 {
-	gint bytes;
 	qq_data *qd;
+	gint bytes;
 
-	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;
+	guint8 ret;
+	guint32 uid;
+	struct in_addr ip;
+	guint16 port;
+	struct tm *tm_local;
 
 	qd = (qq_data *) gc->proto_data;
+	qq_show_packet("Login reply", data, len);
+
 	/* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
 	bytes = 0;
+	bytes += qq_get8(&ret, data + bytes);
+	bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
+	get_session_md5(qd->session_md5, qd->uid, qd->session_key);
+	purple_debug_info("QQ", "Got session_key\n");
+	bytes += qq_get32(&uid, data + bytes);
+	if (uid != qd->uid) {
+		purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+	}
+	bytes += qq_getIP(&qd->my_ip, data + bytes);
+	bytes += qq_get16(&qd->my_port, data + bytes);
+	purple_debug_info("QQ", "Internet IP: %s, %d\n", inet_ntoa(qd->my_ip), qd->my_port);
 
-	/* 000-000: reply code */
-	bytes += qq_get8(&packet.result, data + bytes);
-	/* 001-016: session key */
-	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(&packet.uid, data + bytes);
-	/* 021-024: server detected user public IP */
-	bytes += qq_getIP(&packet.client_ip, data + bytes);
-	/* 025-026: server detected user port */
-	bytes += qq_get16(&packet.client_port, data + bytes);
-	/* 027-030: server detected itself ip 127.0.0.1 ? */
-	bytes += qq_getIP(&packet.server_ip, data + bytes);
-	/* 031-032: server listening port */
-	bytes += qq_get16(&packet.server_port, data + bytes);
-	/* 033-036: login time for current session */
-	bytes += qq_getime(&packet.login_time, data + bytes);
-	/* 037-062: 26 bytes, unknown */
-	bytes += qq_getdata((guint8 *) &packet.unknown1, 26, data + bytes);
-	/* 063-066: unknown server1 ip address */
-	bytes += qq_getIP(&packet.unknown_server1_ip, data + bytes);
-	/* 067-068: unknown server1 port */
-	bytes += qq_get16(&packet.unknown_server1_port, data + bytes);
-	/* 069-072: unknown server2 ip address */
-	bytes += qq_getIP(&packet.unknown_server2_ip, data + bytes);
-	/* 073-074: unknown server2 port */
-	bytes += qq_get16(&packet.unknown_server2_port, data + bytes);
-	/* 075-076: 2 bytes unknown */
-	bytes += qq_get16(&packet.unknown2, data + bytes);
-	/* 077-078: 2 bytes unknown */
-	bytes += qq_get16(&packet.unknown3, data + bytes);
-	/* 079-110: 32 bytes unknown */
-	bytes += qq_getdata((guint8 *) &packet.unknown4, 32, data + bytes);
-	/* 111-122: 12 bytes unknown */
-	bytes += qq_getdata((guint8 *) &packet.unknown5, 12, data + bytes);
-	/* 123-126: login IP of last session */
-	bytes += qq_getIP(&packet.last_client_ip, data + bytes);
-	/* 127-130: login time of last session */
-	bytes += qq_getime(&packet.last_login_time, data + bytes);
-	/* 131-138: 8 bytes unknown */
-	bytes += qq_getdata((guint8 *) &packet.unknown6, 8, data + bytes);
+	bytes += qq_getIP(&qd->my_local_ip, data + bytes);
+	bytes += qq_get16(&qd->my_local_port, data + bytes);
+	purple_debug_info("QQ", "Local IP: %s, %d\n", inet_ntoa(qd->my_local_ip), qd->my_local_port);
 
+	bytes += qq_getime(&qd->login_time, data + bytes);
+	tm_local = localtime(&qd->login_time);
+	purple_debug_info("QQ", "Login time: %d-%d-%d, %d:%d:%d\n",
+			(1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+			tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+	/* skip unknown 2 bytes, 0x(03 0a) */
+	bytes += 2;
+	/* skip unknown 24 bytes, maybe token to access Qun shared files */
+	bytes += 24;
+	/* unknow ip and port */
+	bytes += qq_getIP(&ip, data + bytes);
+	bytes += qq_get16(&port, data + bytes);
+	purple_debug_info("QQ", "Unknow IP: %s, %d\n", inet_ntoa(ip), port);
+	/* unknow ip and port */
+	bytes += qq_getIP(&ip, data + bytes);
+	bytes += qq_get16(&port, data + bytes);
+	purple_debug_info("QQ", "Unknow IP: %s, %d\n", inet_ntoa(ip), port);
+	/* unknown 4 bytes, 0x(00 81 00 00)*/
+	bytes += 4;
+	/* skip unknown 32 bytes, maybe key to access QQ Home */
+	bytes += 32;
+	/* skip unknown 16 bytes, 0x(00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00) */
+	bytes += 16;
+	/* time */
+	bytes += qq_getime(&qd->last_login_time[0], data + bytes);
+	tm_local = localtime(&qd->last_login_time[0]);
+	purple_debug_info("QQ", "Last login time: %d-%d-%d, %d:%d:%d\n",
+			(1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+			tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+	/* unknow time */
+	g_return_val_if_fail(sizeof(qd->last_login_time) / sizeof(time_t) > 1, QQ_LOGIN_REPLY_OK);
+	bytes += qq_getime(&qd->last_login_time[1], data + bytes);
+	tm_local = localtime(&qd->last_login_time[1]);
+	purple_debug_info("QQ", "Time: %d-%d-%d, %d:%d:%d\n",
+			(1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+			tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+
+	g_return_val_if_fail(sizeof(qd->last_login_time) / sizeof(time_t) > 2, QQ_LOGIN_REPLY_OK);
+	bytes += qq_getime(&qd->last_login_time[2], data + bytes);
+	tm_local = localtime(&qd->last_login_time[2]);
+	purple_debug_info("QQ", "Time: %d-%d-%d, %d:%d:%d\n",
+			(1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+			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 */
-
-	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 = packet.client_ip.s_addr;
-
-	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;
 }
 
@@ -255,9 +247,9 @@ void qq_request_login(PurpleConnection *
 	bytes = 0;
 	/* now generate the encrypted data
 	 * 000-015 use password_twice_md5 as key to encrypt empty string */
-	encrypted_len = qq_encrypt(raw_data + bytes, (guint8 *) "", 0, qd->ld.pwd_2nd_md5);
+	encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_2nd_md5);
 	g_return_if_fail(encrypted_len == 16);
-	bytes += encrypted_len;
+	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
 
 	/* 016-016 */
 	bytes += qq_put8(raw_data + bytes, 0x00);
@@ -430,7 +422,7 @@ guint8 qq_process_login( PurpleConnectio
 			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)"),
+						_("Unknow reply code when login (0x%02X)"),
 						ret );
 			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
 			break;
@@ -871,7 +863,9 @@ void qq_request_check_pwd_2007(PurpleCon
 	gint bytes;
 	guint8 *encrypted;
 	gint encrypted_len;
-	static guint8 header[] = { 0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0x0E };
+	static guint8 header[] = {
+			0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0x0E
+	};
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
@@ -894,7 +888,7 @@ void qq_request_check_pwd_2007(PurpleCon
 	/* create packet */
 	bytes = 0;
 	bytes += qq_putdata(raw_data + bytes, header, sizeof(header));
-	/* token get from qq_request_token_ex */
+	/* token get from qq_process_token_ex */
 	bytes += qq_put16(raw_data + bytes, qd->ld.token_ex_len);
 	bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
 	/* password encrypted */
@@ -1086,9 +1080,13 @@ void qq_request_check_pwd_2008(PurpleCon
 	gint bytes;
 	guint8 *encrypted;
 	gint encrypted_len;
-	static guint8 header[] = { 0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0xE0 };
-	static guint8 unknown[] = { 0xDB, 0xB9, 0xF3, 0x0B, 0xF9, 0x13, 0x87, 0xB2,
-		0xE6, 0x20, 0x43, 0xBE, 0x53, 0xCA, 0x65, 0x03 };
+	static guint8 header[] = {
+			0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0xE0
+	};
+	static guint8 unknown[] = {
+			0xDB, 0xB9, 0xF3, 0x0B, 0xF9, 0x13, 0x87, 0xB2,
+			0xE6, 0x20, 0x43, 0xBE, 0x53, 0xCA, 0x65, 0x03
+	};
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
@@ -1146,105 +1144,316 @@ void qq_request_check_pwd_2008(PurpleCon
 	qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
 }
 
-/*
-static void qq_send_packet_login2007(PurpleConnection *gc)
+void qq_request_login_2007(PurpleConnection *gc)
 {
 	qq_data *qd;
-	guint8 *buf, *cursor, *cursor_verify_data;
-	guint16 seq_ret;
-	gint encrypted_len, bytes;
-	gint pos, bodyOffset, encrypted_data_bytes, tail_offset, body_length, temp_pos;
-	guint8 verifyData[QQ_LOGIN_DATA_LENGTH], raw_data[QQ_LOGIN_ENCRYPT_BUFFER], encrypted_data[QQ_LOGIN_DATA_LENGTH];
-	
-	memset(raw_data, 0, QQ_LOGIN_ENCRYPT_BUFFER);
-	memset(verifyData, 0, QQ_LOGIN_DATA_LENGTH);
-	memset(encrypted_data, 0, QQ_LOGIN_DATA_LENGTH);
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted;
+	gint encrypted_len;
+	static const guint8 login_1_16[] = {
+			0x56, 0x4E, 0xC8, 0xFB, 0x0A, 0x4F, 0xEF, 0xB3,
+			0x7A, 0x5D, 0xD8, 0x86, 0x0F, 0xAC, 0xE5, 0x1A
+	};
+	static const guint8 login_2_16[] = {
+			0x5E, 0x22, 0x3A, 0xBE, 0x13, 0xBF, 0xDA, 0x4C,
+			0xA9, 0xB7, 0x0B, 0x43, 0x63, 0x51, 0x8E, 0x28
+	};
+	static const guint8 login_3_83[] = {
+			0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x01, 0x40, 0x01, 0x01, 0x58, 0x83,
+			0xD0, 0x00, 0x10, 0x9D, 0x14, 0x64, 0x0A, 0x2E,
+			0xE2, 0x11, 0xF7, 0x90, 0xF0, 0xB5, 0x5F, 0x16,
+			0xFB, 0x41, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x02, 0x76, 0x3C, 0xEE,
+			0x4A, 0x00, 0x10, 0x86, 0x81, 0xAD, 0x1F, 0xC8,
+			0xC9, 0xCC, 0xCF, 0xCA, 0x9F, 0xFF, 0x88, 0xC0,
+			0x5C, 0x88, 0xD5
+	};
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
+
+	g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+
+	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+
+	/* Encrypted password and put in encrypted */
+	bytes = 0;
+	bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_2nd_md5, sizeof(qd->ld.pwd_2nd_md5));
+	bytes += qq_put16(raw_data + bytes, 0);
+	bytes += qq_put16(raw_data + bytes, 0xffff);
+
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_4th_md5);
+
+	/* create packet */
+	bytes = 0;
+	bytes += qq_put16(raw_data + bytes, 0);
+	/* 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);
+	g_return_if_fail(encrypted_len == 16);
+	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+	/* unknow fill */
+	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, 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));
+	/* 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);
+	/* 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);
+
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
+
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
-	
-	cursor = buf;
+	memset(buf, 0, MAX_PACKET_SIZE);
 	bytes = 0;
-	bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_LOGIN, TRUE, &seq_ret);
-	bytes += create_packet_dw(buf, &cursor, qd->uid);
+	/* logint token get from qq_process_check_pwd_2007 */
+	bytes += qq_put16(buf + bytes, qd->ld.login_token_len);
+	bytes += qq_putdata(buf + bytes, qd->ld.login_token, qd->ld.login_token_len);
+	bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
 
-	bodyOffset = bytes;
+	qd->send_seq++;
+	qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
+}
 
-	bytes += create_packet_w(buf, &cursor, qd->passport_key_lenght);
-	bytes += create_packet_data(buf, &cursor, qd->passport_key, qd->passport_key_lenght);
-	bytes += create_packet_w(buf, &cursor, 0);
-	pos = bytes;
-	bytes += 2;
-	cursor += 2;
+/* process the login reply packet */
+guint8 qq_process_login_2007( PurpleConnection *gc, guint8 *data, gint data_len)
+{
+	qq_data *qd;
+	gint bytes;
+	guint8 ret;
+	gchar *error;
+	guint32 uid;
 
-	encrypted_data_bytes = 0;
-	cursor_verify_data = verifyData;
-	encrypted_data_bytes += create_packet_data(verifyData, &cursor_verify_data, qd->pwkey, QQ_KEY_LENGTH);
-	encrypted_data_bytes += create_packet_b(verifyData, &cursor_verify_data, 0);
-	encrypted_data_bytes += create_packet_b(verifyData, &cursor_verify_data, 0);
-	encrypted_data_bytes += create_packet_b(verifyData, &cursor_verify_data, 255);
-	encrypted_data_bytes += create_packet_b(verifyData, &cursor_verify_data, 255);
-//	encrypted_data_bytes += create_packet_w(verifyData, &cursor_verify_data, 0);
-	
-	qq_encrypt(verifyData, encrypted_data_bytes, qd->pwkey_double, encrypted_data, &encrypted_len);
-	bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
-	
-	temp_pos = bytes;
-	bytes = pos;
-	cursor = buf+bytes;
-	bytes += create_packet_w(buf, &cursor, encrypted_len);
-	bytes = temp_pos;
-	cursor = buf+bytes;
-	
-	qq_encrypt((guint8 *) "", 0, qd->pwkey_double, raw_data, &encrypted_len);
-	bytes += create_packet_data(buf, &cursor, raw_data, encrypted_len);
-	
-	g_memmove(buf+bytes, kQQFixedContent1_35, 35);
-	bytes += 35;
-	cursor = buf+bytes;
-	
-	bytes += create_packet_b(buf, &cursor, g_random_int_range (0,255));
-	bytes += create_packet_b(buf, &cursor, qd->login_mode);
-	
-	bytes += create_packet_dw(buf, &cursor, 0);
-	bytes += create_packet_dw(buf, &cursor, 0);
-	bytes += create_packet_w(buf, &cursor, 0);
-	
-	bytes += create_packet_data(buf, &cursor, qd->selected_server, qd->selected_server_lenght);
-	
-	g_memmove(buf+bytes, kQQFixedContent2_16, 16);
-	bytes += 16;
-	cursor = buf+bytes;
-	
-	bytes += create_packet_b(buf, &cursor, qd->login_token_lenght);
-	bytes += create_packet_data(buf, &cursor, qd->login_token, qd->login_token_lenght);
+	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
 
-	g_memmove(buf+bytes, kQQFixedContent3_332, 332);
-	bytes += 332;
-	cursor = buf+bytes;
-	
-	tail_offset = bytes;
-	body_length = tail_offset - bodyOffset;
+	qd = (qq_data *) gc->proto_data;
 
-	memset(raw_data, 0, QQ_LOGIN_ENCRYPT_BUFFER);
-	encrypted_len = body_length;
-//	qq_encrypt(buf+passport_length+2+11, body_length-passport_length-2 , qd->pwkey, raw_data, &encrypted_len);
-	qq_encrypt(buf+qd->passport_key_lenght+2+11, body_length-qd->passport_key_lenght-2 , qd->login_key, raw_data, &encrypted_len);
-	bytes = qd->passport_key_lenght+2+11;
-	cursor = buf+bytes;
-	bytes += create_packet_data(buf, &cursor, raw_data, encrypted_len);
-	bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
+	bytes = 0;
+	bytes += qq_get8(&ret, data + bytes);
+	if (ret != 0) {
+		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+				">>> [default] decrypt and dump");
+		error = g_strdup_printf(
+				_("Unknow reply code when login (0x%02X)"),
+				ret );
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+				error);
+		g_free(error);
+		return QQ_LOGIN_REPLY_ERR;
+	}
 
-	if (bytes == (cursor - buf))	// packet creation OK
-		_qq_send_packet(gc, buf, bytes, QQ_CMD_LOGIN);
-	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create login packet\n");
-	
+	bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
+	purple_debug_info("QQ", "Got session_key\n");
+	get_session_md5(qd->session_md5, qd->uid, qd->session_key);
+
+	bytes += qq_get32(&uid, data + bytes);
+	if (uid != qd->uid) {
+		purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+	}
+	bytes += qq_getIP(&qd->my_ip, data + bytes);
+	bytes += qq_get16(&qd->my_port, data + bytes);
+	bytes += qq_getIP(&qd->my_local_ip, data + bytes);
+	bytes += qq_get16(&qd->my_local_port, data + bytes);
+	bytes += qq_getime(&qd->login_time, data + bytes);
+	/* skip unknow 50 byte */
+	bytes += 50;
+	/* skip client key 32 byte */
+	bytes += 32;
+	/* skip unknow 12 byte */
+	bytes += 12;
+	/* last login */
+	bytes += qq_getIP(&qd->last_login_ip, data + bytes);
+	bytes += qq_getime(&qd->last_login_time[0], data + bytes);
+	purple_debug_info("QQ", "Last Login: %s, %s\n",
+			inet_ntoa(qd->last_login_ip), ctime(&qd->last_login_time[0]));
+	return QQ_LOGIN_REPLY_OK;
 }
- */
-void qq_request_login_2007(PurpleConnection *gc)
+
+void qq_request_login_2008(PurpleConnection *gc)
 {
+	qq_data *qd;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted;
+	gint encrypted_len;
+	guint8 index, count;
+
+	static const guint8 login_1_16[] = {
+			0xD2, 0x41, 0x75, 0x12, 0xC2, 0x86, 0x57, 0x10,
+			0x78, 0x57, 0xDC, 0x24, 0x8C, 0xAA, 0x8F, 0x4E
+	};
+
+	static const guint8 login_2_16[] = {
+			0x94, 0x0B, 0x73, 0x7A, 0xA2, 0x51, 0xF0, 0x4B,
+			0x95, 0x2F, 0xC6, 0x0A, 0x5B, 0xF6, 0x76, 0x52
+	};
+	static const guint8 login_3_18[] = {
+			0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x01, 0x40, 0x01, 0x1b, 0x02, 0x84,
+			0x50, 0x00
+	};
+	static const guint8 login_4_16[] = {
+			0x2D, 0x49, 0x15, 0x55, 0x78, 0xFC, 0xF3, 0xD4,
+			0x53, 0x55, 0x60, 0x9C, 0x37, 0x9F, 0xE9, 0x59
+	};
+	static const guint8 login_5_6[] = {
+			0x02, 0x68, 0xe8, 0x07, 0x83, 0x00
+	};
+	static const guint8 login_6_16[] = {
+			0x3B, 0xCE, 0x43, 0xF1, 0x8B, 0xA4, 0x2B, 0xB5,
+			0xB3, 0x51, 0x57, 0xF7, 0x06, 0x4B, 0x18, 0xFC
+	};
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+
+	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+	encrypted = g_newa(guint8, MAX_PACKET_SIZE);	/* 16 bytes more */
+
+	/* Encrypted password and put in encrypted */
+	bytes = 0;
+	bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_2nd_md5, sizeof(qd->ld.pwd_2nd_md5));
+	bytes += qq_put16(raw_data + bytes, 0);
+	bytes += qq_put16(raw_data + bytes, 0xffff);
+
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_4th_md5);
+
+	/* 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);
+	/* put data which NULL string encrypted by key pwd_4th_md5 */
+	encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_4th_md5);
+	g_return_if_fail(encrypted_len == 16);
+	bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+	/* unknow 19 bytes zero filled*/
+	memset(raw_data + bytes, 0, 19);
+	bytes += 19;
+	bytes += qq_putdata(raw_data + bytes, login_1_16, sizeof(login_1_16));
+
+	index = rand() % 3;	  /* can be set as 1 */
+	for( count = 0; count < encrypted_len;  count++ )
+		index ^= encrypted[count];
+	for( count = 0; count < sizeof(login_1_16);  count++ )
+		index ^= login_1_16[count];
+	bytes += qq_put8(raw_data + bytes, index);	/* random in QQ 2007*/
+
+	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));
+	/* 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);
+	/* 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));
+	bytes += qq_putdata(raw_data + bytes, login_4_16, sizeof(login_4_16));
+	/* 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));
+	/* 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));
+	bytes += qq_putdata(raw_data + bytes, login_6_16, sizeof(login_6_16));
+	/* unknow 249 bytes zero filled*/
+	memset(raw_data + bytes, 0, 249);
+	bytes += 249;
+
+	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
+
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
+	bytes = 0;
+	/* logint token get from qq_process_check_pwd_2007 */
+	bytes += qq_put16(buf + bytes, qd->ld.login_token_len);
+	bytes += qq_putdata(buf + bytes, qd->ld.login_token, qd->ld.login_token_len);
+	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);
 }
 
-void qq_request_login_2008(PurpleConnection *gc)
+guint8 qq_process_login_2008( PurpleConnection *gc, guint8 *data, gint data_len)
 {
+	qq_data *qd;
+	gint bytes;
+	guint8 ret;
+	gchar *error;
+	guint32 uid;
+
+	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
+
+	qd = (qq_data *) gc->proto_data;
+
+	bytes = 0;
+	bytes += qq_get8(&ret, data + bytes);
+	if (ret != 0) {
+		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+				">>> [default] decrypt and dump");
+		error = g_strdup_printf(
+				_("Unknow reply code when login (0x%02X)"),
+				ret );
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+				error);
+		g_free(error);
+		return QQ_LOGIN_REPLY_ERR;
+	}
+
+	bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
+	purple_debug_info("QQ", "Got session_key\n");
+	get_session_md5(qd->session_md5, qd->uid, qd->session_key);
+
+	bytes += qq_get32(&uid, data + bytes);
+	if (uid != qd->uid) {
+		purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+	}
+	bytes += qq_getIP(&qd->my_ip, data + bytes);
+	bytes += qq_get16(&qd->my_port, data + bytes);
+	bytes += qq_getIP(&qd->my_local_ip, data + bytes);
+	bytes += qq_get16(&qd->my_local_port, data + bytes);
+	bytes += qq_getime(&qd->login_time, data + bytes);
+	/* skip 1 byte, always 0x03 */
+	/* skip 1 byte, login mode */
+	bytes = 131;
+	bytes += qq_getIP(&qd->last_login_ip, data + bytes);
+	bytes += qq_getime(&qd->last_login_time[0], data + bytes);
+	purple_debug_info("QQ", "Last Login: %s, %s\n",
+			inet_ntoa(qd->last_login_ip), ctime(&qd->last_login_time[0]));
+	return QQ_LOGIN_REPLY_OK;
 }
============================================================
--- libpurple/protocols/qq/qq_network.c	633a2fb01e5d4d9570670883c0b7e96b4aea23fb
+++ libpurple/protocols/qq/qq_network.c	8c495458bcb243c0f708a8cc7a357f7006fd2acb
@@ -1027,7 +1027,10 @@ void qq_disconnect(PurpleConnection *gc)
 	memset(qd->session_key, 0, sizeof(qd->session_key));
 	memset(qd->session_md5, 0, sizeof(qd->session_md5));
 
+	qd->my_local_ip.s_addr = 0;
+	qd->my_local_port = 0;
 	qd->my_ip.s_addr = 0;
+	qd->my_port = 0;
 
 	qq_group_free_all(qd);
 	qq_buddies_list_free(gc->account, qd);
============================================================
--- libpurple/protocols/qq/qq_process.c	f299a881d70bbf12c5a9dfdb4f3b90fc65cdc5b8
+++ libpurple/protocols/qq/qq_process.c	82531c57433cf8601a7eaf021afbe4d130178663
@@ -613,15 +613,28 @@ guint8 qq_proc_login_cmds(PurpleConnecti
 				}
 			}
 			break;
+		case QQ_CMD_LOGIN:
 		default:
-			/* May use password_twice_md5 in the past version like QQ2005 */
-			data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
-			if (data_len >= 0) {
-				purple_debug_warning("QQ", "Decrypt login packet by random_key, %d bytes\n", data_len);
+			if (qd->client_version > 2005) {
+				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_4th_md5);
+				if (data_len >= 0) {
+					purple_debug_warning("QQ", "Decrypt login packet by pwd_4th_md5\n");
+				} else {
+					data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.login_key);
+					if (data_len >= 0) {
+						purple_debug_warning("QQ", "Decrypt login packet by login_key\n");
+					}
+				}
 			} else {
-				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_2nd_md5);
+				/* May use password_twice_md5 in the past version like QQ2005 */
+				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
 				if (data_len >= 0) {
-					purple_debug_warning("QQ", "Decrypt login packet by pwd_2nd_md5, %d bytes\n", data_len);
+					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);
+					if (data_len >= 0) {
+						purple_debug_warning("QQ", "Decrypt login packet by pwd_2nd_md5\n");
+					}
 				}
 			}
 			break;
@@ -680,7 +693,13 @@ guint8 qq_proc_login_cmds(PurpleConnecti
 			}
 			break;
 		case QQ_CMD_LOGIN:
-			ret_8 = qq_process_login(gc, data, data_len);
+			if (qd->client_version == 2008) {
+				ret_8 = qq_process_login_2008(gc, data, data_len);
+			} else if (qd->client_version == 2007) {
+				ret_8 = qq_process_login_2007(gc, data, data_len);
+			} else {
+				ret_8 = qq_process_login(gc, data, data_len);
+			}
 			if (ret_8 != QQ_LOGIN_REPLY_OK) {
 				return  ret_8;
 			}


More information about the Commits mailing list