changeset 24071:619ac2303c46

2009.10.02 - ccpaging <ccpaging(at)gmail.com> * Added 'Captcha Display' function * Most functions from patch written by Emil Alexiev merged into trunk, except 'buddy operations' * 'online buddy status' and 'qun buddies' still have problems
author SHiNE CsyFeK <csyfek@gmail.com>
date Wed, 22 Oct 2008 14:43:46 +0000
parents 832178d951ca
children efd4a0e6dd86
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/buddy_list.c libpurple/protocols/qq/buddy_list.h libpurple/protocols/qq/char_conv.c libpurple/protocols/qq/char_conv.h libpurple/protocols/qq/group_im.c libpurple/protocols/qq/im.c libpurple/protocols/qq/packet_parse.c libpurple/protocols/qq/packet_parse.h libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq_base.c libpurple/protocols/qq/qq_base.h libpurple/protocols/qq/qq_network.c libpurple/protocols/qq/qq_network.h libpurple/protocols/qq/qq_process.c libpurple/protocols/qq/send_file.c
diffstat 16 files changed, 293 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:43:46 2008 +0000
@@ -1,3 +1,8 @@
+2009.10.02 - ccpaging <ccpaging(at)gmail.com>
+	* Added 'Captcha Display' function
+	* Most functions from patch written by Emil Alexiev merged into trunk, except 'buddy operations'
+	* 'online buddy status' and 'qun buddies' still have problems
+
 2008.09.30 - ccpaging <ccpaging(at)gmail.com>
 	* Successfully login using 2007/2008 protocols
 
--- a/libpurple/protocols/qq/buddy_list.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Wed Oct 22 14:43:46 2008 +0000
@@ -84,9 +84,12 @@
  * server may return a position tag if list is too long for one packet */
 void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint update_class)
 {
+	qq_data *qd;
 	guint8 raw_data[16] = {0};
 	gint bytes = 0;
 
+	qd = (qq_data *) gc->proto_data;
+
 	/* 000-001 starting position, can manually specify */
 	bytes += qq_put16(raw_data + bytes, position);
 	/* before Mar 18, 2004, any value can work, and we sent 00
@@ -95,7 +98,10 @@
 	 * Now I tested that 00,00,00,00,00,01 work perfectly
 	 * March 22, found the 00,00,00 starts to work as well */
 	bytes += qq_put8(raw_data + bytes, 0x00);
-
+	if (qd->client_version >= 2007) {
+		bytes += qq_put16(raw_data + bytes, 0x0000);
+	}
+	
 	qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes, update_class, 0);
 }
 
@@ -177,7 +183,7 @@
 	count = 0;
 	while (bytes < data_len) {
 		if (data_len - bytes < QQ_ONLINE_BUDDY_ENTRY_LEN) {
-			purple_debug_error("QQ", "[buddies online] only %d, need %d",
+			purple_debug_error("QQ", "[buddies online] only %d, need %d\n",
 					(data_len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN);
 			break;
 		}
@@ -292,11 +298,18 @@
 		bytes += pascal_len;
 		qq_filter_str(q_bud->nickname);
 
+		/* Fixme: merge following as 32bit flag */
 		bytes += qq_get16(&unknown, data + bytes);
 		bytes += qq_get8(&q_bud->ext_flag, data + bytes);
 		bytes += qq_get8(&q_bud->comm_flag, data + bytes);
+		
+		if (qd->client_version >= 2007) {
+			bytes += 4;		/* skip 4 bytes */
+			bytes_expected = 16 + pascal_len;
+		} else {
+			bytes_expected = 12 + pascal_len;
+		}
 
-		bytes_expected = 12 + pascal_len;
 
 		if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
 			purple_debug_info("QQ",
@@ -390,6 +403,10 @@
 				purple_debug_info("QQ",
 					"Not find room id %d in qq_process_get_buddies_and_rooms\n", uid);
 				qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
+				//group = g_newa(qq_group, 1);
+				//group->id = uid;
+				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, uid, NULL, 0,
+						0, 0);
 			} else {
 				group->my_role = QQ_ROOM_ROLE_YES;
 				qq_group_refresh(gc, group);
@@ -421,6 +438,7 @@
 		case QQ_BUDDY_ONLINE_NORMAL:
 		case QQ_BUDDY_ONLINE_AWAY:
 		case QQ_BUDDY_ONLINE_INVISIBLE:
+		case QQ_BUDDY_ONLINE_BUSY:
 			return TRUE;
 		case QQ_BUDDY_CHANGE_TO_OFFLINE:
 			return FALSE;
@@ -467,22 +485,29 @@
 	if (!qd->is_login)
 		return;
 
-	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
-		away_cmd = QQ_BUDDY_ONLINE_INVISIBLE;
-	} else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
-			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
-			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
-		away_cmd = QQ_BUDDY_ONLINE_AWAY;
-	} else {
-		away_cmd = QQ_BUDDY_ONLINE_NORMAL;
-	}
-
+	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
+		away_cmd = QQ_BUDDY_ONLINE_INVISIBLE;
+	} else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE))
+	{
+		if (qd->client_version >= 2007) {
+			away_cmd = QQ_BUDDY_ONLINE_BUSY;
+		} else {
+			away_cmd = QQ_BUDDY_ONLINE_INVISIBLE;
+		}
+	} else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
+			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
+			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
+		away_cmd = QQ_BUDDY_ONLINE_AWAY;
+	} else {
+		away_cmd = QQ_BUDDY_ONLINE_NORMAL;
+	}
+	
 	misc_status = 0x00000000;
 	fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video");
 	if (fake_video)
 		misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO;
 
-	if (qd->client_version > 2005) {
+	if (qd->client_version >= 2007) {
 		bytes = 0;
 		bytes += qq_put8(raw_data + bytes, away_cmd);
 		/* status version */
@@ -625,6 +650,9 @@
 	case QQ_BUDDY_ONLINE_INVISIBLE:
 		status_id = "invisible";
 		break;
+	case QQ_BUDDY_ONLINE_BUSY:
+		status_id = "busy";
+		break;
 	default:
 		status_id = "invisible";
 		purple_debug_error("QQ", "unknown status: %x\n", q_bud->status);
--- a/libpurple/protocols/qq/buddy_list.h	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.h	Wed Oct 22 14:43:46 2008 +0000
@@ -45,7 +45,8 @@
 	QQ_BUDDY_ONLINE_NORMAL = 10,
 	QQ_BUDDY_CHANGE_TO_OFFLINE = 20,
 	QQ_BUDDY_ONLINE_AWAY = 30,
-	QQ_BUDDY_ONLINE_INVISIBLE = 40
+	QQ_BUDDY_ONLINE_INVISIBLE = 40,
+	QQ_BUDDY_ONLINE_BUSY = 50,
 };
 
 void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint update_class);
--- a/libpurple/protocols/qq/char_conv.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Wed Oct 22 14:43:46 2008 +0000
@@ -141,7 +141,7 @@
 }
 
 /* convert QQ formatted msg to Purple formatted msg (and UTF-8) */
-gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg)
+gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg, const gint client_version)
 {
 	GString *encoded;
 	guint8 font_attr, font_size, color[3], bar;
@@ -153,6 +153,9 @@
 	/* checked qq_show_packet OK */
 	/* qq_show_packet("QQ_MESG recv for font style", data, len); */
 
+	if (client_version >= 2007) {
+		bytes += 1;
+	}
 	bytes += qq_get8(&font_attr, data + bytes);
 	bytes += qq_getdata(color, 3, data + bytes);	/* red,green,blue */
 	color_code = g_strdup_printf("#%02x%02x%02x", color[0], color[1], color[2]);
@@ -232,8 +235,10 @@
 
 	converted = g_string_new("");
 	segments = split_data((guint8 *) text, strlen(text), "\x14\x15", 0);
+	if(segments == NULL)
+		return NULL;
+
 	g_string_append(converted, segments[0]);
-
 	while ((*(++segments)) != NULL) {
 		cur_seg = *segments;
 		qq_smiley = cur_seg[0];
--- a/libpurple/protocols/qq/char_conv.h	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.h	Wed Oct 22 14:43:46 2008 +0000
@@ -37,7 +37,7 @@
 
 gchar *utf8_to_qq(const gchar *str, const gchar *to_charset);
 gchar *qq_to_utf8(const gchar *str, const gchar *from_charset);
-gchar *qq_encode_to_purple(guint8 *font_attr_data, gint len, const gchar *msg);
+gchar *qq_encode_to_purple(guint8 *font_attr_data, gint len, const gchar *msg, const gint client_version);
 
 gchar *qq_im_filter_html(const gchar *text);
 void qq_filter_str(gchar *str);
--- a/libpurple/protocols/qq/group_im.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Wed Oct 22 14:43:46 2008 +0000
@@ -401,12 +401,12 @@
 
 	/* group im_group has no flag to indicate whether it has font_attr or not */
 	msg_with_purple_smiley = qq_smiley_to_purple(packet.msg);
-	if (packet.font_attr_len > 0)
+	if (packet.font_attr_len > 0) {
 		msg_utf8_encoded = qq_encode_to_purple(packet.font_attr,
-				packet.font_attr_len, msg_with_purple_smiley);
-	else
+				packet.font_attr_len, msg_with_purple_smiley, qd->client_version);
+	} else {
 		msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
-
+	}
 	group = qq_room_search_id(gc, id);
  	qq_room_got_chat_in(gc, group, packet.member_uid, msg_utf8_encoded, packet.send_time);
 
--- a/libpurple/protocols/qq/im.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Wed Oct 22 14:43:46 2008 +0000
@@ -390,7 +390,8 @@
 	msg_utf8_encoded = im_text->is_there_font_attr ?
 		qq_encode_to_purple(im_text->font_attr,
 				im_text->font_attr_len,
-				msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
+				msg_with_purple_smiley, qd->client_version) 
+		: qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
 
 	/* send encoded to purple, note that we use im_text->send_time,
 	 * not the time we receive the message
--- a/libpurple/protocols/qq/packet_parse.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.c	Wed Oct 22 14:43:46 2008 +0000
@@ -151,6 +151,9 @@
  * return the number of bytes packed, otherwise return -1 */
 gint qq_put32(guint8 *buf, guint32 dw)
 {
+	guint32 dw_dest;
+	memcpy(&dw_dest, buf, sizeof(dw_dest));
+
     guint32 dw_porter;
     dw_porter = g_htonl(dw);
 #ifdef PARSER_DEBUG
@@ -161,6 +164,19 @@
     return sizeof(dw_porter);
 }
 
+gint qq_putime(guint8 *buf, time_t *t)
+{
+	guint32 dw, dw_porter;
+	memcpy(&dw, t, sizeof(dw));
+    dw_porter = g_htonl(dw);
+#ifdef PARSER_DEBUG
+	purple_debug_info("QQ", "[DBG][put32] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter);
+#endif
+    memcpy(buf, &dw_porter, sizeof(dw_porter));
+    return sizeof(dw_porter);
+}
+
 gint qq_putIP(guint8* buf, struct in_addr *ip)
 {
     memcpy(buf, ip, sizeof(struct in_addr));
--- a/libpurple/protocols/qq/packet_parse.h	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.h	Wed Oct 22 14:43:46 2008 +0000
@@ -54,6 +54,7 @@
 gint qq_put16(guint8 *buf, guint16 w);
 gint qq_put32(guint8 *buf, guint32 dw);
 gint qq_putIP(guint8* buf, struct in_addr *ip);
+gint qq_putime(guint8 *buf, time_t *t);
 gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen);
 
 /*
--- a/libpurple/protocols/qq/qq.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Wed Oct 22 14:43:46 2008 +0000
@@ -267,6 +267,9 @@
 	case QQ_BUDDY_ONLINE_INVISIBLE:
 		g_string_append(status, _("Invisible"));
 		break;
+	case QQ_BUDDY_ONLINE_BUSY:
+		g_string_append(status, _("Busy"));
+		break;
 	default:
 		g_string_printf(status, _("Unknown-%d"), q_bud->status);
 	}
@@ -405,6 +408,10 @@
 			"invisible", _("Invisible"), FALSE, TRUE, FALSE);
 	types = g_list_append(types, status);
 
+	status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
+			"busy", _("Busy"), TRUE, TRUE, FALSE);
+	types = g_list_append(types, status);
+
 	status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
 			"offline", _("Offline"), FALSE, TRUE, FALSE);
 	types = g_list_append(types, status);
@@ -898,7 +905,9 @@
 	GList *server_list = NULL;
 	GList *server_kv_list = NULL;
 	GList *it;
+//#ifdef DEBUG
 	GList *version_kv_list = NULL;
+//#endif
 
 	server_list = server_list_build('A');
 
@@ -927,7 +936,7 @@
 	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
+//#ifdef DEBUG
 	kvp = g_new0(PurpleKeyValuePair, 1);
 	kvp->key = g_strdup(_("QQ2005"));
 	kvp->value = g_strdup("qq2005");
@@ -945,7 +954,7 @@
 
 	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
+//#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_base.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:43:46 2008 +0000
@@ -460,11 +460,10 @@
 	 * with this command, server return the same result including
 	 * the amount of online QQ users, my ip and port */
 	bytes += qq_put32(raw_data + bytes, qd->uid);
-
-	qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, 4);
+	qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
 }
 
-/* parse the return of keep-alive packet, it includes some system information */
+/* parse the return ofqq_process_keep_alive keep-alive packet, it includes some system information */
 gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
@@ -497,6 +496,107 @@
 	return TRUE;
 }
 
+void qq_request_keep_alive_2007(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 raw_data[32] = {0};
+	gint bytes= 0;
+	gchar *uid_str;
+
+	qd = (qq_data *) gc->proto_data;
+
+	/* In fact, we can send whatever we like to server
+	 * with this command, server return the same result including
+	 * the amount of online QQ users, my ip and port */
+	uid_str = g_strdup_printf("%u", qd->uid);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str));
+	qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
+
+	g_free(uid_str);
+}
+
+gboolean qq_process_keep_alive_2007(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+	qq_data *qd;
+	gint bytes= 0;
+	guint8 ret;
+
+	g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
+
+	qd = (qq_data *) gc->proto_data;
+
+	/* qq_show_packet("Keep alive reply packet", data, len); */
+
+	bytes = 0;
+	bytes += qq_get8(&ret, data + bytes);
+	bytes += qq_get32(&qd->online_total, data + bytes);
+	if(0 == qd->online_total) {
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Keep alive error"));
+	}
+
+	bytes += qq_getIP(&qd->my_ip, data + bytes);
+	bytes += qq_get16(&qd->my_port, data + bytes);
+	return TRUE;
+}
+
+void qq_request_keep_alive_2008(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 raw_data[16] = {0};
+	gint bytes= 0;
+
+	qd = (qq_data *) gc->proto_data;
+
+	/* In fact, we can send whatever we like to server
+	 * with this command, server return the same result including
+	 * the amount of online QQ users, my ip and port */
+	bytes += qq_put32(raw_data + bytes, qd->uid);
+	bytes += qq_putime(raw_data + bytes, &qd->login_time);
+	qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
+}
+
+gboolean qq_process_keep_alive_2008(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+	qq_data *qd;
+	gint bytes= 0;
+	guint8 ret;
+	time_t server_time;
+	struct tm *tm_local;
+
+	g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
+
+	qd = (qq_data *) gc->proto_data;
+
+	/* qq_show_packet("Keep alive reply packet", data, len); */
+
+	bytes = 0;
+	bytes += qq_get8(&ret, data + bytes);
+	bytes += qq_get32(&qd->online_total, data + bytes);
+	if(0 == qd->online_total) {
+		purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Keep alive error"));
+	}
+
+	bytes += qq_getIP(&qd->my_ip, data + bytes);
+	bytes += qq_get16(&qd->my_port, data + bytes);
+	/* skip 2 byytes, 0x(00 3c) */
+	bytes += 2;
+	bytes += qq_getime(&server_time, data + bytes);
+	/* skip 5 bytes, all are 0 */
+
+	purple_debug_info("QQ", "keep alive, %s:%d\n",
+		inet_ntoa(qd->my_ip), qd->my_port);
+
+	tm_local = localtime(&server_time);
+	purple_debug_info("QQ", "Server 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);
+	return TRUE;
+}
+
 /* For QQ2007/2008 */
 void qq_request_get_server(PurpleConnection *gc)
 {
@@ -681,8 +781,8 @@
 	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);
+	bytes += qq_put16(raw_data + bytes, qd->ld.token_ex_len); 	/* login token ex */
+	bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
 
 	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
 
@@ -811,7 +911,9 @@
 
 	bytes += qq_get16(&(qd->ld.token_ex_len), data + bytes);
 	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);
+	bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len, data + bytes);
+	qq_show_packet("Get token ex", qd->ld.token_ex, qd->ld.token_ex_len);
+	
 	if(reply != 1)
 	{
 		purple_debug_info("QQ", "Captcha verified, result %d\n", reply);
@@ -828,9 +930,12 @@
 	bytes += qq_get8(&qd->captcha.next_index, data + bytes);
 
 	bytes += qq_get16(&qd->captcha.token_len, data + bytes);
-	qd->captcha.token = g_new0(guint8, qd->captcha.token_len);
+	qd->captcha.token = g_realloc(qd->captcha.token, qd->captcha.token_len);
 	bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, data + bytes);
+	qq_show_packet("Get captcha token", qd->captcha.token, qd->captcha.token_len);
 
+	purple_debug_info("QQ", "Request next captcha %d, new %d, total %d\n", 
+			qd->captcha.next_index, captcha_len, qd->captcha.data_len);
 	if(qd->captcha.next_index > 0)
 	{
 		return QQ_LOGIN_REPLY_NEXT_TOKEN_EX;
@@ -894,7 +999,7 @@
 	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(qd->ld.token_ex != NULL && qd->ld.token_ex_len > 0);
 
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
 	memset(raw_data, 0, MAX_PACKET_SIZE - 16);
@@ -913,7 +1018,7 @@
 	bytes = 0;
 	bytes += qq_putdata(raw_data + bytes, header, sizeof(header));
 	/* token get from qq_request_token_ex */
-	bytes += qq_put8(raw_data + bytes, qd->ld.token_ex_len);
+	bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
 	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);
@@ -923,7 +1028,7 @@
 	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_put8(raw_data + 1, bytes - 2);
 	
@@ -933,7 +1038,7 @@
 	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);
 
@@ -963,7 +1068,7 @@
 	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); */
+	qq_show_packet("Check password reply", data, data_len);
 
 	bytes = 0;
 	bytes += qq_get16(&unknow_token_len, data + bytes);	/* maybe total length */
@@ -1097,11 +1202,12 @@
 	memset(raw_data + bytes, 0, 10);
 	bytes += 10;
 	/* redirect data, 15 bytes */
+	qq_show_packet("Redirect", qd->redirect, qd->redirect_len);
 	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->ld.token_ex_len);
+	bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
 	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));
@@ -1130,8 +1236,10 @@
 	qq_data *qd;
 	gint bytes;
 	guint8 ret;
+	guint32 uid;
 	gchar *error;
-	guint32 uid;
+	gchar *msg;
+	gchar *msg_utf8;
 
 	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
 
@@ -1140,15 +1248,34 @@
 	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 );
+		msg = g_strndup((gchar *)data + bytes, data_len - bytes);
+		msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+
+		switch (ret) {
+			case 0x05:
+				error = g_strdup_printf(
+						_("Server is busy now, Please try later\n%s"),
+						ret, msg_utf8);
+				break;
+			case 0x0A:
+				/* 0a 2d 9a 4b 9a 01 01 00 00 00 05 00 00 00 00 79 0e 5f fd */
+			default:
+				error = g_strdup_printf(
+						_("Unknow reply code when login (0x%02X):\n%s"),
+						ret, msg_utf8);
+				break;
+		}
+
+		purple_debug_error("QQ", "%s\n", error);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 				error);
+
+		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, error);
+
 		g_free(error);
+		g_free(msg_utf8);
+		g_free(msg);
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
@@ -1262,7 +1389,7 @@
 	/* 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->ld.token_ex_len);
+	bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
 	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));
@@ -1301,8 +1428,10 @@
 	qq_data *qd;
 	gint bytes;
 	guint8 ret;
+	guint32 uid;
 	gchar *error;
-	guint32 uid;
+	gchar *msg;
+	gchar *msg_utf8;
 
 	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
 
@@ -1311,15 +1440,32 @@
 	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 );
+		msg = g_strndup((gchar *)data + bytes, data_len - bytes);
+		msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+
+		switch (ret) {
+			case 0x05:
+				error = g_strdup_printf(
+						_("Server is busy now, Please try later\n%s"),
+						ret, msg_utf8);
+				break;
+			default:
+				error = g_strdup_printf(
+						_("Unknow reply code when login (0x%02X):\n%s"),
+						ret, msg_utf8);
+				break;
+		}
+
+		purple_debug_error("QQ", "%s\n", error);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 				error);
+
+		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, error);
+
 		g_free(error);
+		g_free(msg_utf8);
+		g_free(msg);
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
--- a/libpurple/protocols/qq/qq_base.h	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.h	Wed Oct 22 14:43:46 2008 +0000
@@ -52,6 +52,12 @@
 void qq_request_keep_alive(PurpleConnection *gc);
 gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc);
 
+void qq_request_keep_alive_2007(PurpleConnection *gc);
+gboolean qq_process_keep_alive_2007(guint8 *data, gint data_len, PurpleConnection *gc);
+
+void qq_request_keep_alive_2008(PurpleConnection *gc);
+gboolean qq_process_keep_alive_2008(guint8 *data, gint data_len, PurpleConnection *gc);
+
 /* for QQ2007/2008 */
 void qq_request_get_server(PurpleConnection *gc);
 guint16 qq_process_get_server(PurpleConnection *gc, guint8 *rcved, gint rcved_len);
--- a/libpurple/protocols/qq/qq_network.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Wed Oct 22 14:43:46 2008 +0000
@@ -667,7 +667,13 @@
 	qd->itv_count.keep_alive--;
 	if (qd->itv_count.keep_alive <= 0) {
 		qd->itv_count.keep_alive = qd->itv_config.keep_alive;
-		qq_request_keep_alive(gc);
+		if (qd->client_version >= 2008) {
+			qq_request_keep_alive_2008(gc);
+		} else if (qd->client_version >= 2007) {
+			qq_request_keep_alive_2007(gc);
+		} else {
+			qq_request_keep_alive(gc);
+		}
 		return TRUE;
 	}
 
@@ -691,7 +697,9 @@
 	const gchar *passwd;
 	guint8 *dest;
 	int dest_len = QQ_KEY_LENGTH;
-
+#ifndef DEBUG
+	int bytes;
+#endif
 	/* _qq_show_socket("Got login socket", source); */
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -771,7 +779,7 @@
 
 	set_all_keys( gc );
 
-	if (qd->client_version > 2005) {
+	if (qd->client_version >= 2007) {
 		purple_connection_update_progress(gc, _("Get server ..."), 2, QQ_CONNECT_STEPS);
 		qq_request_get_server(gc);
 		return;
--- a/libpurple/protocols/qq/qq_network.h	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.h	Wed Oct 22 14:43:46 2008 +0000
@@ -30,7 +30,7 @@
 
 #include "qq.h"
 
-#define QQ_CONNECT_STEPS    3	/* steps in connection */
+#define QQ_CONNECT_STEPS    4	/* steps in connection */
 
 gboolean qq_connect_later(gpointer data);
 void qq_disconnect(PurpleConnection *gc);
--- a/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:43:46 2008 +0000
@@ -589,7 +589,7 @@
 	switch (cmd) {
 		case QQ_CMD_TOKEN:
 			if (qq_process_token(gc, rcved, rcved_len) == QQ_LOGIN_REPLY_OK) {
-				if (qd->client_version > 2005) {
+				if (qd->client_version >= 2007) {
 					qq_request_token_ex(gc);
 				} else {
 					qq_request_login(gc);
@@ -615,7 +615,7 @@
 			break;
 		case QQ_CMD_LOGIN:
 		default:
-			if (qd->client_version > 2005) {
+			if (qd->client_version >= 2007) {
 				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_twice_md5\n");
@@ -696,6 +696,7 @@
 				return  ret_8;
 			}
 
+			purple_connection_update_progress(gc, _("Logined"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
 			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 */
@@ -776,7 +777,13 @@
 			qq_process_send_im_reply(data, data_len, gc);
 			break;
 		case QQ_CMD_KEEP_ALIVE:
-			qq_process_keep_alive(data, data_len, gc);
+			if (qd->client_version >= 2008) {
+				qq_process_keep_alive_2008(data, data_len, gc);
+			} else if (qd->client_version >= 2007) {
+				qq_process_keep_alive_2007(data, data_len, gc);
+			} else {
+				qq_process_keep_alive(data, data_len, gc);
+			}
 			break;
 		case QQ_CMD_GET_BUDDIES_ONLINE:
 			ret_8 = qq_process_get_buddies_online_reply(data, data_len, gc);
--- a/libpurple/protocols/qq/send_file.c	Wed Oct 22 14:42:23 2008 +0000
+++ b/libpurple/protocols/qq/send_file.c	Wed Oct 22 14:43:46 2008 +0000
@@ -121,11 +121,8 @@
 		sin.sin_port = g_htons(info->remote_minor_port);
 		sin.sin_addr.s_addr = g_htonl(info->remote_real_ip);
 	}
-	purple_debug_info("QQ", "sending to channel: %d.%d.%d.%d:%d\n",
-			(int)sin.sin_addr.s_addr & 0xff,
-			(int)(sin.sin_addr.s_addr >> 8) & 0xff,
-			(int)(sin.sin_addr.s_addr >> 16) & 0xff,
-			(int)sin.sin_addr.s_addr >> 24,
+	purple_debug_info("QQ", "sending to channel: %s:%d\n",
+			inet_ntoa(sin.sin_addr),
 			(int)g_ntohs(sin.sin_port)
 		  );
 	return sendto(info->sender_fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(sin));