changeset 24074:c2253c485728

2008.10.04 - ccpaging <ccpagint(at)gmail.com> * Update protocol for 2007 * Code cleanup
author SHiNE CsyFeK <csyfek@gmail.com>
date Wed, 22 Oct 2008 14:47:39 +0000
parents df699d739b8f
children a95c7e71064c
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/buddy_info.c libpurple/protocols/qq/group_info.c libpurple/protocols/qq/im.c libpurple/protocols/qq/im.h libpurple/protocols/qq/packet_parse.c libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq_base.c libpurple/protocols/qq/qq_process.c
diffstat 9 files changed, 526 insertions(+), 435 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:47:39 2008 +0000
@@ -1,3 +1,7 @@
+2008.10.04 - ccpaging <ccpagint(at)gmail.com>
+	* Update protocol for 2007
+	* Code cleanup
+
 2008.10.03 - ccpaging <ccpaging(at)gmail.com>
 	* 2007 protocol:
 		1. fixed 'get room info'
--- a/libpurple/protocols/qq/buddy_info.c	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Wed Oct 22 14:47:39 2008 +0000
@@ -787,7 +787,7 @@
 	
 	/* extend bytes in qq2007*/
 	bytes += 4;	/* skip 8 bytes */
-	qq_show_packet("Buddies level", data + bytes, data_len - bytes);
+	/* qq_show_packet("Buddies level", data + bytes, data_len - bytes); */
 	
 	do {
 		bytes += qq_get16(&str_len, data + bytes);
--- a/libpurple/protocols/qq/group_info.c	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Wed Oct 22 14:47:39 2008 +0000
@@ -191,9 +191,10 @@
 	purple_debug_info("QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n",
 			group->type8, group->creator_uid, group->category, max_members);
 
-	/* skip 7 bytes unknow in qq2007 0x(00 00 01 00 00 00 fc)*/
-	bytes += 7;
-	
+	if (qd->client_version >= 2007) {
+		/* skip 7 bytes unknow in qq2007 0x(00 00 01 00 00 00 fc)*/
+		bytes += 7;
+	}
 	/* qq_show_packet("Room Info", data + bytes, data_len - bytes); */
 	/* strlen + <str content> */
 	bytes += convert_as_pascal_string(data + bytes, &(group->title_utf8), QQ_CHARSET_DEFAULT);
@@ -314,9 +315,7 @@
 
 	g_return_if_fail(data != NULL && len > 0);
 
-#if 0
-	qq_show_packet("qq_process_room_cmd_get_buddies", data, len);
-#endif
+	/* qq_show_packet("qq_process_room_cmd_get_buddies", data, len); */
 
 	bytes = 0;
 	bytes += qq_get32(&id, data + bytes);
--- a/libpurple/protocols/qq/im.c	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Wed Oct 22 14:47:39 2008 +0000
@@ -35,7 +35,6 @@
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
-#include "group_im.h"
 #include "qq_define.h"
 #include "im.h"
 #include "packet_parse.h"
@@ -43,7 +42,6 @@
 #include "send_file.h"
 #include "utils.h"
 
-#define QQ_SEND_IM_REPLY_OK       0x00
 #define DEFAULT_FONT_NAME_LEN 	  4
 
 enum
@@ -64,52 +62,15 @@
 	QQ_NORMAL_IM_FILE_EX_NOTIFY_IP = 0x87
 };
 
-enum {
-	QQ_RECV_SYS_IM_KICK_OUT = 0x01
-};
-
-typedef struct _qq_recv_im_header qq_recv_im_header;
-typedef struct _qq_recv_normal_im_text qq_recv_normal_im_text;
-typedef struct _qq_recv_normal_im_common qq_recv_normal_im_common;
-typedef struct _qq_recv_normal_im_unprocessed qq_recv_normal_im_unprocessed;
-
-struct _qq_recv_normal_im_common {
-	/* this is the common part of normal_text */
-	guint16 sender_ver;
-	guint32 sender_uid;
-	guint32 receiver_uid;
-	guint8 session_md5[QQ_KEY_LENGTH];
-	guint16 normal_im_type;
-};
+typedef struct _qq_im_header qq_im_header;
+typedef struct _qq_recv_extended_im_text qq_recv_extended_im_text;
 
-struct _qq_recv_normal_im_text {
-	qq_recv_normal_im_common *common;
-	/* now comes the part for text only */
-	guint16 msg_seq;
-	guint32 send_time;
-	guint16 sender_icon;
-	guint8 unknown2[3];
-	guint8 is_there_font_attr;
-	guint8 unknown3[4];
-	guint8 msg_type;
-	gchar *msg;		/* no fixed length, ends with 0x00 */
-	guint8 *font_attr;
-	gint font_attr_len;
-};
-
-struct _qq_recv_normal_im_unprocessed {
-	qq_recv_normal_im_common *common;
-	/* now comes the part of unprocessed */
-	guint8 *unknown;	/* no fixed length */
-	gint length;
-};
-
-struct _qq_recv_im_header {
-	guint32 sender_uid;
-	guint32 receiver_uid;
-	guint32 server_im_seq;
-	struct in_addr sender_ip;
-	guint16 sender_port;
+struct _qq_im_header {
+	/* this is the common part of normal_text */
+	guint16 version_from;
+	guint32 uid_from;
+	guint32 uid_to;
+	guint8 session_md5[QQ_KEY_LENGTH];
 	guint16 im_type;
 };
 
@@ -182,118 +143,22 @@
 	return (guint8 *) send_im_tail;
 }
 
-static const gchar *qq_get_recv_im_type_str(gint type)
-{
-	switch (type) {
-		case QQ_RECV_IM_TO_BUDDY:
-			return "QQ_RECV_IM_TO_BUDDY";
-		case QQ_RECV_IM_TO_UNKNOWN:
-			return "QQ_RECV_IM_TO_UNKNOWN";
-		case QQ_RECV_IM_UNKNOWN_QUN_IM:
-			return "QQ_RECV_IM_UNKNOWN_QUN_IM";
-		case QQ_RECV_IM_ADD_TO_QUN:
-			return "QQ_RECV_IM_ADD_TO_QUN";
-		case QQ_RECV_IM_DEL_FROM_QUN:
-			return "QQ_RECV_IM_DEL_FROM_QUN";
-		case QQ_RECV_IM_APPLY_ADD_TO_QUN:
-			return "QQ_RECV_IM_APPLY_ADD_TO_QUN";
-		case QQ_RECV_IM_CREATE_QUN:
-			return "QQ_RECV_IM_CREATE_QUN";
-		case QQ_RECV_IM_SYS_NOTIFICATION:
-			return "QQ_RECV_IM_SYS_NOTIFICATION";
-		case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
-			return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN";
-		case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
-			return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN";
-		case QQ_RECV_IM_TEMP_QUN_IM:
-			return "QQ_RECV_IM_TEMP_QUN_IM";
-		case QQ_RECV_IM_QUN_IM:
-			return "QQ_RECV_IM_QUN_IM";
-		case QQ_RECV_IM_NEWS:
-			return "QQ_RECV_IM_NEWS";
-		case QQ_RECV_IM_FROM_BUDDY_2006:
-			return "QQ_RECV_IM_FROM_BUDDY_2006";
-		case QQ_RECV_IM_FROM_UNKNOWN_2006:
-			return "QQ_RECV_IM_FROM_UNKNOWN_2006";
-		default:
-			return "QQ_RECV_IM_UNKNOWN";
-	}
-}
-
 /* read the common parts of the normal_im,
  * returns the bytes read if succeed, or -1 if there is any error */
-static gint normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common)
+static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len)
 {
 	gint bytes;
-	g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1);
+	g_return_val_if_fail(data != NULL && len > 0, -1);
 
 	bytes = 0;
-	/* now push data into common header */
-	bytes += qq_get16(&(common->sender_ver), data + bytes);
-	bytes += qq_get32(&(common->sender_uid), data + bytes);
-	bytes += qq_get32(&(common->receiver_uid), data + bytes);
-	bytes += qq_getdata(common->session_md5, QQ_KEY_LENGTH, data + bytes);
-	bytes += qq_get16(&(common->normal_im_type), data + bytes);
-
-	if (bytes != 28) {	/* read common place fail */
-		purple_debug_error("QQ", "Expect 28 bytes, read %d bytes\n", bytes);
-		return -1;
-	}
-
+	bytes += qq_get16(&(im_header->version_from), data + bytes);
+	bytes += qq_get32(&(im_header->uid_from), data + bytes);
+	bytes += qq_get32(&(im_header->uid_to), data + bytes);
+	bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes);
+	bytes += qq_get16(&(im_header->im_type), data + bytes);
 	return bytes;
 }
 
-static void process_recv_news(guint8 *data, gint data_len, PurpleConnection *gc)
-{
-	qq_data *qd = (qq_data *) gc->proto_data;
-	gint bytes;
-	guint8 *temp;
-	guint8 temp_len;
-	gchar *title, *brief, *url;
-	gchar *title_utf8;
-	gchar *content, *content_utf8;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-#if 0
-	qq_show_packet("Rcv news", data, data_len);
-#endif
-
-	temp = g_newa(guint8, data_len);
-	bytes = 4;	/* ignore unknown 4 bytes */
-
-	bytes += qq_get8(&temp_len, data + bytes);
-	g_return_if_fail(bytes + temp_len <= data_len);
-	bytes += qq_getdata(temp, temp_len, data+bytes);
-	title = g_strndup((gchar *)temp, temp_len);
-
-	bytes += qq_get8(&temp_len, data + bytes);
-	g_return_if_fail(bytes + temp_len <= data_len);
-	bytes += qq_getdata(temp, temp_len, data+bytes);
-	brief = g_strndup((gchar *)temp, temp_len);
-
-	bytes += qq_get8(&temp_len, data + bytes);
-	g_return_if_fail(bytes + temp_len <= data_len);
-	bytes += qq_getdata(temp, temp_len, data+bytes);
-	url = g_strndup((gchar *)temp, temp_len);
-
-	title_utf8 = qq_to_utf8(title, QQ_CHARSET_DEFAULT);
-	content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url);
-	content_utf8 = qq_to_utf8(content, QQ_CHARSET_DEFAULT);
-
-	if (qd->is_show_news) {
-		qq_got_attention(gc, content_utf8);
-	} else {
-		purple_debug_info("QQ", "QQ Server news:\n%s\n%s", title_utf8, content_utf8);
-	}
-	g_free(title);
-	g_free(title_utf8);
-	g_free(brief);
-	g_free(url);
-	g_free(content);
-	g_free(content_utf8);
-}
-
 void qq_got_attention(PurpleConnection *gc, const gchar *msg)
 {
 	qq_data *qd;
@@ -316,215 +181,316 @@
 }
 
 /* process received normal text IM */
-static void process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
+static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
 {
 	guint16 purple_msg_type;
 	gchar *name;
 	gchar *msg_with_purple_smiley;
 	gchar *msg_utf8_encoded;
 	qq_data *qd;
-	qq_recv_normal_im_text *im_text;
 	gint bytes = 0;
 	PurpleBuddy *b;
 	qq_buddy *qq_b;
 
-	g_return_if_fail(common != NULL);
-	qd = (qq_data *) gc->proto_data;
+	struct {
+		/* now comes the part for text only */
+		guint16 msg_seq;
+		guint32 send_time;
+		guint16 sender_icon;
+		guint8 unknown2[3];
+		guint8 is_there_font_attr;
+		guint8 unknown3[4];
+		guint8 msg_type;
+		gchar *msg;		/* no fixed length, ends with 0x00 */
+		guint8 *font_attr;
+		gint font_attr_len;
+	} im_text;
 
-	/* now it is QQ_NORMAL_IM_TEXT */
-	/*
-	   if (*cursor >= (data + len - 1)) {
-	   purple_debug_warning("QQ", "Received normal IM text is empty\n");
-	   return;
-	   } else
-	   */
-	im_text = g_newa(qq_recv_normal_im_text, 1);
-
-	im_text->common = common;
-
+	g_return_if_fail (data != NULL && len > 0);
+	g_return_if_fail(im_header != NULL);
+	
+	qd = (qq_data *) gc->proto_data;
+	memset(&im_text, 0, sizeof(im_text));
+	
 	/* push data into im_text */
-	bytes += qq_get16(&(im_text->msg_seq), data + bytes);
-	bytes += qq_get32(&(im_text->send_time), data + bytes);
-	bytes += qq_get16(&(im_text->sender_icon), data + bytes);
-	bytes += qq_getdata((guint8 *) & (im_text->unknown2), 3, data + bytes);
-	bytes += qq_get8(&(im_text->is_there_font_attr), data + bytes);
+	bytes += qq_get16(&(im_text.msg_seq), data + bytes);
+	bytes += qq_get32(&(im_text.send_time), data + bytes);
+	bytes += qq_get16(&(im_text.sender_icon), data + bytes);
+	bytes += qq_getdata((guint8 *) & (im_text.unknown2), 3, data + bytes);
+	bytes += qq_get8(&(im_text.is_there_font_attr), data + bytes);
 	/**
 	 * from lumaqq	for unknown3
 	 *	totalFragments = buf.get() & 255;
 	 *	fragmentSequence = buf.get() & 255;
 	 *	messageId = buf.getChar();
 	 */
-	bytes += qq_getdata((guint8 *) & (im_text->unknown3), 4, data + bytes);
-	bytes += qq_get8(&(im_text->msg_type), data + bytes);
+	bytes += qq_getdata((guint8 *) & (im_text.unknown3), 4, data + bytes);
+	bytes += qq_get8(&(im_text.msg_type), data + bytes);
 
 	/* we need to check if this is auto-reply
 	 * QQ2003iii build 0304, returns the msg without font_attr
 	 * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */
-	if (im_text->msg_type == QQ_IM_AUTO_REPLY) {
-		im_text->is_there_font_attr = 0x00;	/* indeed there is no this flag */
-		im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes);
+	if (im_text.msg_type == QQ_IM_AUTO_REPLY) {
+		im_text.is_there_font_attr = 0x00;	/* indeed there is no this flag */
+		im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes);
 	} else {		/* it is normal mesasge */
-		if (im_text->is_there_font_attr) {
-			im_text->msg = g_strdup((gchar *)(data + bytes));
-			bytes += strlen(im_text->msg) + 1; /* length decided by strlen! will it cause a crash? */
-			im_text->font_attr_len = len - bytes;
-			im_text->font_attr = g_memdup(data + bytes, im_text->font_attr_len);
-		} else		/* not im_text->is_there_font_attr */
-			im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes);
-	}			/* if im_text->msg_type */
+		if (im_text.is_there_font_attr) {
+			im_text.msg = g_strdup((gchar *)(data + bytes));
+			bytes += strlen(im_text.msg) + 1; /* length decided by strlen! will it cause a crash? */
+			im_text.font_attr_len = len - bytes;
+			im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len);
+		} else		/* not im_text.is_there_font_attr */
+			im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes);
+	}			/* if im_text.msg_type */
 
-	name = uid_to_purple_name(common->sender_uid);
+	name = uid_to_purple_name(im_header->uid_from);
 	b = purple_find_buddy(gc->account, name);
 	if (b == NULL) {
-		qq_create_buddy(gc, common->sender_uid, FALSE, TRUE);
+		qq_create_buddy(gc, im_header->uid_from, FALSE, TRUE);
 		b = purple_find_buddy(gc->account, name);
 	}
 	qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 	if (qq_b != NULL) {
-		qq_b->client_tag = common->sender_ver;
+		qq_b->client_tag = im_header->version_from;
 	}
 
-	purple_msg_type = (im_text->msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0;
+	purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0;
 
-	msg_with_purple_smiley = qq_smiley_to_purple(im_text->msg);
-	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_smiley_to_purple(im_text.msg);
+	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, 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,
+	/* send encoded to purple, note that we use im_text.send_time,
 	 * not the time we receive the message
 	 * as it may have been delayed when I am not online. */
-	serv_got_im(gc, name, msg_utf8_encoded, purple_msg_type, (time_t) im_text->send_time);
+	serv_got_im(gc, name, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time);
 
 	g_free(msg_utf8_encoded);
 	g_free(msg_with_purple_smiley);
 	g_free(name);
-	g_free(im_text->msg);
-	if (im_text->is_there_font_attr)
-		g_free(im_text->font_attr);
+	g_free(im_text.msg);
+	if (im_text.font_attr)	g_free(im_text.font_attr);
+}
+
+/* process received extended (2007) text IM */
+static void process_extend_im_text(
+		PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
+{
+	guint16 purple_msg_type;
+	gchar *name;
+	gchar *msg_with_purple_smiley;
+	gchar *msg_utf8_encoded;
+	qq_data *qd;
+	gint bytes, text_len;
+
+	struct {
+		/* now comes the part for text only */
+		guint16 sessionId;
+		guint32 send_time;
+		guint16 senderHead;
+		guint32 flag;
+		guint8 unknown2[8];
+		guint8 fragmentCount;
+		guint8 fragmentIndex;
+		guint16 messageId;
+		guint8 replyType;
+		gchar *msg;		/* no fixed length, ends with 0x00 */
+		guint8 fromMobileQQ;
+	
+		guint8 is_there_font_attr;
+		guint8 *font_attr;
+		gint8 font_attr_len;
+	} im_text;
+
+	g_return_if_fail (data != NULL && len > 0);
+	g_return_if_fail(im_header != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+	memset(&im_text, 0, sizeof(im_text));
+
+	/* push data into im_text */
+	bytes = 0;
+	bytes += qq_get16(&(im_text.sessionId), data + bytes);
+	bytes += qq_get32(&(im_text.send_time), data + bytes);
+	bytes += qq_get16(&(im_text.senderHead), data + bytes);
+	bytes += qq_get32(&(im_text.flag), data + bytes);
+	
+	bytes += qq_getdata(im_text.unknown2, 8, data + bytes);
+	bytes += qq_get8(&(im_text.fragmentCount), data + bytes);
+	bytes += qq_get8(&(im_text.fragmentIndex), data + bytes);
+		
+	bytes += qq_get16(&(im_text.messageId), data + bytes);
+	bytes += qq_get8(&(im_text.replyType), data + bytes);
+	
+	im_text.font_attr_len = data[len-1] & 0xff;
+	
+	text_len = len - bytes - im_text.font_attr_len;
+	im_text.msg = g_strndup((gchar *)(data + bytes), text_len);
+	bytes += text_len;
+	if(im_text.font_attr_len >= 0)
+		im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len);
+	else
+	{
+		purple_debug_error("QQ", "Failed to get IM's font attribute len %d\n", 
+			im_text.font_attr_len);
+		return;
+	}
+
+	if(im_text.fragmentCount == 0)
+		im_text.fragmentCount = 1;
+		
+	// Filter tail space
+	if(im_text.fragmentIndex == im_text.fragmentCount -1)
+	{
+		gint real_len = text_len;
+		while(real_len > 0 && im_text.msg[real_len - 1] == 0x20)
+			real_len --;
+		
+		text_len = real_len;
+		// Null string instaed of space
+		im_text.msg[text_len] = 0;
+	}
+	
+	name = uid_to_purple_name(im_header->uid_from);
+	if (purple_find_buddy(gc->account, name) == NULL)
+		qq_create_buddy(gc, im_header->uid_from, FALSE, TRUE);
+
+	purple_msg_type = 0;
+
+	msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg);
+	msg_utf8_encoded = im_text.font_attr ?
+	    qq_encode_to_purple(im_text.font_attr,
+			      im_text.font_attr_len,
+			      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
+	 * as it may have been delayed when I am not online. */
+	serv_got_im(gc, name, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time);
+
+	g_free(msg_utf8_encoded);
+	g_free(msg_with_purple_smiley);
+	g_free(name);
+	g_free(im_text.msg);
+	if (im_text.font_attr) g_free(im_text.font_attr);
 }
 
 /* it is a normal IM, maybe text or video request */
-static void process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc)
+void qq_process_im(PurpleConnection *gc, guint8 *data, gint len)
 {
 	gint bytes = 0;
-	qq_recv_normal_im_common *common;
-	qq_recv_normal_im_unprocessed *im_unprocessed;
+	qq_im_header im_header;
 
-	g_return_if_fail (data != NULL && len != 0);
+	g_return_if_fail (data != NULL && len > 0);
 
-	common = g_newa (qq_recv_normal_im_common, 1);
-
-	bytes = normal_im_common_read(data, len, common);
+	bytes = get_im_header(&im_header, data, len);
 	if (bytes < 0) {
-		purple_debug_error("QQ", "Fail read the common part of normal IM\n");
+		purple_debug_error("QQ", "Fail read im header, len %d\n", len);
+		qq_show_packet ("IM Header", data, len);
 		return;
 	}
+	purple_debug_info("QQ",
+			"Got IM to %d, type: %02X from: %d ver: %s (%04X)\n",
+			im_header.uid_to, im_header.im_type, im_header.uid_from,
+			qq_get_ver_desc(im_header.version_from), im_header.version_from);
 
-	switch (common->normal_im_type) {
+	switch (im_header.im_type) {
 		case QQ_NORMAL_IM_TEXT:
-			purple_debug_info("QQ",
-					"Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n",
-					common->sender_uid, common->receiver_uid,
-					qq_get_ver_desc (common->sender_ver), common->sender_ver);
 			if (bytes >= len - 1) {
 				purple_debug_warning("QQ", "Received normal IM text is empty\n");
 				return;
 			}
-			process_recv_normal_im_text(data + bytes, len - bytes, common, gc);
+			process_im_text(gc, data + bytes, len - bytes, &im_header);
 			break;
 		case QQ_NORMAL_IM_FILE_REJECT_UDP:
-			qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc);
+			qq_process_recv_file_reject(data + bytes, len - bytes, im_header.uid_from, gc);
 			break;
 		case QQ_NORMAL_IM_FILE_APPROVE_UDP:
-			qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc);
+			qq_process_recv_file_accept(data + bytes, len - bytes, im_header.uid_from, gc);
 			break;
 		case QQ_NORMAL_IM_FILE_REQUEST_UDP:
-			qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc);
+			qq_process_recv_file_request(data + bytes, len - bytes, im_header.uid_from, gc);
 			break;
 		case QQ_NORMAL_IM_FILE_CANCEL:
-			qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc);
+			qq_process_recv_file_cancel(data + bytes, len - bytes, im_header.uid_from, gc);
 			break;
 		case QQ_NORMAL_IM_FILE_NOTIFY:
-			qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc);
+			qq_process_recv_file_notify(data + bytes, len - bytes, im_header.uid_from, gc);
 			break;
 		case QQ_NORMAL_IM_FILE_REQUEST_TCP:
 			/* Check ReceivedFileIM::parseContents in eva*/
 			/* some client use this function for detect invisable buddy*/
-			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n");
-			qq_show_packet ("Not support", data, len);
-			break;
 		case QQ_NORMAL_IM_FILE_APPROVE_TCP:
-			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_APPROVE_TCP\n");
-			qq_show_packet ("Not support", data, len);
-			break;
 		case QQ_NORMAL_IM_FILE_REJECT_TCP:
-			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REJECT_TCP\n");
-			qq_show_packet ("Not support", data, len);
-			break;
 		case QQ_NORMAL_IM_FILE_PASV:
-			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_PASV\n");
-			qq_show_packet ("Not support", data, len);
-			break;
 		case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP:
-			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n");
-			qq_show_packet ("QQ", data, len);
-			break;
 		case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT:
-			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT\n");
-			qq_show_packet ("QQ", data, len);
-			break;
 		case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL:
-			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL\n");
-			qq_show_packet ("Not support", data, len);
-			break;
 		case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP:
-			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_NOTIFY_IP\n");
 			qq_show_packet ("Not support", data, len);
 			break;
 		default:
-			im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1);
-			im_unprocessed->common = common;
-			im_unprocessed->unknown = data + bytes;
-			im_unprocessed->length = len - bytes;
 			/* a simple process here, maybe more later */
-			purple_debug_warning("QQ",
-					"Normal IM, unprocessed type [0x%04x], len %d\n",
-					common->normal_im_type, im_unprocessed->length);
-			qq_show_packet ("QQ", im_unprocessed->unknown, im_unprocessed->length);
+			qq_show_packet ("Unknow", data + bytes, len - bytes);
 			return;
 	}
 }
 
-/* process im from system administrator */
-static void process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc)
+/* it is a extended IM, maybe text or video request */
+void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len)
 {
-	gint len;
-	guint8 reply;
-	gchar **segments, *msg_utf8;
+	gint bytes;
+	qq_im_header im_header;
+
+	g_return_if_fail (data != NULL && len > 0);
 
-	g_return_if_fail(data != NULL && data_len != 0);
+	bytes = get_im_header(&im_header, data, len);
+	if (bytes < 0) {
+		purple_debug_error("QQ", "Fail read im header, len %d\n", len);
+		qq_show_packet ("IM Header", data, len);
+		return;
+	}
+	purple_debug_info("QQ",
+			"Got Extend IM to %d, type: %02X from: %d ver: %s (%04X)\n",
+			im_header.uid_to, im_header.im_type, im_header.uid_from,
+			qq_get_ver_desc(im_header.version_from), im_header.version_from);
 
-	len = data_len;
-
-	if (NULL == (segments = split_data(data, len, "\x2f", 2)))
-		return;
-
-	reply = strtol(segments[0], NULL, 10);
-	if (reply == QQ_RECV_SYS_IM_KICK_OUT)
-		purple_debug_warning("QQ", "We are kicked out by QQ server\n");
-	msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
-	purple_notify_warning(gc, NULL, _("System Message"), msg_utf8);
+	switch (im_header.im_type) {
+	case QQ_NORMAL_IM_TEXT:
+		process_extend_im_text(gc, data + bytes, len - bytes, &im_header);
+		break;
+	case QQ_NORMAL_IM_FILE_REJECT_UDP:
+		qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc);
+		break;
+	case QQ_NORMAL_IM_FILE_APPROVE_UDP:
+		qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc);
+		break;
+	case QQ_NORMAL_IM_FILE_REQUEST_UDP:
+		qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc);
+		break;
+	case QQ_NORMAL_IM_FILE_CANCEL:
+		qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc);
+		break;
+	case QQ_NORMAL_IM_FILE_NOTIFY:
+		qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc);
+		break;
+	default:
+		/* a simple process here, maybe more later */
+		qq_show_packet ("Unknow", data + bytes, len - bytes);
+		break;
+	}
 }
 
-/* send an IM to to_uid */
-void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type)
+/* send an IM to uid_to */
+void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type)
 {
 	qq_data *qd;
 	guint8 *raw_data, *send_im_tail;
-	guint16 normal_im_type;
+	guint16 im_type;
 	gint msg_len, raw_len, font_name_len, tail_len, bytes;
 	time_t now;
 	gchar *msg_filtered;
@@ -534,7 +500,7 @@
 	const gchar *start, *end, *last;
 
 	qd = (qq_data *) gc->proto_data;
-	normal_im_type = QQ_NORMAL_IM_TEXT;
+	im_type = QQ_NORMAL_IM_TEXT;
 
 	last = msg;
 	while (purple_markup_find_tag("font", last, &start, &end, &attribs)) {
@@ -591,17 +557,17 @@
 	/* 000-003: receiver uid */
 	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 004-007: sender uid */
-	bytes += qq_put32(raw_data + bytes, to_uid);
+	bytes += qq_put32(raw_data + bytes, uid_to);
 	/* 008-009: sender client version */
 	bytes += qq_put16(raw_data + bytes, qd->client_tag);
 	/* 010-013: receiver uid */
 	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
-	bytes += qq_put32(raw_data + bytes, to_uid);
+	bytes += qq_put32(raw_data + bytes, uid_to);
 	/* 018-033: md5 of (uid+session_key) */
 	bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
 	/* 034-035: message type */
-	bytes += qq_put16(raw_data + bytes, normal_im_type);
+	bytes += qq_put16(raw_data + bytes, im_type);
 	/* 036-037: sequence number */
 	bytes += qq_put16(raw_data + bytes, qd->send_seq);
 	/* 038-041: send time */
@@ -640,127 +606,5 @@
 	g_free(msg_filtered);
 }
 
-/* parse the reply to send_im */
-void qq_process_send_im_reply(guint8 *data, gint data_len, PurpleConnection *gc)
-{
-	qq_data *qd;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	qd = gc->proto_data;
-
-	if (data[0] != QQ_SEND_IM_REPLY_OK) {
-		purple_debug_warning("QQ", "Send IM fail\n");
-		purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
-	}	else {
-		purple_debug_info("QQ", "IM ACK OK\n");
-	}
-}
-
-/* I receive a message, mainly it is text msg,
- * but we need to proess other types (group etc) */
-void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
-{
-	qq_data *qd;
-	gint bytes;
-	qq_recv_im_header *im_header;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-
-	if (data_len < 16) {	/* we need to ack with the first 16 bytes */
-		purple_debug_error("QQ", "MSG is too short\n");
-		return;
-	} else {
-		/* when we receive a message,
-		 * we send an ACK which is the first 16 bytes of incoming packet */
-		qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16);
-	}
-
-	/* check len first */
-	if (data_len < 20) {	/* length of im_header */
-		purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len);
-		return;
-	}
-
-	bytes = 0;
-	im_header = g_newa(qq_recv_im_header, 1);
-	bytes += qq_get32(&(im_header->sender_uid), data + bytes);
-	bytes += qq_get32(&(im_header->receiver_uid), data + bytes);
-	bytes += qq_get32(&(im_header->server_im_seq), data + bytes);
-	/* if the message is delivered via server, it is server IP/port */
-	bytes += qq_getIP(&(im_header->sender_ip), data + bytes);
-	bytes += qq_get16(&(im_header->sender_port), data + bytes);
-	bytes += qq_get16(&(im_header->im_type), data + bytes);
-	/* im_header prepared */
-
-	if (im_header->receiver_uid != qd->uid) {	/* should not happen */
-		purple_debug_error("QQ", "MSG to [%d], NOT me\n", im_header->receiver_uid);
-		return;
-	}
 
-	/* check bytes */
-	if (bytes >= data_len - 1) {
-		purple_debug_warning("QQ", "Empty MSG\n");
-		return;
-	}
 
-	switch (im_header->im_type) {
-		case QQ_RECV_IM_NEWS:
-			process_recv_news(data + bytes, data_len - bytes, gc);
-			break;
-		case QQ_RECV_IM_FROM_BUDDY_2006:
-		case QQ_RECV_IM_FROM_UNKNOWN_2006:
-		case QQ_RECV_IM_TO_UNKNOWN:
-		case QQ_RECV_IM_TO_BUDDY:
-			purple_debug_info("QQ", "MSG from buddy [%d]\n", im_header->sender_uid);
-			process_recv_normal_im(data + bytes, data_len - bytes, gc);
-			break;
-		case QQ_RECV_IM_UNKNOWN_QUN_IM:
-		case QQ_RECV_IM_TEMP_QUN_IM:
-		case QQ_RECV_IM_QUN_IM:
-			purple_debug_info("QQ", "MSG from room [%d]\n", im_header->sender_uid);
-			/* sender_uid is in fact id */
-			qq_process_room_msg_normal(data + bytes, data_len - bytes, im_header->sender_uid, gc, im_header->im_type);
-			break;
-		case QQ_RECV_IM_ADD_TO_QUN:
-			purple_debug_info("QQ", "Notice from [%d], Added\n", im_header->sender_uid);
-			/* sender_uid is group id
-			 * we need this to create a dummy group and add to blist */
-			qq_process_room_msg_been_added(data + bytes, data_len - bytes, im_header->sender_uid, gc);
-			break;
-		case QQ_RECV_IM_DEL_FROM_QUN:
-			purple_debug_info("QQ", "Notice from room [%d], Removed\n", im_header->sender_uid);
-			/* sender_uid is group id */
-			qq_process_room_msg_been_removed(data + bytes, data_len - bytes, im_header->sender_uid, gc);
-			break;
-		case QQ_RECV_IM_APPLY_ADD_TO_QUN:
-			purple_debug_info("QQ", "Notice from room [%d], Joined\n", im_header->sender_uid);
-			/* sender_uid is group id */
-			qq_process_room_msg_apply_join(data + bytes, data_len - bytes, im_header->sender_uid, gc);
-			break;
-		case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
-			purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n",
-					im_header->sender_uid);
-			/* sender_uid is group id */
-			qq_process_room_msg_been_approved(data + bytes, data_len - bytes, im_header->sender_uid, gc);
-			break;
-		case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
-			purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n",
-					im_header->sender_uid);
-			/* sender_uid is group id */
-			qq_process_room_msg_been_rejected(data + bytes, data_len - bytes, im_header->sender_uid, gc);
-			break;
-		case QQ_RECV_IM_SYS_NOTIFICATION:
-			purple_debug_info("QQ", "Admin notice from [%d]\n", im_header->sender_uid);
-			process_recv_sys_im(data + bytes, data_len - bytes, gc);
-			break;
-		default:
-			purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%02x]\n",
-					im_header->sender_uid, qq_get_recv_im_type_str(im_header->im_type),
-					im_header->im_type);
-			qq_show_packet("Unknown MSG type", data, data_len);
-	}
-}
-
--- a/libpurple/protocols/qq/im.h	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/im.h	Wed Oct 22 14:47:39 2008 +0000
@@ -52,8 +52,8 @@
 	QQ_RECV_IM_TEMP_QUN_IM = 0x002A,
 	QQ_RECV_IM_QUN_IM = 0x002B,
 	QQ_RECV_IM_SYS_NOTIFICATION = 0x0030,
-	QQ_RECV_IM_FROM_BUDDY_2006 = 0x0084,
-	QQ_RECV_IM_FROM_UNKNOWN_2006 = 0x0085,
+	QQ_RECV_IM_EXTEND = 0x0084,
+	QQ_RECV_IM_EXTEND_85 = 0x0085,
 };
 
 void qq_got_attention(PurpleConnection *gc, const gchar *msg);
@@ -63,8 +63,8 @@
 		const gchar *font_name,
 		gboolean is_bold, gboolean is_italic, gboolean is_underline, gint len);
 
-void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type);
-void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
-void qq_process_send_im_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type);
 
+void qq_process_im(PurpleConnection *gc, guint8 *data, gint len);
+void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len);
 #endif
--- a/libpurple/protocols/qq/packet_parse.c	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.c	Wed Oct 22 14:47:39 2008 +0000
@@ -151,10 +151,7 @@
  * 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;
+	guint32 dw_porter;
     dw_porter = g_htonl(dw);
 #ifdef PARSER_DEBUG
 	purple_debug_info("QQ", "[DBG][put32] buf %p\n", (void *)buf);
--- a/libpurple/protocols/qq/qq.c	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Wed Oct 22 14:47:39 2008 +0000
@@ -435,7 +435,7 @@
 /* send an instant msg to a buddy */
 static gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags)
 {
-	gint type, to_uid;
+	gint type, uid_to;
 	gchar *msg, *msg_with_qq_smiley;
 	qq_data *qd;
 
@@ -446,15 +446,15 @@
 	g_return_val_if_fail(strlen(message) <= QQ_MSG_IM_MAX, -E2BIG);
 
 	type = (flags == PURPLE_MESSAGE_AUTO_RESP ? QQ_IM_AUTO_REPLY : QQ_IM_TEXT);
-	to_uid = purple_name_to_uid(who);
+	uid_to = purple_name_to_uid(who);
 
 	/* if msg is to myself, bypass the network */
-	if (to_uid == qd->uid) {
+	if (uid_to == qd->uid) {
 		serv_got_im(gc, who, message, flags, time(NULL));
 	} else {
 		msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT);
 		msg_with_qq_smiley = purple_smiley_to_qq(msg);
-		qq_send_packet_im(gc, to_uid, msg_with_qq_smiley, type);
+		qq_request_send_im(gc, uid_to, msg_with_qq_smiley, type);
 		g_free(msg);
 		g_free(msg_with_qq_smiley);
 	}
--- a/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Wed Oct 22 14:47:39 2008 +0000
@@ -66,7 +66,7 @@
 	struct tm *tm_local;
 
 	qd = (qq_data *) gc->proto_data;
-	qq_show_packet("Login reply", data, len);
+	/* qq_show_packet("Login reply", data, len); */
 
 	if (len < 139) {
 		purple_connection_error_reason(gc,
@@ -665,7 +665,7 @@
 	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);
+	/* qq_show_packet("Redirect to", qd->redirect, qd->redirect_len); */
 
 	qq_getIP(&qd->redirect_ip, data + 11);
 	purple_debug_info("QQ", "Get server %s\n", inet_ntoa(qd->redirect_ip));
@@ -912,7 +912,7 @@
 	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);
-	qq_show_packet("Get token ex", qd->ld.token_ex, qd->ld.token_ex_len);
+	/* qq_show_packet("Get token ex", qd->ld.token_ex, qd->ld.token_ex_len); */
 	
 	if(reply != 1)
 	{
@@ -932,7 +932,7 @@
 	bytes += qq_get16(&qd->captcha.token_len, data + bytes);
 	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);
+	/* 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);
@@ -1038,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);
 
@@ -1068,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 */
@@ -1086,10 +1086,11 @@
 		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);
+		/* 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));
+		/* qq_show_packet("Get login key", qd->ld.login_key, sizeof(qd->ld.login_key)); */
 		return QQ_LOGIN_REPLY_OK;
 	}
 
@@ -1202,7 +1203,7 @@
 	memset(raw_data + bytes, 0, 10);
 	bytes += 10;
 	/* redirect data, 15 bytes */
-	qq_show_packet("Redirect", qd->redirect, qd->redirect_len);
+	/* 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));
@@ -1214,7 +1215,7 @@
 	memset(raw_data + bytes, 0, 332 - sizeof(login_3_83));
 	bytes += 332 - sizeof(login_3_83);
 
-	qq_show_packet("Login", raw_data, bytes);
+	/* qq_show_packet("Login", raw_data, bytes); */
 
 	encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
 
@@ -1254,11 +1255,12 @@
 		switch (ret) {
 			case 0x05:
 				error = g_strdup_printf(
-						_("Server is busy now, Please try later\n%s"),
+						_("Server is busy now (0x%02X), 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 */
+				/* Missing get server before login*/
 			default:
 				error = g_strdup_printf(
 						_("Unknow reply code when login (0x%02X):\n%s"),
@@ -1408,7 +1410,7 @@
 	memset(raw_data + bytes, 0, 249);
 	bytes += 249;
 
-	qq_show_packet("Login request", raw_data, bytes);
+	/* 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);
@@ -1446,7 +1448,7 @@
 		switch (ret) {
 			case 0x05:
 				error = g_strdup_printf(
-						_("Server is busy now, Please try later\n%s"),
+						_("Server is busy now (0x%02X), Please try later\n%s"),
 						ret, msg_utf8);
 				break;
 			default:
--- a/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:46:44 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:47:39 2008 +0000
@@ -58,7 +58,7 @@
 };
 
 /* default process, decrypt and dump */
-static void process_cmd_unknow(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq)
+static void process_unknow_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq)
 {
 	qq_data *qd;
 	gchar *msg_utf8 = NULL;
@@ -81,8 +81,253 @@
 	}
 }
 
+/* parse the reply to send_im */
+static void do_im_ack(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+	qq_data *qd;
+
+	g_return_if_fail(data != NULL && data_len != 0);
+
+	qd = gc->proto_data;
+
+	if (data[0] != 0) {
+		purple_debug_warning("QQ", "Failed sent IM\n");
+		purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
+		return;
+	}
+	
+	purple_debug_info("QQ", "OK sent IM\n");
+}
+
+static void do_server_news(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+	qq_data *qd = (qq_data *) gc->proto_data;
+	gint bytes;
+	guint8 *temp;
+	guint8 temp_len;
+	gchar *title, *brief, *url;
+	gchar *title_utf8;
+	gchar *content, *content_utf8;
+
+	g_return_if_fail(data != NULL && data_len != 0);
+
+	/* qq_show_packet("Rcv news", data, data_len); */
+
+	temp = g_newa(guint8, data_len);
+	bytes = 4;	/* ignore unknown 4 bytes */
+
+	bytes += qq_get8(&temp_len, data + bytes);
+	g_return_if_fail(bytes + temp_len <= data_len);
+	bytes += qq_getdata(temp, temp_len, data+bytes);
+	title = g_strndup((gchar *)temp, temp_len);
+
+	bytes += qq_get8(&temp_len, data + bytes);
+	g_return_if_fail(bytes + temp_len <= data_len);
+	bytes += qq_getdata(temp, temp_len, data+bytes);
+	brief = g_strndup((gchar *)temp, temp_len);
+
+	bytes += qq_get8(&temp_len, data + bytes);
+	g_return_if_fail(bytes + temp_len <= data_len);
+	bytes += qq_getdata(temp, temp_len, data+bytes);
+	url = g_strndup((gchar *)temp, temp_len);
+
+	title_utf8 = qq_to_utf8(title, QQ_CHARSET_DEFAULT);
+	content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url);
+	content_utf8 = qq_to_utf8(content, QQ_CHARSET_DEFAULT);
+
+	if (qd->is_show_news) {
+		qq_got_attention(gc, content_utf8);
+	} else {
+		purple_debug_info("QQ", "QQ Server news:\n%s\n%s", title_utf8, content_utf8);
+	}
+	g_free(title);
+	g_free(title_utf8);
+	g_free(brief);
+	g_free(url);
+	g_free(content);
+	g_free(content_utf8);
+}
+
+/* process im from system administrator */
+static void do_server_im(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+	gint len;
+	guint8 reply;
+	gchar **segments, *msg_utf8;
+
+	g_return_if_fail(data != NULL && data_len != 0);
+
+	len = data_len;
+
+	if (NULL == (segments = split_data(data, len, "\x2f", 2)))
+		return;
+
+	reply = strtol(segments[0], NULL, 10);
+	if (reply == 1)
+		purple_debug_warning("QQ", "We are kicked out by QQ server\n");
+		
+	msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
+	purple_notify_warning(gc, NULL, _("System Message"), msg_utf8);
+}
+
+static const gchar *get_im_type_desc(gint type)
+{
+	switch (type) {
+		case QQ_RECV_IM_TO_BUDDY:
+			return "QQ_RECV_IM_TO_BUDDY";
+		case QQ_RECV_IM_TO_UNKNOWN:
+			return "QQ_RECV_IM_TO_UNKNOWN";
+		case QQ_RECV_IM_UNKNOWN_QUN_IM:
+			return "QQ_RECV_IM_UNKNOWN_QUN_IM";
+		case QQ_RECV_IM_ADD_TO_QUN:
+			return "QQ_RECV_IM_ADD_TO_QUN";
+		case QQ_RECV_IM_DEL_FROM_QUN:
+			return "QQ_RECV_IM_DEL_FROM_QUN";
+		case QQ_RECV_IM_APPLY_ADD_TO_QUN:
+			return "QQ_RECV_IM_APPLY_ADD_TO_QUN";
+		case QQ_RECV_IM_CREATE_QUN:
+			return "QQ_RECV_IM_CREATE_QUN";
+		case QQ_RECV_IM_SYS_NOTIFICATION:
+			return "QQ_RECV_IM_SYS_NOTIFICATION";
+		case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
+			return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN";
+		case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
+			return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN";
+		case QQ_RECV_IM_TEMP_QUN_IM:
+			return "QQ_RECV_IM_TEMP_QUN_IM";
+		case QQ_RECV_IM_QUN_IM:
+			return "QQ_RECV_IM_QUN_IM";
+		case QQ_RECV_IM_NEWS:
+			return "QQ_RECV_IM_NEWS";
+		case QQ_RECV_IM_EXTEND:
+			return "QQ_RECV_IM_EXTEND";
+		case QQ_RECV_IM_EXTEND_85:
+			return "QQ_RECV_IM_EXTEND_85";
+		default:
+			return "QQ_RECV_IM_UNKNOWN";
+	}
+}
+
+/* I receive a message, mainly it is text msg,
+ * but we need to proess other types (group etc) */
+static void process_private_msg(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
+{
+	qq_data *qd;
+	gint bytes;
+
+	struct {
+		guint32 uid_from;
+		guint32 uid_to;
+		guint32 seq;
+		struct in_addr ip_from;
+		guint16 port_from;
+		guint16 im_type;
+	} header;
+
+	g_return_if_fail(data != NULL && data_len != 0);
+
+	qd = (qq_data *) gc->proto_data;
+
+	if (data_len < 16) {	/* we need to ack with the first 16 bytes */
+		purple_debug_error("QQ", "MSG is too short\n");
+		return;
+	} else {
+		/* when we receive a message,
+		 * we send an ACK which is the first 16 bytes of incoming packet */
+		qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16);
+	}
+
+	/* check len first */
+	if (data_len < 20) {	/* length of im_header */
+		purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len);
+		return;
+	}
+
+	bytes = 0;
+	bytes += qq_get32(&(header.uid_from), data + bytes);
+	bytes += qq_get32(&(header.uid_to), data + bytes);
+	bytes += qq_get32(&(header.seq), data + bytes);
+	/* if the message is delivered via server, it is server IP/port */
+	bytes += qq_getIP(&(header.ip_from), data + bytes);
+	bytes += qq_get16(&(header.port_from), data + bytes);
+	bytes += qq_get16(&(header.im_type), data + bytes);
+	/* im_header prepared */
+
+	if (header.uid_to != qd->uid) {	/* should not happen */
+		purple_debug_error("QQ", "MSG to [%d], NOT me\n", header.uid_to);
+		return;
+	}
+
+	/* check bytes */
+	if (bytes >= data_len - 1) {
+		purple_debug_warning("QQ", "Empty MSG\n");
+		return;
+	}
+
+	switch (header.im_type) {
+		case QQ_RECV_IM_NEWS:
+			do_server_news(data + bytes, data_len - bytes, gc);
+			break;
+		case QQ_RECV_IM_EXTEND:
+		case QQ_RECV_IM_EXTEND_85:
+			purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from);
+			qq_process_extend_im(gc, data + bytes, data_len - bytes);
+			break;
+		case QQ_RECV_IM_TO_UNKNOWN:
+		case QQ_RECV_IM_TO_BUDDY:
+			purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from);
+			qq_process_im(gc, data + bytes, data_len - bytes);
+			break;
+		case QQ_RECV_IM_UNKNOWN_QUN_IM:
+		case QQ_RECV_IM_TEMP_QUN_IM:
+		case QQ_RECV_IM_QUN_IM:
+			purple_debug_info("QQ", "MSG from room [%d]\n", header.uid_from);
+			/* uid_from is in fact id */
+			qq_process_room_msg_normal(data + bytes, data_len - bytes, header.uid_from, gc, header.im_type);
+			break;
+		case QQ_RECV_IM_ADD_TO_QUN:
+			purple_debug_info("QQ", "Notice from [%d], Added\n", header.uid_from);
+			/* uid_from is group id
+			 * we need this to create a dummy group and add to blist */
+			qq_process_room_msg_been_added(data + bytes, data_len - bytes, header.uid_from, gc);
+			break;
+		case QQ_RECV_IM_DEL_FROM_QUN:
+			purple_debug_info("QQ", "Notice from room [%d], Removed\n", header.uid_from);
+			/* uid_from is group id */
+			qq_process_room_msg_been_removed(data + bytes, data_len - bytes, header.uid_from, gc);
+			break;
+		case QQ_RECV_IM_APPLY_ADD_TO_QUN:
+			purple_debug_info("QQ", "Notice from room [%d], Joined\n", header.uid_from);
+			/* uid_from is group id */
+			qq_process_room_msg_apply_join(data + bytes, data_len - bytes, header.uid_from, gc);
+			break;
+		case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
+			purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n",
+					header.uid_from);
+			/* uid_from is group id */
+			qq_process_room_msg_been_approved(data + bytes, data_len - bytes, header.uid_from, gc);
+			break;
+		case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
+			purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n",
+					header.uid_from);
+			/* uid_from is group id */
+			qq_process_room_msg_been_rejected(data + bytes, data_len - bytes, header.uid_from, gc);
+			break;
+		case QQ_RECV_IM_SYS_NOTIFICATION:
+			purple_debug_info("QQ", "Admin notice from [%d]\n", header.uid_from);
+			do_server_im(data + bytes, data_len - bytes, gc);
+			break;
+		default:
+			purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%02x]\n",
+					header.uid_from, get_im_type_desc(header.im_type),
+					header.im_type);
+			qq_show_packet("Unknown MSG type", data, data_len);
+			break;
+	}
+}
+
 /* Send ACK if the sys message needs an ACK */
-static void ack_server_msg(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
+static void request_server_ack(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
 {
 	qq_data *qd;
 	guint8 bar, *ack;
@@ -112,7 +357,7 @@
 			   "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
 }
 
-static void process_server_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+static void do_server_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
 {
 	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar *title, *content;
@@ -148,7 +393,7 @@
 	to = segments[2];
 	msg = segments[3];
 
-	ack_server_msg(gc, code[0], strtol(from, NULL, 10), seq);
+	request_server_ack(gc, code[0], strtol(from, NULL, 10), seq);
 
 	if (strtol(to, NULL, 10) != qd->uid) {	/* not to me */
 		purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
@@ -173,7 +418,7 @@
 			qq_process_buddy_from_server(gc,  funct, from, to, msg_utf8);
 			break;
 		case QQ_SERVER_NOTICE:
-			process_server_notice(gc, from, to, msg_utf8);
+			do_server_notice(gc, from, to, msg_utf8);
 			break;
 		case QQ_SERVER_NEW_CLIENT:
 			purple_debug_warning("QQ", "QQ Server has newer client version\n");
@@ -216,7 +461,7 @@
 	/* now process the packet */
 	switch (cmd) {
 		case QQ_CMD_RECV_IM:
-			qq_process_recv_im(data, data_len, seq, gc);
+			process_private_msg(data, data_len, seq, gc);
 			break;
 		case QQ_CMD_RECV_MSG_SYS:
 			process_server_msg(data, data_len, seq, gc);
@@ -225,7 +470,7 @@
 			qq_process_buddy_change_status(data, data_len, gc);
 			break;
 		default:
-			process_cmd_unknow(gc, _("Unknow SERVER CMD"), data, data_len, cmd, seq);
+			process_unknow_cmd(gc, _("Unknow SERVER CMD"), data, data_len, cmd, seq);
 			break;
 	}
 }
@@ -681,7 +926,7 @@
 		case QQ_CMD_CHECK_PWD:
 			ret_8 = qq_process_check_pwd(gc, data, data_len);
 			if (ret_8 != QQ_LOGIN_REPLY_OK) {
-				return  ret_8;
+				return ret_8;
 			}
 			if (qd->client_version == 2008) {
 				qq_request_login_2008(gc);
@@ -698,7 +943,7 @@
 				ret_8 = qq_process_login(gc, data, data_len);
 			}
 			if (ret_8 != QQ_LOGIN_REPLY_OK) {
-				return  ret_8;
+				return ret_8;
 			}
 
 			purple_connection_update_progress(gc, _("Logined"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
@@ -715,7 +960,7 @@
 			qq_update_all(gc, 0);
 			break;
 		default:
-			process_cmd_unknow(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq);
+			process_unknow_cmd(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq);
 			return QQ_LOGIN_REPLY_ERR;
 	}
 	return QQ_LOGIN_REPLY_OK;
@@ -779,7 +1024,7 @@
 			qq_process_change_status_reply(data, data_len, gc);
 			break;
 		case QQ_CMD_SEND_IM:
-			qq_process_send_im_reply(data, data_len, gc);
+			do_im_ack(data, data_len, gc);
 			break;
 		case QQ_CMD_KEEP_ALIVE:
 			if (qd->client_version >= 2008) {
@@ -822,7 +1067,7 @@
 			purple_debug_info("QQ", "All buddies and groups received\n");
 			break;
 		default:
-			process_cmd_unknow(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq);
+			process_unknow_cmd(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq);
 			is_unknow = TRUE;
 			break;
 	}